Skip to main content
Glama
template.tsx.j2.bak10.5 kB
{/* chuk-mcp-remotion/src/chuk_mcp_remotion/components/charts/HorizontalBarChart/template.tsx.j2 */} import React from 'react'; import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from 'remotion'; interface DataPoint { label: string; value: number; color?: string; } interface HorizontalBarChartProps { data?: Array<{ label: string; value: number; color?: string }>; title?: string; xlabel?: string; ylabel?: string; startFrame: number; durationInFrames: number; } export const HorizontalBarChart: React.FC<HorizontalBarChartProps> = ({ data = [], title, xlabel, ylabel, startFrame, durationInFrames }) => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const relativeFrame = frame - startFrame; // Don't render if outside the time range if (frame < startFrame || frame >= startFrame + durationInFrames) { return null; } if (data.length === 0) { return null; } // Calculate bounds const values = data.map(d => d.value); const maxValue = Math.max(...values); // Chart dimensions const chartWidth = 1000; const chartHeight = Math.max(400, data.length * parseInt('[[ spacing.spacing['4xl'] ]]')); const paddingLeft = 200; const paddingRight = parseInt('[[ spacing.spacing['4xl'] ]]'); const paddingTop = parseInt('[[ spacing.spacing['3xl'] ]]'); const paddingBottom = parseInt('[[ spacing.spacing['3xl'] ]]'); const barHeight = (chartHeight - paddingTop - paddingBottom) / data.length * 0.7; const barSpacing = (chartHeight - paddingTop - paddingBottom) / data.length; // Entrance animation const entranceProgress = spring({ frame: relativeFrame, fps, config: { damping: 200, mass: 0.5, stiffness: 200 } }); // Exit animation const exitDuration = 20; const exitProgress = interpolate( relativeFrame, [durationInFrames - exitDuration, durationInFrames], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ); const opacity = entranceProgress * exitProgress; const scale = interpolate(entranceProgress, [0, 1], [0.9, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }); const titleTranslateY = interpolate(entranceProgress, [0, 1], [-30, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }); // Default colors with gradients const defaultColors = [ '[[ colors.primary[2] ]]', '[[ colors.primary[3] ]]', '[[ colors.accent[2] ]]', '[[ colors.accent[3] ]]', '[[ colors.primary[4] ]]', '[[ colors.accent[4] ]]', '[[ colors.primary[5] ]]', '[[ colors.accent[5] ]]' ]; return ( <AbsoluteFill style={{ pointerEvents: 'none' }}> <div style={{ position: 'absolute', top: '50%', left: '50%', transform: `translate(-50%, -50%) scale(${scale})`, opacity, fontFamily: "'[[ "', '".join(typography.primary_font.fonts) ]]'", filter: 'drop-shadow(0 20px 40px [[ colors.background.dark ]])' }} > {title && ( <h3 style={{ fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].lg ]]'), fontWeight: '[[ typography.font_weights.bold ]]', color: '[[ colors.text.on_dark ]]', marginBottom: parseInt('[[ spacing.spacing.xl ]]'), textAlign: 'center', transform: `translateY(${titleTranslateY}px)`, textShadow: '0 [[ spacing.border_width.medium ]] [[ spacing.spacing.lg ]]px [[ colors.primary[0] ]]80', letterSpacing: '[[ typography.letter_spacing.tight ]]' }} > {title} </h3> )} <svg width={chartWidth} height={chartHeight} style={{ borderRadius: parseInt('[[ spacing.border_radius.xl ]]'), padding: parseInt('[[ spacing.spacing.lg ]]'), overflow: 'visible' }} > <defs> <linearGradient id="bgGradientH" x1="0%" y1="0%" x2="100%" y2="100%"> <stop offset="0%" stopColor="[[ colors.background.dark ]]" /> <stop offset="100%" stopColor="[[ colors.background.darker ]]" /> </linearGradient> {data.map((item, idx) => { const color = item.color || defaultColors[idx % defaultColors.length]; return ( <linearGradient key={idx} id={`barGradient${idx}`} x1="0%" y1="0%" x2="100%" y2="0%"> <stop offset="0%" stopColor={color} stopOpacity="0.9" /> <stop offset="100%" stopColor={color} stopOpacity="1" /> </linearGradient> ); })} <filter id="glowH"> <feGaussianBlur stdDeviation="3" result="coloredBlur"/> <feMerge> <feMergeNode in="coloredBlur"/> <feMergeNode in="SourceGraphic"/> </feMerge> </filter> </defs> {/* Background */} <rect x="0" y="0" width={chartWidth} height={chartHeight} fill="url(#bgGradientH)" rx="[[ spacing.border_radius.xl ]]" /> {/* Grid lines */} <g stroke="[[ colors.border.light ]]" strokeWidth="[[ spacing.border_width.thin ]]"> {[0, 0.25, 0.5, 0.75, 1].map((ratio) => { const x = paddingLeft + ratio * (chartWidth - paddingLeft - paddingRight); return ( <line key={`grid-${ratio}`} x1={x} y1={paddingTop} x2={x} y2={chartHeight - paddingBottom} /> ); })} </g> {/* Axes */} <g stroke="[[ colors.border.medium ]]" strokeWidth="[[ spacing.border_width.medium ]]"> <line x1={paddingLeft} y1={paddingTop} x2={paddingLeft} y2={chartHeight - paddingBottom} /> <line x1={paddingLeft} y1={chartHeight - paddingBottom} x2={chartWidth - paddingRight} y2={chartHeight - paddingBottom} /> </g> {/* Bars */} {data.map((item, idx) => { const barDelay = idx * 5; const barProgress = spring({ frame: Math.max(0, relativeFrame - barDelay), fps, config: { damping: 150, mass: 0.5, stiffness: 200 } }); const y = paddingTop + idx * barSpacing + (barSpacing - barHeight) / 2; const barWidth = (item.value / maxValue) * (chartWidth - paddingLeft - paddingRight); const animatedWidth = barWidth * barProgress; const x = paddingLeft; // Ranking number (1, 2, 3, etc.) const rank = idx + 1; return ( <g key={idx}> {/* Rank badge */} <circle cx={paddingLeft - 140} cy={y + barHeight / 2} r="[[ spacing.spacing.lg ]]" fill={rank === 1 ? '[[ colors.primary[2] ]]' : rank === 2 ? '[[ colors.border.medium ]]' : rank === 3 ? '[[ colors.accent[3] ]]' : '[[ colors.border.light ]]'} opacity={barProgress} filter="url(#glowH)" /> <text x={paddingLeft - 140} y={y + barHeight / 2} textAnchor="middle" dominantBaseline="middle" fill={rank <= 3 ? '[[ colors.background.dark ]]' : '[[ colors.text.on_dark ]]'} fontSize="[[ typography.font_sizes[typography.default_resolution].xs ]]" fontWeight="[[ typography.font_weights.black ]]" fontFamily="'[[ "', '".join(typography.primary_font.fonts) ]]'" opacity={barProgress} > {rank} </text> {/* Label */} <text x={paddingLeft - parseInt('[[ spacing.spacing.xl ]]')} y={y + barHeight / 2} textAnchor="end" dominantBaseline="middle" fill="[[ colors.text.on_dark ]]" fontSize="[[ typography.font_sizes[typography.default_resolution].xs ]]" fontWeight="[[ typography.font_weights.semibold ]]" fontFamily="'[[ "', '".join(typography.primary_font.fonts) ]]'" opacity={barProgress} > {item.label} </text> {/* Bar */} <rect x={x} y={y} width={animatedWidth} height={barHeight} fill={`url(#barGradient${idx})`} rx="[[ spacing.border_radius.md ]]" filter="url(#glowH)" opacity={barProgress} /> {/* Value label */} <text x={x + animatedWidth + parseInt('[[ spacing.spacing.sm ]]')} y={y + barHeight / 2} dominantBaseline="middle" fill="[[ colors.text.on_dark ]]" fontSize="[[ typography.font_sizes[typography.default_resolution].sm ]]" fontWeight="[[ typography.font_weights.bold ]]" fontFamily="'[[ "', '".join(typography.primary_font.fonts) ]]'" opacity={barProgress} > {item.value} </text> </g> ); })} {/* Axis labels */} {xlabel && ( <text x={paddingLeft + (chartWidth - paddingLeft - paddingRight) / 2} y={chartHeight - parseInt('[[ spacing.spacing.lg ]]')} textAnchor="middle" fill="[[ colors.text.muted ]]" fontSize="[[ typography.font_sizes[typography.default_resolution].xs ]]" fontFamily="'[[ "', '".join(typography.primary_font.fonts) ]]'" > {xlabel} </text> )} </svg> </div> </AbsoluteFill> ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chrishayuk/chuk-mcp-remotion'

If you have feedback or need assistance with the MCP directory API, please join our Discord server