@humain/ui 2.1.62 → 2.1.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/organisms/ai/AIToolCall/AIToolCall.d.ts +9 -3
- package/dist/components/organisms/ai/AIToolRun/index.d.ts +1 -1
- package/dist/components/organisms/ai/AIToolRun/validation.d.ts +24 -3
- package/dist/components/organisms/ai/_shared/format-ai-duration.d.ts +1 -0
- package/dist/components/organisms/ai/_shared/workflow-step-renderer.d.ts +1 -0
- package/dist/index.js +52 -52
- package/dist/index151.js +1 -1
- package/dist/index174.js +10 -333
- package/dist/index174.js.map +1 -1
- package/dist/index175.js +310 -137
- package/dist/index175.js.map +1 -1
- package/dist/index176.js +148 -174
- package/dist/index176.js.map +1 -1
- package/dist/index177.js +186 -78
- package/dist/index177.js.map +1 -1
- package/dist/index178.js +74 -124
- package/dist/index178.js.map +1 -1
- package/dist/index179.js +133 -1
- package/dist/index179.js.map +1 -0
- package/dist/index180.js +2 -201
- package/dist/index181.js +201 -0
- package/dist/{index180.js.map → index181.js.map} +1 -1
- package/dist/index183.js +130 -166
- package/dist/index183.js.map +1 -1
- package/dist/index184.js +195 -983
- package/dist/index184.js.map +1 -1
- package/dist/index185.js +1069 -369
- package/dist/index185.js.map +1 -1
- package/dist/index186.js +395 -197
- package/dist/index186.js.map +1 -1
- package/dist/index187.js +198 -243
- package/dist/index187.js.map +1 -1
- package/dist/index188.js +263 -3
- package/dist/index188.js.map +1 -1
- package/dist/index189.js +3 -209
- package/dist/index189.js.map +1 -1
- package/dist/index190.js +190 -777
- package/dist/index190.js.map +1 -1
- package/dist/index191.js +771 -252
- package/dist/index191.js.map +1 -1
- package/dist/index192.js +250 -65
- package/dist/index192.js.map +1 -1
- package/dist/index193.js +72 -523
- package/dist/index193.js.map +1 -1
- package/dist/index194.js +537 -144
- package/dist/index194.js.map +1 -1
- package/dist/index195.js +139 -720
- package/dist/index195.js.map +1 -1
- package/dist/index196.js +707 -167
- package/dist/index196.js.map +1 -1
- package/dist/index197.js +175 -151
- package/dist/index197.js.map +1 -1
- package/dist/index198.js +152 -487
- package/dist/index198.js.map +1 -1
- package/dist/index199.js +472 -225
- package/dist/index199.js.map +1 -1
- package/dist/index200.js +236 -260
- package/dist/index200.js.map +1 -1
- package/dist/index201.js +265 -215
- package/dist/index201.js.map +1 -1
- package/dist/index202.js +207 -307
- package/dist/index202.js.map +1 -1
- package/dist/index203.js +302 -135
- package/dist/index203.js.map +1 -1
- package/dist/index204.js +140 -271
- package/dist/index204.js.map +1 -1
- package/dist/index205.js +263 -253
- package/dist/index205.js.map +1 -1
- package/dist/index206.js +244 -137
- package/dist/index206.js.map +1 -1
- package/dist/index207.js +100 -238
- package/dist/index207.js.map +1 -1
- package/dist/index208.js +278 -74
- package/dist/index208.js.map +1 -1
- package/dist/index209.js +56 -97
- package/dist/index209.js.map +1 -1
- package/dist/index210.js +142 -47
- package/dist/index210.js.map +1 -1
- package/dist/index211.js +38 -84
- package/dist/index211.js.map +1 -1
- package/dist/index212.js +72 -252
- package/dist/index212.js.map +1 -1
- package/dist/index213.js +274 -26
- package/dist/index213.js.map +1 -1
- package/dist/index214.js +29 -94
- package/dist/index214.js.map +1 -1
- package/dist/index215.js +98 -25
- package/dist/index215.js.map +1 -1
- package/dist/index216.js +20 -565
- package/dist/index216.js.map +1 -1
- package/dist/index217.js +564 -17
- package/dist/index217.js.map +1 -1
- package/dist/index218.js +20 -23
- package/dist/index218.js.map +1 -1
- package/dist/index219.js +20 -95
- package/dist/index219.js.map +1 -1
- package/dist/index220.js +92 -88
- package/dist/index220.js.map +1 -1
- package/dist/index221.js +97 -29
- package/dist/index221.js.map +1 -1
- package/dist/index222.js +24 -470
- package/dist/index222.js.map +1 -1
- package/dist/index223.js +446 -196
- package/dist/index223.js.map +1 -1
- package/dist/index224.js +199 -729
- package/dist/index224.js.map +1 -1
- package/dist/index225.js +750 -61
- package/dist/index225.js.map +1 -1
- package/dist/index226.js +58 -181
- package/dist/index226.js.map +1 -1
- package/dist/index227.js +175 -259
- package/dist/index227.js.map +1 -1
- package/dist/index228.js +246 -172
- package/dist/index228.js.map +1 -1
- package/dist/index229.js +195 -32
- package/dist/index229.js.map +1 -1
- package/dist/index230.js +12 -40
- package/dist/index230.js.map +1 -1
- package/dist/index231.js +23 -26
- package/dist/index231.js.map +1 -1
- package/dist/index232.js +45 -22
- package/dist/index232.js.map +1 -1
- package/dist/index233.js +27 -48
- package/dist/index233.js.map +1 -1
- package/dist/index234.js +52 -26
- package/dist/index234.js.map +1 -1
- package/dist/index235.js +40 -225
- package/dist/index235.js.map +1 -1
- package/dist/index236.js +217 -39
- package/dist/index236.js.map +1 -1
- package/dist/index237.js +50 -0
- package/dist/index237.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/wc/AIToolCall.js +218 -0
- package/dist/wc/AIToolCall.js.map +1 -0
- package/dist/wc/hf-ai-tool-call.js +34 -219
- package/dist/wc/hf-ai-tool-call.js.map +1 -1
- package/dist/wc/hf-ai-tool-run.js +673 -576
- package/dist/wc/hf-ai-tool-run.js.map +1 -1
- package/dist/wc/workflow-step-renderer.js +94 -86
- package/dist/wc/workflow-step-renderer.js.map +1 -1
- package/dist/wc-react/AIToolCall.js +218 -0
- package/dist/wc-react/AIToolCall.js.map +1 -0
- package/dist/wc-react/hf-ai-tool-call.js +32 -217
- package/dist/wc-react/hf-ai-tool-call.js.map +1 -1
- package/dist/wc-react/hf-ai-tool-run.js +698 -601
- package/dist/wc-react/hf-ai-tool-run.js.map +1 -1
- package/dist/wc-react/workflow-step-renderer.js +76 -68
- package/dist/wc-react/workflow-step-renderer.js.map +1 -1
- package/package.json +1 -1
- package/skills/humain-ui/SKILL.md +1 -1
- package/skills/humain-ui-wc/SKILL.md +1 -1
- package/skills/humain-ui-wc.skill +0 -0
- package/skills/humain-ui.skill +0 -0
- package/dist/index182.js +0 -143
- package/dist/index182.js.map +0 -1
- package/dist/wc/wrench.js +0 -10
- package/dist/wc/wrench.js.map +0 -1
- package/dist/wc-react/wrench.js +0 -10
- package/dist/wc-react/wrench.js.map +0 -1
package/dist/index198.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index198.js","names":[],"sources":["../src/components/organisms/content/MetricItem/MetricItem.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport {\n ArrowUp,\n ArrowUpRight,\n Eye,\n MoreVertical,\n TrendingUp,\n} from 'lucide-react';\nimport * as React from 'react';\nimport { Area, AreaChart, Line, LineChart } from 'recharts';\nimport { Button } from '@/components/atoms/buttons/Button';\nimport { type ChartConfig, ChartContainer } from '@/components/ui/chart';\nimport { useReducedMotion } from '@/hooks/use-reduced-motion';\nimport { cn } from '@/lib/utils';\n\nconst MINI_CHART_MARGIN = { top: 0, right: 0, bottom: 0, left: 0 };\n\nfunction getFiniteMetricNumber(value: number, fallback = 0): number {\n return Number.isFinite(value) ? value : fallback;\n}\n\nfunction getFinitePositiveMetricNumber(\n value: number | undefined,\n fallback: number,\n): number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0\n ? value\n : fallback;\n}\n\nfunction getFinitePositiveMetricNumberOrUndefined(\n value: number | undefined,\n): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) && value > 0\n ? value\n : undefined;\n}\n\nfunction formatMetricValue(value: string | number): string | number {\n return typeof value === 'number'\n ? getFiniteMetricNumber(value).toLocaleString()\n : value;\n}\n\n// ============================================\n// MetricMiniChart - Sparkline chart component\n// ============================================\n\nconst metricMiniChartVariants = cva('relative shrink-0', {\n variants: {\n variant: {\n line: '',\n area: '',\n layers: '',\n },\n },\n defaultVariants: {\n variant: 'line',\n },\n});\n\nexport interface MetricMiniChartProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof metricMiniChartVariants> {\n /** Data points for the sparkline */\n data: number[];\n /** Color of the chart line/area */\n color?: string;\n /** Width of the chart */\n width?: number;\n /** Height of the chart */\n height?: number;\n /** Show marker dot at the end */\n showMarker?: boolean;\n /** Trend direction for styling */\n trend?: 'positive' | 'negative';\n}\n\nfunction MetricMiniChart({\n data,\n color,\n width,\n height = 56,\n variant = 'line',\n showMarker = true,\n trend = 'positive',\n className,\n ...props\n}: MetricMiniChartProps) {\n const gradientId = React.useId();\n const prefersReducedMotion = useReducedMotion();\n const shouldAnimate = !prefersReducedMotion;\n const trendColor =\n trend === 'positive' ? 'var(--success)' : 'var(--destructive)';\n const chartColor = color || trendColor;\n const chartWidth = getFinitePositiveMetricNumberOrUndefined(width);\n const chartHeight = getFinitePositiveMetricNumber(height, 56);\n const safeData = React.useMemo(\n () => data.map((value) => getFiniteMetricNumber(value)),\n [data],\n );\n\n // Transform data for recharts\n const chartData = React.useMemo(\n () => safeData.map((value, index) => ({ index, value })),\n [safeData],\n );\n\n const chartConfig: ChartConfig = React.useMemo(\n () => ({\n value: {\n color: chartColor,\n },\n }),\n [chartColor],\n );\n\n // Find the max value index for marker positioning\n const lastIndex = safeData.length - 1;\n const lastValue = safeData[lastIndex] ?? 0;\n const markerTop = React.useMemo(() => {\n const maxValue = safeData.length > 0 ? Math.max(...safeData) : 0;\n return maxValue <= 0\n ? 50\n : Math.max(10, Math.min(90, 100 - (lastValue / maxValue) * 100));\n }, [safeData, lastValue]);\n const rootStyle = React.useMemo(\n () => ({ width: chartWidth, height: chartHeight }),\n [chartHeight, chartWidth],\n );\n\n // Width can be undefined to allow CSS classes (like w-full) to control responsive width\n return (\n <div\n data-slot=\"metric-mini-chart\"\n aria-hidden=\"true\"\n inert={true}\n className={cn(metricMiniChartVariants({ variant }), className)}\n style={rootStyle}\n {...props}\n >\n <ChartContainer config={chartConfig} className=\"size-full\">\n {variant === 'layers' ? (\n <AreaChart data={chartData} margin={MINI_CHART_MARGIN}>\n <defs>\n <linearGradient\n id={`fillArea${gradientId}`}\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"1\"\n >\n <stop offset=\"0%\" stopColor={chartColor} stopOpacity={0.3} />\n <stop offset=\"100%\" stopColor={chartColor} stopOpacity={0.05} />\n </linearGradient>\n </defs>\n <Area\n type=\"monotone\"\n dataKey=\"value\"\n stroke={chartColor}\n strokeWidth={2}\n fill={`url(#fillArea${gradientId})`}\n isAnimationActive={shouldAnimate}\n />\n </AreaChart>\n ) : (\n <LineChart data={chartData} margin={MINI_CHART_MARGIN}>\n {variant === 'area' && (\n <>\n <defs>\n <linearGradient\n id={`fillLine${gradientId}`}\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"1\"\n >\n <stop\n offset=\"0%\"\n stopColor={chartColor}\n stopOpacity={0.15}\n />\n <stop\n offset=\"100%\"\n stopColor={chartColor}\n stopOpacity={0.02}\n />\n </linearGradient>\n </defs>\n <Area\n type=\"monotone\"\n dataKey=\"value\"\n stroke=\"none\"\n fill={`url(#fillLine${gradientId})`}\n isAnimationActive={shouldAnimate}\n />\n </>\n )}\n <Line\n type=\"monotone\"\n dataKey=\"value\"\n stroke={chartColor}\n strokeWidth={2}\n dot={false}\n isAnimationActive={shouldAnimate}\n />\n </LineChart>\n )}\n </ChartContainer>\n\n {/* Marker dot */}\n {showMarker && variant === 'line' && (\n <div\n className=\"absolute\"\n style={{\n right: 0,\n top: `${markerTop}%`,\n transform: 'translate(50%, -50%)',\n }}\n >\n <div\n className=\"size-[19px] rounded-full opacity-20\"\n style={{ backgroundColor: chartColor }}\n />\n <div\n className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 size-[10px] rounded-full bg-background border-2\"\n style={{ borderColor: chartColor }}\n />\n </div>\n )}\n </div>\n );\n}\n\nMetricMiniChart.displayName = 'MetricMiniChart';\n\n// ============================================\n// MetricChange - Change indicator component\n// ============================================\n\nconst metricChangeVariants = cva('inline-flex items-center gap-0.5', {\n variants: {\n variant: {\n default: '',\n badge:\n 'bg-background border border-border rounded-md shadow-xs px-1.5 py-0.5',\n },\n trend: {\n positive: '',\n negative: '',\n },\n },\n defaultVariants: {\n variant: 'default',\n trend: 'positive',\n },\n});\n\nexport interface MetricChangeProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof metricChangeVariants> {\n /** Change value (e.g., \"100%\", \"+25%\") */\n value: string;\n /** Icon type ('none' to hide icon) */\n icon?: 'arrow' | 'trend' | 'arrow-right' | 'none';\n}\n\nfunction MetricChange({\n value,\n variant = 'default',\n trend = 'positive',\n icon = 'arrow',\n className,\n ...props\n}: MetricChangeProps) {\n const iconColor =\n trend === 'positive' ? 'var(--success)' : 'var(--destructive)';\n const textColor =\n variant === 'badge'\n ? 'text-secondary-foreground'\n : trend === 'positive'\n ? 'text-success'\n : 'text-destructive';\n\n const IconComponent =\n icon === 'none'\n ? null\n : icon === 'trend'\n ? TrendingUp\n : icon === 'arrow-right'\n ? ArrowUpRight\n : ArrowUp;\n\n const iconSize = variant === 'badge' ? 12 : 16;\n\n return (\n <div\n className={cn(metricChangeVariants({ variant, trend }), className)}\n {...props}\n >\n {IconComponent && (\n <IconComponent\n aria-hidden=\"true\"\n width={iconSize}\n height={iconSize}\n color={iconColor}\n className={cn(trend === 'negative' && 'rotate-180')}\n />\n )}\n <span className=\"sr-only\">\n {trend === 'positive' ? 'Increased' : 'Decreased'}\n </span>\n <span className={cn('text-sm font-medium', textColor)}>{value}</span>\n </div>\n );\n}\n\nMetricChange.displayName = 'MetricChange';\n\n// ============================================\n// MetricItem - Main component\n// ============================================\n\nconst metricItemVariants = cva(\n 'relative flex flex-col gap-2 p-5 bg-background rounded-xl border border-border shadow-xs transition-shadow duration-200 ease-out hover:shadow-sm motion-reduce:transition-none',\n {\n variants: {\n type: {\n simple: '',\n 'icon-01': '',\n 'icon-02': '',\n 'icon-03': '',\n 'icon-04': 'flex-row items-center',\n 'chart-01': '',\n 'chart-02': '',\n 'chart-03': '',\n 'chart-04': 'p-0 bg-transparent border-0 shadow-none',\n },\n breakpoint: {\n desktop: '',\n mobile: '',\n },\n },\n defaultVariants: {\n type: 'simple',\n breakpoint: 'desktop',\n },\n },\n);\n\nexport interface MetricItemProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof metricItemVariants> {\n /** Title/label of the metric */\n title: string;\n /** Main value to display */\n value: string | number;\n /** Change indicator value */\n changeValue?: string;\n /** Change trend direction */\n changeTrend?: 'positive' | 'negative';\n /** Change icon type ('none' to hide icon) */\n changeIcon?: 'arrow' | 'trend' | 'arrow-right' | 'none';\n /** Comparison text (e.g., \"vs last month\") */\n comparisonText?: string;\n /** Subtitle/description text shown next to value (alternative to change indicator) */\n subtitle?: string;\n /** Show dropdown menu button */\n showDropdown?: boolean;\n /** Show action buttons */\n showActions?: boolean;\n /** Action button click handler */\n onViewReport?: () => void;\n /** Dropdown click handler */\n onDropdownClick?: () => void;\n /** Icon for icon variants */\n icon?: React.ReactNode;\n /** Icon background color */\n iconBgColor?: string;\n /** Icon color */\n iconColor?: string;\n /** Chart data for chart variants */\n chartData?: number[];\n /** Featured icon variant style */\n featuredIconVariant?: 'circle' | 'square' | 'modern';\n /** Custom content to render instead of chart (for chart-04 type) */\n children?: React.ReactNode;\n}\n\nconst MetricItem = React.memo(function MetricItem({\n title,\n value,\n changeValue,\n changeTrend = 'positive',\n changeIcon,\n comparisonText,\n subtitle,\n showDropdown = false,\n showActions = false,\n onViewReport,\n onDropdownClick,\n icon,\n iconBgColor,\n iconColor,\n chartData,\n featuredIconVariant = 'circle',\n type = 'simple',\n breakpoint = 'desktop',\n className,\n children,\n ...props\n}: MetricItemProps) {\n // Default icon for icon variants\n const defaultIcon = <TrendingUp width={24} height={24} />;\n const displayIcon = icon || defaultIcon;\n const displayValue = formatMetricValue(value);\n\n // Default icon colors based on type and variant\n const defaultIconBgColor =\n type === 'icon-01'\n ? featuredIconVariant === 'modern'\n ? 'var(--muted)' // for modern\n : 'var(--success-muted)' // for circle/square\n : type === 'icon-02' || type === 'icon-03' || type === 'icon-04'\n ? 'var(--muted)'\n : 'var(--success-muted)';\n\n const defaultIconColor =\n type === 'icon-01'\n ? featuredIconVariant === 'modern'\n ? 'var(--secondary-foreground)' // for modern\n : 'var(--success)' // for circle/square\n : type === 'icon-02'\n ? 'var(--secondary-foreground)'\n : 'var(--success)';\n\n const finalIconBgColor = iconBgColor || defaultIconBgColor;\n const finalIconColor = iconColor || defaultIconColor;\n\n // Chart-04 has a different structure\n if (type === 'chart-04') {\n return (\n <div\n data-slot=\"metric-item\"\n className={cn(metricItemVariants({ type, breakpoint }), className)}\n {...props}\n >\n <div className=\"flex flex-col gap-0.5 w-full\">\n {/* Header */}\n <div className=\"flex items-center px-5 pt-3 pb-2 w-full\">\n <p className=\"flex-1 text-sm font-semibold text-foreground\">\n {title}\n </p>\n </div>\n\n {/* Content card */}\n <div className=\"relative bg-background border border-border rounded-xl shadow-xs p-5 flex flex-col gap-5\">\n {/* Number and change */}\n <div className=\"flex flex-wrap items-center gap-3\">\n <p className=\"text-display-sm font-semibold text-foreground\">\n {displayValue}\n </p>\n {changeValue && (\n <div className=\"flex items-center gap-2\">\n <MetricChange\n value={changeValue}\n trend={changeTrend}\n // chart-04 defaults to 'trend' icon for backward compatibility\n icon={changeIcon ?? 'trend'}\n />\n {comparisonText && (\n <span className=\"text-sm font-medium text-muted-foreground\">\n {comparisonText}\n </span>\n )}\n </div>\n )}\n </div>\n\n {/* Chart or custom content */}\n {children ? (\n children\n ) : chartData ? (\n <MetricMiniChart\n data={chartData}\n width={undefined}\n height={56}\n variant=\"line\"\n showMarker={false}\n trend={changeTrend}\n className=\"w-full\"\n />\n ) : null}\n\n {/* Dropdown */}\n {showDropdown && (\n <button\n type=\"button\"\n aria-label=\"More options\"\n className=\"absolute top-2 end-2 p-3 cursor-pointer rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background transition-colors duration-150 ease-out hover:text-foreground\"\n onClick={onDropdownClick}\n >\n <MoreVertical\n width={20}\n height={20}\n className=\"text-muted-foreground\"\n />\n </button>\n )}\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-slot=\"metric-item\"\n className={cn(metricItemVariants({ type, breakpoint }), className)}\n {...props}\n >\n {/* Featured Icon (for icon-01 only - standalone icon above content) */}\n {type === 'icon-01' && (\n <div\n className={cn(\n 'flex items-center justify-center shrink-0 relative overflow-clip',\n featuredIconVariant === 'modern'\n ? 'rounded-[10px] border border-border shadow-xs'\n : featuredIconVariant === 'square'\n ? 'rounded-[10px] border border-border shadow-xs'\n : 'rounded-full',\n )}\n style={{\n width: 48,\n height: 48,\n backgroundColor: finalIconBgColor,\n color: finalIconColor,\n }}\n >\n {displayIcon}\n {/* Skeuomorphic inner shadow overlay for modern variant */}\n {featuredIconVariant === 'modern' && (\n <div\n className=\"absolute inset-0 pointer-events-none rounded-[10px]\"\n style={{\n boxShadow: 'var(--shadow-inset-ring)',\n }}\n />\n )}\n </div>\n )}\n\n {/* Icon-02 and Icon-03: Heading row with icon + title */}\n {(type === 'icon-02' || type === 'icon-03') && (\n <div className=\"flex items-center gap-3 w-full\">\n <div\n className={cn(\n 'flex items-center justify-center shrink-0',\n type === 'icon-02'\n ? 'rounded-full'\n : 'rounded-lg border border-border shadow-xs',\n )}\n style={{\n width: 48,\n height: 48,\n backgroundColor: finalIconBgColor,\n color: finalIconColor,\n }}\n >\n {icon || <Eye width={24} height={24} />}\n </div>\n <p className=\"flex-1 text-base font-semibold text-foreground\">\n {title}\n </p>\n </div>\n )}\n\n {/* Icon-04: Horizontal layout with featured icon + content */}\n {type === 'icon-04' && (\n <div\n className={cn(\n 'flex items-center justify-center shrink-0 rounded-md border border-border shadow-xs',\n )}\n style={{\n width: 40,\n height: 40,\n backgroundColor: finalIconBgColor,\n color: finalIconColor,\n }}\n >\n {icon || <TrendingUp width={20} height={20} />}\n </div>\n )}\n\n {/* Heading and number section */}\n {type !== 'icon-02' && type !== 'icon-03' && (\n <div\n className={cn(\n 'flex flex-col gap-2',\n type === 'icon-04' ? 'flex-1 min-w-0' : 'w-full',\n )}\n >\n {/* Title */}\n {type === 'chart-01' || type === 'chart-02' ? (\n <p className=\"text-base font-semibold text-foreground\">{title}</p>\n ) : type === 'icon-04' || type === 'icon-01' ? (\n <p className=\"text-sm font-semibold text-muted-foreground\">\n {title}\n </p>\n ) : (\n <p className=\"text-sm font-medium text-muted-foreground\">{title}</p>\n )}\n\n {/* Number and badge row */}\n {type === 'simple' || type === 'icon-01' || type === 'icon-04' ? (\n <div className=\"flex flex-wrap items-center justify-between gap-3 w-full\">\n <p className=\"text-display-sm font-semibold text-foreground\">\n {displayValue}\n </p>\n {/* Show inline change + comparison OR badge OR subtitle */}\n {changeValue && comparisonText ? (\n <div className=\"flex items-center gap-2\">\n <MetricChange\n value={changeValue}\n trend={changeTrend}\n icon=\"arrow\"\n />\n <span className=\"text-sm font-medium text-muted-foreground\">\n {comparisonText}\n </span>\n </div>\n ) : changeValue ? (\n <MetricChange\n value={changeValue}\n trend={changeTrend}\n // badge variant defaults to 'arrow-right' icon for backward compatibility\n icon={changeIcon ?? 'arrow-right'}\n variant=\"badge\"\n />\n ) : subtitle ? (\n <span className=\"text-sm font-medium text-muted-foreground\">\n {subtitle}\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n )}\n\n {/* Chart variants content */}\n {type === 'chart-01' && (\n <div className=\"flex items-end gap-4 w-full\">\n <div className=\"flex-1 flex flex-col gap-3\">\n <p className=\"text-display-sm font-semibold text-foreground\">\n {displayValue}\n </p>\n {(changeValue || comparisonText) && (\n <div className=\"flex items-center gap-2\">\n {changeValue && (\n <MetricChange\n value={changeValue}\n trend={changeTrend}\n icon=\"arrow\"\n />\n )}\n {comparisonText && (\n <span className=\"text-sm font-medium text-muted-foreground truncate\">\n {comparisonText}\n </span>\n )}\n </div>\n )}\n </div>\n {chartData && (\n <MetricMiniChart\n data={chartData}\n width={112}\n height={56}\n variant=\"line\"\n trend={changeTrend}\n />\n )}\n </div>\n )}\n\n {type === 'chart-02' && (\n <div className=\"flex items-end gap-4 w-full\">\n <div className=\"flex-1 flex items-start gap-2\">\n <p className=\"text-display-md font-semibold text-foreground\">\n {displayValue}\n </p>\n {changeValue && (\n <MetricChange\n value={changeValue}\n trend={changeTrend}\n icon=\"trend\"\n />\n )}\n </div>\n {chartData && (\n <MetricMiniChart\n data={chartData}\n width={128}\n height={56}\n variant=\"layers\"\n showMarker={false}\n trend={changeTrend}\n />\n )}\n </div>\n )}\n\n {type === 'chart-03' && (\n <>\n <div className=\"flex flex-col gap-2 w-full\">\n <p className=\"text-sm font-medium text-muted-foreground\">{title}</p>\n <div className=\"flex flex-wrap items-center justify-between gap-3 w-full\">\n <p className=\"text-display-sm font-semibold text-foreground\">\n {displayValue}\n </p>\n {(changeValue || comparisonText) && (\n <div className=\"flex items-center gap-2\">\n {changeValue && (\n <MetricChange\n value={changeValue}\n trend={changeTrend}\n icon=\"trend\"\n />\n )}\n {comparisonText && (\n <span className=\"text-sm font-medium text-muted-foreground\">\n {comparisonText}\n </span>\n )}\n </div>\n )}\n </div>\n </div>\n {chartData && (\n <MetricMiniChart\n data={chartData}\n height={72}\n variant=\"area\"\n showMarker={false}\n trend={changeTrend}\n className=\"w-full\"\n />\n )}\n </>\n )}\n\n {/* Icon-02 and Icon-03 number section */}\n {(type === 'icon-02' || type === 'icon-03') && (\n <div className=\"flex items-end gap-4 w-full\">\n <div className=\"flex-1 flex flex-col gap-3\">\n <p className=\"text-display-md font-semibold text-foreground\">\n {displayValue}\n </p>\n {(changeValue || comparisonText) && (\n <div className=\"flex items-center gap-2\">\n {changeValue && (\n <MetricChange\n value={changeValue}\n trend={changeTrend}\n icon={type === 'icon-03' ? 'trend' : 'arrow'}\n />\n )}\n {comparisonText && (\n <span className=\"text-sm font-medium text-muted-foreground truncate\">\n {comparisonText}\n </span>\n )}\n </div>\n )}\n </div>\n {chartData && (\n <MetricMiniChart\n data={chartData}\n width={112}\n height={56}\n variant=\"line\"\n trend={changeTrend}\n />\n )}\n </div>\n )}\n\n {/* Dropdown button */}\n {showDropdown && (\n <button\n type=\"button\"\n aria-label=\"More options\"\n className=\"absolute top-2 end-2 cursor-pointer rounded-sm p-3 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background transition-colors duration-150 ease-out hover:text-foreground\"\n onClick={onDropdownClick}\n >\n <MoreVertical\n width={20}\n height={20}\n className=\"text-muted-foreground\"\n />\n </button>\n )}\n\n {/* Action buttons */}\n {showActions && (\n <div className=\"flex items-center gap-2 pt-2\">\n <Button\n appearance=\"outline\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={onViewReport}\n >\n View report\n </Button>\n </div>\n )}\n </div>\n );\n});\n\nMetricItem.displayName = 'MetricItem';\n\nexport {\n MetricChange,\n MetricItem,\n MetricMiniChart,\n metricChangeVariants,\n metricItemVariants,\n metricMiniChartVariants,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index198.js","names":[],"sources":["../src/components/organisms/content/MetricCard/MetricCard.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { ArrowDown, ArrowUp } from 'lucide-react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { BarChart } from '@/components/molecules/charts/BarChart';\nimport {\n type ChartDataPoint,\n type ChartSeries,\n LineChart,\n} from '@/components/molecules/charts/LineChart';\nimport { cn } from '@/lib/utils';\n\nconst metricCardVariants = cva(\n 'bg-background border border-border rounded-xl shadow-sm flex flex-col w-full',\n {\n variants: {\n size: {\n sm: 'gap-3 p-3', // Compact: 12px padding, 12px gap\n md: 'gap-5 p-5', // Default: 20px padding, 20px gap (Figma)\n lg: 'gap-6 p-6', // Spacious: 24px padding, 24px gap\n },\n },\n defaultVariants: {\n size: 'md',\n },\n },\n);\n\n// Title sizes\nconst titleSizeClasses = {\n sm: 'text-sm', // 14px\n md: 'text-md', // 16px (matches Figma)\n lg: 'text-lg', // 18px\n};\n\n// Value sizes\nconst valueSizeClasses = {\n sm: 'text-display-xs',\n md: 'text-display-sm',\n lg: 'text-display-md',\n};\n\n// Change indicator sizes\nconst changeSizeClasses = {\n sm: 'text-xs', // 12px\n md: 'text-sm', // 14px (matches Figma)\n lg: 'text-base', // 16px\n};\n\n// Change sentiment colors\nconst changeSentimentColors = {\n positive: 'text-success',\n negative: 'text-destructive',\n neutral: 'text-muted-foreground',\n};\n\nfunction getFiniteMetricValue(value: number): number {\n return Number.isFinite(value) ? value : 0;\n}\n\nexport interface MetricCardProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof metricCardVariants> {\n // Required\n /** The primary metric value to display */\n value: number;\n\n // Optional core elements\n /** Title displayed above the metric */\n title?: string;\n /** Description text (e.g., \"vs last month\") */\n description?: string;\n\n // Change indicator (hybrid approach)\n /** Change value (auto-calculated if omitted but data provided) */\n change?: number;\n /** Explicit override for change value */\n changeOverride?: number;\n /** Sentiment for change indicator color */\n changeSentiment?: 'positive' | 'negative' | 'neutral';\n\n // Chart data (standardized format)\n /** Chart data points */\n data?: ChartDataPoint[];\n /** Chart series configuration */\n series?: ChartSeries[];\n /** Type of chart to display */\n chartType?: 'line' | 'bar' | 'area';\n\n // Chart customization (moderate control)\n /** Stroke width for line/area charts */\n strokeWidth?: number;\n /** Show area fill under lines (for line/area charts) */\n showArea?: boolean;\n /** Show dots on data points (for line charts) */\n showDots?: boolean;\n /** Curve type for line/area charts */\n curveType?: 'linear' | 'smooth';\n\n // Formatting\n /** Custom formatter for the value */\n valueFormatter?: (n: number) => string;\n /** Custom formatter for the change indicator */\n changeFormatter?: (n: number) => string;\n}\n\ninterface MetricCardMiniChartProps {\n data?: ChartDataPoint[];\n series?: ChartSeries[];\n chartType: NonNullable<MetricCardProps['chartType']>;\n strokeWidth: number;\n showArea: boolean;\n showDots: boolean;\n curveType: NonNullable<MetricCardProps['curveType']>;\n}\n\nfunction MetricCardMiniChart({\n data,\n series,\n chartType,\n strokeWidth,\n showArea,\n showDots,\n curveType,\n}: MetricCardMiniChartProps) {\n if (!data || !series) return null;\n\n const sharedProps = {\n data,\n series,\n size: 'sm' as const,\n showYAxis: false,\n showXAxis: false,\n showTooltip: false,\n className: 'h-14 w-28',\n };\n\n if (chartType === 'bar') {\n return <BarChart {...sharedProps} />;\n }\n\n return (\n <LineChart\n {...sharedProps}\n strokeWidth={strokeWidth}\n showDots={showDots}\n curveType={curveType}\n showArea={chartType === 'area' || showArea}\n />\n );\n}\n\nfunction MetricCard({\n value,\n title,\n description = 'vs last month',\n change,\n changeOverride,\n changeSentiment = 'neutral',\n data,\n series,\n chartType = 'line',\n strokeWidth = 2,\n showArea = true,\n showDots = false,\n curveType = 'smooth',\n valueFormatter = (n) => n.toLocaleString(),\n changeFormatter = (n) => {\n return `${Math.abs(n).toFixed(0)}%`;\n },\n size = 'md',\n className,\n ...props\n}: MetricCardProps) {\n // Count-up animation on mount\n const safeValue = getFiniteMetricValue(value);\n const [displayValue, setDisplayValue] = useState(0);\n const hasAnimated = useRef(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (hasAnimated.current) {\n setDisplayValue(safeValue);\n return;\n }\n hasAnimated.current = true;\n\n // Respect reduced motion preference (SSR-safe via ownerDocument.defaultView)\n const win = rootRef.current?.ownerDocument.defaultView;\n if (!win) {\n setDisplayValue(safeValue);\n return;\n }\n\n const prefersReducedMotion = win.matchMedia(\n '(prefers-reduced-motion: reduce)',\n ).matches;\n if (prefersReducedMotion) {\n setDisplayValue(safeValue);\n return;\n }\n\n const duration = 600;\n const startTime = win.performance.now();\n let frameId: number;\n\n const animate = (now: number) => {\n const elapsed = now - startTime;\n const progress = Math.min(elapsed / duration, 1);\n // Ease-out cubic for smooth deceleration\n const eased = 1 - (1 - progress) ** 3;\n setDisplayValue(Math.round(eased * safeValue));\n\n if (progress < 1) {\n frameId = win.requestAnimationFrame(animate);\n } else {\n setDisplayValue(safeValue);\n }\n };\n\n frameId = win.requestAnimationFrame(animate);\n return () => win.cancelAnimationFrame(frameId);\n }, [safeValue]);\n\n // Calculate change from data if not explicitly provided\n const calculatedChange = useMemo(() => {\n // Priority: override > explicit > auto-calculate\n if (changeOverride !== undefined)\n return getFiniteMetricValue(changeOverride);\n if (change !== undefined) return getFiniteMetricValue(change);\n if (!data || data.length < 2 || !series || series.length === 0)\n return undefined;\n\n // Compare first and last data points using the first series id\n const seriesId = series[0].id;\n const firstValue = data[0].values[seriesId];\n const lastValue = data[data.length - 1].values[seriesId];\n\n if (\n firstValue === 0 ||\n firstValue === undefined ||\n !Number.isFinite(firstValue) ||\n !Number.isFinite(lastValue)\n )\n return undefined; // Avoid division by zero and invalid calculations\n\n return ((lastValue - firstValue) / firstValue) * 100;\n }, [data, series, change, changeOverride]);\n\n const changeDirection =\n calculatedChange && calculatedChange > 0 ? 'up' : 'down';\n\n return (\n <div\n ref={rootRef}\n data-slot=\"metric-card\"\n className={cn(\n metricCardVariants({ size }),\n 'transition-shadow duration-200 ease-out hover:shadow-md motion-reduce:transition-none',\n className,\n )}\n {...props}\n >\n {/* Title */}\n {title && (\n <h3\n className={cn(\n 'font-semibold text-foreground',\n titleSizeClasses[size || 'md'],\n )}\n >\n {title}\n </h3>\n )}\n\n {/* Bottom row: Value + Change on left, Chart on right */}\n <div className=\"flex items-end gap-4\">\n {/* Left side: Value + Change badge */}\n <div className=\"flex flex-col gap-3 flex-1 min-w-0\">\n {/* Value */}\n <div\n className={cn(\n 'font-semibold text-foreground',\n valueSizeClasses[size || 'md'],\n )}\n >\n {valueFormatter(displayValue)}\n </div>\n\n {/* Change indicator + Description */}\n {calculatedChange !== undefined && (\n <div className=\"flex items-center gap-2\">\n {/* Change badge */}\n <div\n className={cn(\n 'inline-flex items-center gap-0.5 font-medium',\n changeSizeClasses[size || 'md'],\n changeSentimentColors[changeSentiment],\n )}\n >\n {changeDirection === 'up' ? (\n <ArrowUp className=\"size-4\" aria-hidden=\"true\" />\n ) : (\n <ArrowDown className=\"size-4\" aria-hidden=\"true\" />\n )}\n <span className=\"sr-only\">\n {changeDirection === 'up' ? 'Increased' : 'Decreased'}\n </span>\n <span>{changeFormatter(calculatedChange)}</span>\n </div>\n\n {/* Description */}\n {description && (\n <span\n className={cn(\n 'font-medium text-muted-foreground',\n changeSizeClasses[size || 'md'],\n )}\n >\n {description}\n </span>\n )}\n </div>\n )}\n </div>\n\n {/* Right side: Mini chart (decorative sparkline) */}\n {data && series && (\n <div aria-hidden=\"true\" inert={true}>\n <MetricCardMiniChart\n data={data}\n series={series}\n chartType={chartType}\n strokeWidth={strokeWidth}\n showArea={showArea}\n showDots={showDots}\n curveType={curveType}\n />\n </div>\n )}\n </div>\n </div>\n );\n}\n\nMetricCard.displayName = 'MetricCard';\n\nexport { MetricCard, metricCardVariants };\n"],"mappings":"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"}
|