metric.tsx 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839
  1. 'use client';
  2. import { type ComponentRenderProps } from '@json-render/react';
  3. import { useData } from '@json-render/react';
  4. import { getByPath } from '@json-render/core';
  5. export function Metric({ element }: ComponentRenderProps) {
  6. const { label, valuePath, format, trend, trendValue } = element.props as {
  7. label: string;
  8. valuePath: string;
  9. format?: string | null;
  10. trend?: string | null;
  11. trendValue?: string | null;
  12. };
  13. const { data } = useData();
  14. const rawValue = getByPath(data, valuePath);
  15. let displayValue = String(rawValue ?? '-');
  16. if (format === 'currency' && typeof rawValue === 'number') {
  17. displayValue = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(rawValue);
  18. } else if (format === 'percent' && typeof rawValue === 'number') {
  19. displayValue = new Intl.NumberFormat('en-US', { style: 'percent', minimumFractionDigits: 1 }).format(rawValue);
  20. } else if (format === 'number' && typeof rawValue === 'number') {
  21. displayValue = new Intl.NumberFormat('en-US').format(rawValue);
  22. }
  23. return (
  24. <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
  25. <span style={{ fontSize: 14, color: 'var(--muted)' }}>{label}</span>
  26. <span style={{ fontSize: 32, fontWeight: 600 }}>{displayValue}</span>
  27. {(trend || trendValue) && (
  28. <span style={{ fontSize: 14, color: trend === 'up' ? '#22c55e' : trend === 'down' ? '#ef4444' : 'var(--muted)' }}>
  29. {trend === 'up' ? '+' : trend === 'down' ? '-' : ''}{trendValue}
  30. </span>
  31. )}
  32. </div>
  33. );
  34. }