@xenide-io/the-old-ui-theme 0.2.9 → 0.3.1

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.
Files changed (133) hide show
  1. package/README.md +1 -1
  2. package/dist/chunk-5HSOBJ4F.mjs +5968 -0
  3. package/dist/index-BgG8Homu.d.mts +1461 -0
  4. package/dist/index-BgG8Homu.d.ts +1461 -0
  5. package/dist/index.d.mts +206 -100
  6. package/dist/index.d.ts +206 -100
  7. package/dist/index.js +4275 -1455
  8. package/dist/index.mjs +689 -735
  9. package/dist/tailwind-preset.js +7 -0
  10. package/dist/tailwind-preset.mjs +7 -0
  11. package/dist/ui.d.mts +3 -2
  12. package/dist/ui.d.ts +3 -2
  13. package/dist/ui.js +3449 -701
  14. package/dist/ui.mjs +106 -3
  15. package/package.json +20 -16
  16. package/src/app/globals.css +1953 -266
  17. package/src/components/charts/index.ts +9 -8
  18. package/src/components/charts/quill/BarChart.tsx +85 -0
  19. package/src/components/charts/quill/FunnelChart.tsx +49 -0
  20. package/src/components/charts/quill/InsightShell.tsx +27 -0
  21. package/src/components/charts/quill/MetricCard.tsx +44 -0
  22. package/src/components/charts/quill/PieChart.tsx +81 -0
  23. package/src/components/charts/quill/Sparkline.tsx +57 -0
  24. package/src/components/charts/quill/TimeSeriesLineChart.tsx +62 -0
  25. package/src/components/charts/quill/index.ts +9 -0
  26. package/src/components/icons/IconBase.tsx +20 -12
  27. package/src/components/icons/createIconBase.tsx +12 -7
  28. package/src/components/icons/icons.tsx +210 -18
  29. package/src/components/icons/index.ts +3 -0
  30. package/src/components/icons/lemon-brand-icons.tsx +16 -0
  31. package/src/components/icons/lemon-category-icons.tsx +72 -0
  32. package/src/components/icons/lemon-icons.tsx +57 -0
  33. package/src/components/sections/AlertShowcase.tsx +2 -1
  34. package/src/components/sections/BadgeShowcase.tsx +1 -1
  35. package/src/components/sections/ButtonShowcase.tsx +6 -10
  36. package/src/components/sections/CardShowcase.tsx +22 -6
  37. package/src/components/sections/DropdownShowcase.tsx +4 -6
  38. package/src/components/sections/FilterChipsShowcase.tsx +74 -26
  39. package/src/components/sections/FormShowcase.tsx +2 -1
  40. package/src/components/sections/IconShowcase.tsx +195 -212
  41. package/src/components/sections/KbdShowcase.tsx +47 -21
  42. package/src/components/sections/ModalShowcase.tsx +7 -6
  43. package/src/components/sections/NewComponentsShowcase.tsx +1052 -0
  44. package/src/components/sections/ProductLayoutsShowcase.tsx +78 -0
  45. package/src/components/sections/ProgressShowcase.tsx +50 -53
  46. package/src/components/sections/QuillChartShowcase.tsx +92 -0
  47. package/src/components/sections/QuillDashboardShowcase.tsx +149 -0
  48. package/src/components/sections/SidebarShowcase.tsx +151 -0
  49. package/src/components/sections/SwapShowcase.tsx +13 -5
  50. package/src/components/sections/TableShowcase.tsx +2 -0
  51. package/src/components/sections/TooltipShowcase.tsx +19 -25
  52. package/src/components/ui/Alert.tsx +28 -19
  53. package/src/components/ui/AlertDialog.tsx +62 -0
  54. package/src/components/ui/AppLayout.tsx +27 -0
  55. package/src/components/ui/AuthLayout.tsx +58 -0
  56. package/src/components/ui/Autocomplete.tsx +106 -0
  57. package/src/components/ui/Badge.tsx +10 -5
  58. package/src/components/ui/Banner.tsx +63 -0
  59. package/src/components/ui/Button.tsx +22 -12
  60. package/src/components/ui/ButtonChrome.tsx +1 -1
  61. package/src/components/ui/ButtonGroup.tsx +16 -0
  62. package/src/components/ui/Calendar.tsx +6 -7
  63. package/src/components/ui/Card.tsx +21 -11
  64. package/src/components/ui/Chip.tsx +46 -0
  65. package/src/components/ui/Collapsible.tsx +46 -0
  66. package/src/components/ui/CollapsibleSection.tsx +11 -11
  67. package/src/components/ui/Combobox.tsx +119 -0
  68. package/src/components/ui/ContextMenu.tsx +81 -0
  69. package/src/components/ui/DataTable.tsx +133 -0
  70. package/src/components/ui/DatePicker.tsx +96 -0
  71. package/src/components/ui/Dialog.tsx +75 -0
  72. package/src/components/ui/Divider.tsx +39 -0
  73. package/src/components/ui/Dot.tsx +32 -0
  74. package/src/components/ui/DropdownMenu.tsx +189 -65
  75. package/src/components/ui/FilterBar.tsx +165 -0
  76. package/src/components/ui/FilterChips.tsx +65 -35
  77. package/src/components/ui/FilterControls.tsx +30 -0
  78. package/src/components/ui/FormField.tsx +69 -0
  79. package/src/components/ui/Input.tsx +166 -140
  80. package/src/components/ui/InputGroup.tsx +16 -0
  81. package/src/components/ui/Kbd.tsx +88 -13
  82. package/src/components/ui/Lettermark.tsx +47 -0
  83. package/src/components/ui/Link.tsx +32 -0
  84. package/src/components/ui/Loader.tsx +96 -0
  85. package/src/components/ui/LoadingBar.tsx +25 -0
  86. package/src/components/ui/Menubar.tsx +66 -0
  87. package/src/components/ui/Metric.tsx +34 -0
  88. package/src/components/ui/Modal.tsx +66 -36
  89. package/src/components/ui/NumberField.tsx +76 -0
  90. package/src/components/ui/Popover.tsx +73 -0
  91. package/src/components/ui/ProgressCircle.tsx +63 -0
  92. package/src/components/ui/Resizable.tsx +71 -0
  93. package/src/components/ui/Row.tsx +51 -0
  94. package/src/components/ui/ScrollArea.tsx +54 -0
  95. package/src/components/ui/SettingsLayout.tsx +94 -0
  96. package/src/components/ui/ShowcaseWrapper.tsx +4 -16
  97. package/src/components/ui/Sidebar.tsx +77 -0
  98. package/src/components/ui/Snack.tsx +48 -0
  99. package/src/components/ui/Spinner.tsx +30 -0
  100. package/src/components/ui/Splotch.tsx +33 -0
  101. package/src/components/ui/Stepper.tsx +5 -6
  102. package/src/components/ui/Table.tsx +2 -0
  103. package/src/components/ui/Tag.tsx +65 -0
  104. package/src/components/ui/Toggle.tsx +62 -0
  105. package/src/components/ui/Tooltip.test.tsx +58 -0
  106. package/src/components/ui/Tooltip.tsx +211 -0
  107. package/src/components/ui/Widget.tsx +31 -0
  108. package/src/components/ui/first-slice.test.tsx +98 -0
  109. package/src/components/ui/index.ts +335 -22
  110. package/src/components/ui/interaction-primitives.test.tsx +77 -0
  111. package/src/components/ui/kbd-icons.test.tsx +90 -0
  112. package/src/components/ui/product-patterns.test.tsx +77 -0
  113. package/src/styles/themes.css +411 -1
  114. package/tailwind-preset.ts +7 -0
  115. package/dist/chunk-SHF57J7U.mjs +0 -2910
  116. package/dist/index-CmS6hcUk.d.mts +0 -753
  117. package/dist/index-CmS6hcUk.d.ts +0 -753
  118. package/src/components/charts/ph-insight-charts.tsx +0 -162
  119. package/src/components/dashboard/DashboardFiltersBar.tsx +0 -19
  120. package/src/components/dashboard/DashboardGrid.tsx +0 -23
  121. package/src/components/dashboard/DashboardInsightPlaceholder.tsx +0 -37
  122. package/src/components/dashboard/DashboardKpiCard.tsx +0 -25
  123. package/src/components/dashboard/DashboardToolbar.tsx +0 -23
  124. package/src/components/dashboard/DashboardWell.tsx +0 -10
  125. package/src/components/dashboard/InsightMiniCard.tsx +0 -26
  126. package/src/components/dashboard/InsightShell.tsx +0 -37
  127. package/src/components/dashboard/InsightShellHeader.tsx +0 -22
  128. package/src/components/dashboard/MiniTrendBars.tsx +0 -51
  129. package/src/components/dashboard/index.ts +0 -31
  130. package/src/components/dashboard/types.ts +0 -9
  131. package/src/components/sections/ChartShowcase.tsx +0 -190
  132. package/src/components/sections/DashboardShowcase.tsx +0 -191
  133. package/src/components/sections/StatShowcase.tsx +0 -129
@@ -1,8 +1,9 @@
1
- export {
2
- HogTrendLineChart,
3
- InsightDoughnutChart,
4
- InsightHorizontalBarChart,
5
- InsightPolarAreaChart,
6
- InsightStackedBarChart,
7
- InsightVerticalBarChart,
8
- } from "@/components/charts/ph-insight-charts";
1
+ "use client";
2
+
3
+ export { TimeSeriesLineChart, BarChart, PieChart, Sparkline, MetricCard, FunnelChart } from "./quill";
4
+ export type { TimeSeriesLineChartProps, TimeSeriesLineChartSeries } from "./quill";
5
+ export type { BarChartProps, BarChartSeries } from "./quill";
6
+ export type { PieChartProps, PieChartSlice } from "./quill";
7
+ export type { SparklineProps } from "./quill";
8
+ export type { MetricCardProps } from "./quill";
9
+ export type { FunnelChartProps, FunnelStep } from "./quill/FunnelChart";
@@ -0,0 +1,85 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { useChartTokens } from "@/lib/chart/use-ph-chart-tokens";
5
+ import { cn } from "@/lib/cn";
6
+
7
+ export interface BarChartSeries {
8
+ key: string;
9
+ label: string;
10
+ data: number[];
11
+ color?: string;
12
+ }
13
+
14
+ export interface BarChartProps {
15
+ series: BarChartSeries[];
16
+ labels: string[];
17
+ className?: string;
18
+ height?: number;
19
+ }
20
+
21
+ export function BarChart({ series, labels, className, height = 280 }: BarChartProps) {
22
+ const tokens = useChartTokens();
23
+ const groupCount = labels.length;
24
+ const barCount = series.length;
25
+
26
+ const { maxVal, chartData } = useMemo(() => {
27
+ let mx = 0;
28
+ const flat = series.flatMap((s) => s.data);
29
+ for (const v of flat) if (v > mx) mx = v;
30
+ return {
31
+ maxVal: mx || 1,
32
+ chartData: series.map((s, i) => ({
33
+ label: s.label,
34
+ color: s.color ?? tokens.series[i % tokens.series.length],
35
+ values: s.data,
36
+ })),
37
+ };
38
+ }, [series, tokens.series]);
39
+
40
+ const padding = { top: 16, right: 8, bottom: 24, left: 8 };
41
+ const innerW = 400 - padding.left - padding.right;
42
+ const innerH = height - padding.top - padding.bottom;
43
+ const groupW = innerW / groupCount;
44
+ const barW = Math.max(4, (groupW * 0.7) / barCount);
45
+ const gap = groupW * 0.15;
46
+
47
+ return (
48
+ <div className={cn("relative w-full", className)} style={{ height }}>
49
+ <svg viewBox={`0 0 400 ${height}`} className="h-full w-full" preserveAspectRatio="xMidYMid meet" suppressHydrationWarning>
50
+ {chartData.map((bar, bi) =>
51
+ bar.values.map((v, li) => {
52
+ const x = padding.left + li * groupW + gap / 2 + bi * barW;
53
+ const barH = (v / maxVal) * (innerH - 4);
54
+ const y = padding.top + innerH - barH;
55
+ return (
56
+ <rect
57
+ key={`${bi}-${li}`}
58
+ x={x}
59
+ y={y}
60
+ width={barW}
61
+ height={barH}
62
+ fill={bar.color}
63
+ rx={2}
64
+ >
65
+ <title>{`${bar.label}: ${v}`}</title>
66
+ </rect>
67
+ );
68
+ })
69
+ )}
70
+ {labels.map((l, i) => (
71
+ <text
72
+ key={l}
73
+ x={padding.left + i * groupW + groupW / 2}
74
+ y={height - 4}
75
+ textAnchor="middle"
76
+ className="fill-ph-mutedtext"
77
+ fontSize="10"
78
+ >
79
+ {l}
80
+ </text>
81
+ ))}
82
+ </svg>
83
+ </div>
84
+ );
85
+ }
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { useChartTokens } from "@/lib/chart/use-ph-chart-tokens";
5
+ import { cn } from "@/lib/cn";
6
+
7
+ export interface FunnelStep {
8
+ label: string;
9
+ count: number;
10
+ }
11
+
12
+ export interface FunnelChartProps {
13
+ steps: FunnelStep[];
14
+ className?: string;
15
+ }
16
+
17
+ export function FunnelChart({ steps, className }: FunnelChartProps) {
18
+ const tokens = useChartTokens();
19
+
20
+ const maxCount = useMemo(() => Math.max(...steps.map((s) => s.count)), [steps]);
21
+
22
+ return (
23
+ <div className={cn("space-y-2", className)} suppressHydrationWarning>
24
+ {steps.map((step, i) => {
25
+ const pct = (step.count / maxCount) * 100;
26
+ const prev = i > 0 ? steps[i - 1].count : step.count;
27
+ const dropoff = prev > 0 ? ((prev - step.count) / prev) * 100 : 0;
28
+
29
+ return (
30
+ <div key={step.label} className="space-y-1">
31
+ <div className="flex items-center justify-between text-sm">
32
+ <span className="font-medium text-ph-ink">{step.label}</span>
33
+ <span className="tabular-nums text-ph-subtle">{step.count.toLocaleString()}</span>
34
+ </div>
35
+ <div className="relative h-8 overflow-hidden rounded-md" style={{ background: tokens.muted }}>
36
+ <div
37
+ className="h-full rounded-md transition-all duration-500"
38
+ style={{ width: `${pct}%`, background: i === 0 ? tokens.series[0] : tokens.series[i % tokens.series.length] }}
39
+ />
40
+ </div>
41
+ {i > 0 && dropoff > 0 && (
42
+ <p className="text-xs text-ph-danger">-{dropoff.toFixed(1)}% dropoff</p>
43
+ )}
44
+ </div>
45
+ );
46
+ })}
47
+ </div>
48
+ );
49
+ }
@@ -0,0 +1,27 @@
1
+ "use client";
2
+
3
+ import type { ReactNode } from "react";
4
+ import { cn } from "@/lib/cn";
5
+
6
+ export interface InsightShellProps {
7
+ title: string;
8
+ subtitle?: string;
9
+ ribbonColor?: string;
10
+ children: ReactNode;
11
+ className?: string;
12
+ }
13
+
14
+ export function InsightShell({ title, subtitle, ribbonColor, children, className }: InsightShellProps) {
15
+ return (
16
+ <div className={cn("ph-insight-shell", className)}>
17
+ <div className="flex items-start gap-3 px-4 pt-4">
18
+ {ribbonColor && <div className="ph-insight-shell__ribbon" style={{ background: ribbonColor }} />}
19
+ <div className="min-w-0 flex-1">
20
+ <h3 className="text-sm font-semibold text-ph-ink truncate">{title}</h3>
21
+ {subtitle && <p className="text-xs text-ph-mutedtext mt-0.5">{subtitle}</p>}
22
+ </div>
23
+ </div>
24
+ <div className="p-4 pt-3">{children}</div>
25
+ </div>
26
+ );
27
+ }
@@ -0,0 +1,44 @@
1
+ "use client";
2
+
3
+ import type { ReactNode } from "react";
4
+ import { cn } from "@/lib/cn";
5
+ import { Sparkline } from "@/components/charts/quill/Sparkline";
6
+ import { Badge } from "@/components/ui/Badge";
7
+
8
+ export interface MetricCardProps {
9
+ value: string | number;
10
+ label: string;
11
+ change?: { value: number; positive?: boolean };
12
+ sparklineData?: number[];
13
+ icon?: ReactNode;
14
+ className?: string;
15
+ }
16
+
17
+ export function MetricCard({
18
+ value,
19
+ label,
20
+ change,
21
+ sparklineData,
22
+ icon,
23
+ className,
24
+ }: MetricCardProps) {
25
+ return (
26
+ <div className={cn("ph-metric", className)}>
27
+ <div className="ph-metric__header">
28
+ {icon && <span className="ph-metric__icon">{icon}</span>}
29
+ <span className="ph-metric__label">{label}</span>
30
+ {change && (
31
+ <Badge variant={change.positive ?? change.value >= 0 ? "success" : "danger"}>
32
+ {change.value >= 0 ? "+" : ""}{change.value}%
33
+ </Badge>
34
+ )}
35
+ </div>
36
+ <div className="ph-metric__value">{value}</div>
37
+ {sparklineData && sparklineData.length > 0 && (
38
+ <div className="mt-2">
39
+ <Sparkline data={sparklineData} />
40
+ </div>
41
+ )}
42
+ </div>
43
+ );
44
+ }
@@ -0,0 +1,81 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { useChartTokens } from "@/lib/chart/use-ph-chart-tokens";
5
+ import { cn } from "@/lib/cn";
6
+
7
+ export interface PieChartSlice {
8
+ key: string;
9
+ label: string;
10
+ value: number;
11
+ color?: string;
12
+ }
13
+
14
+ export interface PieChartProps {
15
+ slices: PieChartSlice[];
16
+ className?: string;
17
+ size?: number;
18
+ }
19
+
20
+ export function PieChart({ slices, className, size = 200 }: PieChartProps) {
21
+ const tokens = useChartTokens();
22
+
23
+ const { total, segments } = useMemo(() => {
24
+ const t = slices.reduce((s, sl) => s + Math.max(0, sl.value), 0);
25
+ let cum = 0;
26
+ const segs = slices.map((sl, i) => {
27
+ const start = cum;
28
+ const val = Math.max(0, sl.value);
29
+ cum += val;
30
+ return {
31
+ ...sl,
32
+ color: sl.color ?? tokens.series[i % tokens.series.length],
33
+ startAngle: (start / t) * 360,
34
+ endAngle: (cum / t) * 360,
35
+ pct: t > 0 ? (val / t) * 100 : 0,
36
+ };
37
+ });
38
+ return { total: t, segments: segs };
39
+ }, [slices, tokens.series]);
40
+
41
+ const cx = size / 2;
42
+ const cy = size / 2;
43
+ const r = size * 0.38;
44
+ const ir = r * 0.6;
45
+
46
+ const polarToCart = (angleDeg: number, radius: number) => {
47
+ const rad = ((angleDeg - 90) * Math.PI) / 180;
48
+ return { x: cx + radius * Math.cos(rad), y: cy + radius * Math.sin(rad) };
49
+ };
50
+
51
+ const describeArc = (start: number, end: number) => {
52
+ if (end - start >= 359.9) {
53
+ const p1 = polarToCart(start, r);
54
+ const p2 = polarToCart(start + 179.9, r);
55
+ const p3 = polarToCart(start + 359.9, r);
56
+ return `M${p1.x},${p1.y} A${r},${r} 0 0,1 ${p2.x},${p2.y} A${r},${r} 0 0,1 ${p3.x},${p3.y} Z`;
57
+ }
58
+ const s = polarToCart(start, r);
59
+ const e = polarToCart(end, r);
60
+ const si = polarToCart(start, ir);
61
+ const ei = polarToCart(end, ir);
62
+ const large = end - start > 180 ? 1 : 0;
63
+ return `M${s.x},${s.y} A${r},${r} 0 ${large},1 ${e.x},${e.y} L${ei.x},${ei.y} A${ir},${ir} 0 ${large},0 ${si.x},${si.y} Z`;
64
+ };
65
+
66
+ return (
67
+ <div className={cn("relative", className)} style={{ width: size, height: size }}>
68
+ <svg viewBox={`0 0 ${size} ${size}`} className="h-full w-full" suppressHydrationWarning>
69
+ {segments.map((s) => (
70
+ <path key={s.key} d={describeArc(s.startAngle, s.endAngle)} fill={s.color}>
71
+ <title>{`${s.label}: ${s.pct.toFixed(1)}%`}</title>
72
+ </path>
73
+ ))}
74
+ <circle cx={cx} cy={cy} r={ir} fill="var(--ph-surface)" />
75
+ </svg>
76
+ <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
77
+ <span className="text-lg font-bold text-ph-ink tabular-nums">{slices.length}</span>
78
+ </div>
79
+ </div>
80
+ );
81
+ }
@@ -0,0 +1,57 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { useChartTokens } from "@/lib/chart/use-ph-chart-tokens";
5
+ import { cn } from "@/lib/cn";
6
+
7
+ export interface SparklineProps {
8
+ data: number[];
9
+ color?: string;
10
+ className?: string;
11
+ width?: number;
12
+ height?: number;
13
+ strokeWidth?: number;
14
+ }
15
+
16
+ export function Sparkline({
17
+ data,
18
+ color,
19
+ className,
20
+ width = 80,
21
+ height = 32,
22
+ strokeWidth = 2,
23
+ }: SparklineProps) {
24
+ const tokens = useChartTokens();
25
+ const resolvedColor = color ?? tokens.series[0];
26
+
27
+ const path = useMemo(() => {
28
+ if (data.length < 2) return "";
29
+ const min = Math.min(...data);
30
+ const max = Math.max(...data);
31
+ const range = max - min || 1;
32
+ const xStep = width / (data.length - 1);
33
+
34
+ return data
35
+ .map((v, i) => {
36
+ const x = i * xStep;
37
+ const y = height - ((v - min) / range) * (height - 4) - 2;
38
+ return `${i === 0 ? "M" : "L"}${x},${y}`;
39
+ })
40
+ .join(" ");
41
+ }, [data, width, height]);
42
+
43
+ if (data.length < 2) return null;
44
+
45
+ return (
46
+ <svg
47
+ className={cn("shrink-0", className)}
48
+ width={width}
49
+ height={height}
50
+ viewBox={`0 0 ${width} ${height}`}
51
+ aria-hidden
52
+ suppressHydrationWarning
53
+ >
54
+ <path d={path} fill="none" stroke={resolvedColor} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round" />
55
+ </svg>
56
+ );
57
+ }
@@ -0,0 +1,62 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { CanvasTrendLineChart } from "@/lib/hog-charts-lite/CanvasTrendLineChart";
5
+ import { useChartTokens } from "@/lib/chart/use-ph-chart-tokens";
6
+ import { buildHogChartTheme } from "@/lib/hog-charts-lite/build-theme";
7
+ import { cn } from "@/lib/cn";
8
+
9
+ export interface TimeSeriesLineChartSeries {
10
+ key: string;
11
+ label: string;
12
+ data: number[];
13
+ color?: string;
14
+ }
15
+
16
+ export interface TimeSeriesLineChartProps {
17
+ series: TimeSeriesLineChartSeries[];
18
+ labels: string[];
19
+ className?: string;
20
+ showGrid?: boolean;
21
+ showCrosshair?: boolean;
22
+ height?: number;
23
+ }
24
+
25
+ export function TimeSeriesLineChart({
26
+ series,
27
+ labels,
28
+ className,
29
+ showGrid = true,
30
+ showCrosshair = true,
31
+ height = 280,
32
+ }: TimeSeriesLineChartProps) {
33
+ const tokens = useChartTokens();
34
+ const theme = useMemo(() => buildHogChartTheme(tokens), [tokens]);
35
+
36
+ const resolved = useMemo(
37
+ () =>
38
+ series.map((s, i) => ({
39
+ key: s.key,
40
+ label: s.label,
41
+ data: s.data,
42
+ color: s.color ?? tokens.series[i % tokens.series.length],
43
+ })),
44
+ [series, tokens.series]
45
+ );
46
+
47
+ return (
48
+ <div
49
+ className={cn("relative w-full", className)}
50
+ style={{ height, minHeight: 220 }}
51
+ >
52
+ <CanvasTrendLineChart
53
+ labels={labels}
54
+ series={resolved}
55
+ theme={theme}
56
+ showGrid={showGrid}
57
+ showCrosshair={showCrosshair}
58
+ className="h-full min-h-[220px]"
59
+ />
60
+ </div>
61
+ );
62
+ }
@@ -0,0 +1,9 @@
1
+ "use client";
2
+
3
+ export { TimeSeriesLineChart, type TimeSeriesLineChartProps, type TimeSeriesLineChartSeries } from "./TimeSeriesLineChart";
4
+ export { BarChart, type BarChartProps, type BarChartSeries } from "./BarChart";
5
+ export { PieChart, type PieChartProps, type PieChartSlice } from "./PieChart";
6
+ export { Sparkline, type SparklineProps } from "./Sparkline";
7
+ export { MetricCard, type MetricCardProps } from "./MetricCard";
8
+ export { FunnelChart, type FunnelChartProps, type FunnelStep } from "./FunnelChart";
9
+ export { InsightShell, type InsightShellProps } from "./InsightShell";
@@ -1,13 +1,13 @@
1
- import type { SVGAttributes } from "react";
1
+ import { forwardRef, type SVGAttributes } from "react";
2
2
  import { cn } from "@/lib/cn";
3
3
 
4
4
  export interface IconProps extends SVGAttributes<SVGSVGElement> {
5
- /** Pixel size or CSS length defaults to `1em` (Lemon parity). */
5
+ /** Pixel size or CSS length; defaults to `1em`. */
6
6
  size?: number | string;
7
7
  }
8
8
 
9
- /** Base SVG wrapper mirrors PostHog `LemonIconBase` + `.LemonIcon` metrics. */
10
- export function IconBase({
9
+ /** Shared SVG wrapper with predictable sizing and accessible-name behavior. */
10
+ export const IconBase = forwardRef<SVGSVGElement, IconProps>(function IconBase({
11
11
  className,
12
12
  size,
13
13
  children,
@@ -15,25 +15,33 @@ export function IconBase({
15
15
  fill = "none",
16
16
  xmlns = "http://www.w3.org/2000/svg",
17
17
  focusable = "false",
18
+ style,
19
+ role,
20
+ "aria-hidden": ariaHidden,
21
+ "aria-label": ariaLabel,
22
+ "aria-labelledby": ariaLabelledBy,
18
23
  ...props
19
- }: IconProps) {
20
- const dimensionProps =
21
- size != null
22
- ? { width: size, height: size }
23
- : {};
24
+ }, ref) {
25
+ const hasAccessibleName = Boolean(ariaLabel || ariaLabelledBy);
24
26
 
25
27
  return (
26
28
  <svg
29
+ ref={ref}
27
30
  className={cn("ph-icon", className)}
28
- {...dimensionProps}
29
31
  viewBox={viewBox}
30
32
  fill={fill}
31
33
  xmlns={xmlns}
32
34
  focusable={focusable}
33
- aria-hidden={props["aria-hidden"] ?? true}
35
+ role={role ?? (hasAccessibleName ? "img" : undefined)}
36
+ aria-hidden={ariaHidden ?? (hasAccessibleName ? undefined : true)}
37
+ aria-label={ariaLabel}
38
+ aria-labelledby={ariaLabelledBy}
39
+ style={{ width: size, height: size, ...style }}
34
40
  {...props}
35
41
  >
36
42
  {children}
37
43
  </svg>
38
44
  );
39
- }
45
+ });
46
+
47
+ IconBase.displayName = "IconBase";
@@ -1,26 +1,31 @@
1
- import type { ReactNode } from "react";
1
+ import { forwardRef, type ReactNode } from "react";
2
2
  import type { IconProps } from "@/components/icons/IconBase";
3
3
  import { IconBase } from "@/components/icons/IconBase";
4
4
 
5
5
  interface IconBaseDefinition {
6
+ name?: string;
6
7
  viewBox?: string;
7
8
  paths: ReactNode;
8
9
  }
9
10
 
10
- /** Factory for filled Lemon-style glyphs (Material / PostHog paths). */
11
- export function createIconBase({ viewBox = "0 0 24 24", paths }: IconBaseDefinition) {
12
- return function IconGlyph(props: IconProps) {
11
+ /** Factory for filled Old UI, Lemon, and Material-style glyphs. */
12
+ export function createIconBase({ name = "IconGlyph", viewBox = "0 0 24 24", paths }: IconBaseDefinition) {
13
+ const IconGlyph = forwardRef<SVGSVGElement, IconProps>(function IconGlyph(props, ref) {
13
14
  return (
14
- <IconBase viewBox={viewBox} {...props}>
15
+ <IconBase ref={ref} viewBox={viewBox} {...props}>
15
16
  {paths}
16
17
  </IconBase>
17
18
  );
18
- };
19
+ });
20
+
21
+ IconGlyph.displayName = name;
22
+ return IconGlyph;
19
23
  }
20
24
 
21
25
  /** Shorthand for single-path filled icons. */
22
- export function createIconBasePath(d: string, viewBox = "0 0 24 24") {
26
+ export function createIconBasePath(d: string, viewBox = "0 0 24 24", name?: string) {
23
27
  return createIconBase({
28
+ name,
24
29
  viewBox,
25
30
  paths: <path d={d} fill="currentColor" />,
26
31
  });