@xenide-io/the-old-ui-theme 0.2.6 → 0.3.0

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 (126) hide show
  1. package/README.md +1 -1
  2. package/dist/chunk-FDMD3IIN.mjs +3831 -0
  3. package/dist/index-D1RTT2Ap.d.mts +1044 -0
  4. package/dist/index-D1RTT2Ap.d.ts +1044 -0
  5. package/dist/index.d.mts +11 -758
  6. package/dist/index.d.ts +11 -758
  7. package/dist/index.js +1749 -768
  8. package/dist/index.mjs +312 -2846
  9. package/dist/tailwind-preset.js +7 -0
  10. package/dist/tailwind-preset.mjs +7 -0
  11. package/dist/ui.d.mts +4 -0
  12. package/dist/ui.d.ts +4 -0
  13. package/dist/ui.js +3864 -0
  14. package/dist/ui.mjs +169 -0
  15. package/package.json +28 -6
  16. package/src/app/globals.css +1005 -227
  17. package/src/components/charts/index.ts +8 -0
  18. package/src/components/charts/ph-insight-charts.tsx +162 -0
  19. package/src/components/dashboard/DashboardFiltersBar.tsx +19 -0
  20. package/src/components/dashboard/DashboardGrid.tsx +23 -0
  21. package/src/components/dashboard/DashboardInsightPlaceholder.tsx +37 -0
  22. package/src/components/dashboard/DashboardKpiCard.tsx +25 -0
  23. package/src/components/dashboard/DashboardToolbar.tsx +23 -0
  24. package/src/components/dashboard/DashboardWell.tsx +10 -0
  25. package/src/components/dashboard/InsightMiniCard.tsx +26 -0
  26. package/src/components/dashboard/InsightShell.tsx +37 -0
  27. package/src/components/dashboard/InsightShellHeader.tsx +22 -0
  28. package/src/components/dashboard/MiniTrendBars.tsx +51 -0
  29. package/src/components/dashboard/index.ts +31 -0
  30. package/src/components/dashboard/types.ts +9 -0
  31. package/src/components/icons/IconBase.tsx +47 -0
  32. package/src/components/icons/createIconBase.tsx +32 -0
  33. package/src/components/icons/icons.tsx +557 -0
  34. package/src/components/icons/index.ts +3 -0
  35. package/src/components/sections/AccordionShowcase.tsx +45 -0
  36. package/src/components/sections/AlertShowcase.tsx +40 -0
  37. package/src/components/sections/AvatarShowcase.tsx +80 -0
  38. package/src/components/sections/BadgeShowcase.tsx +65 -0
  39. package/src/components/sections/ButtonShowcase.tsx +308 -0
  40. package/src/components/sections/CalendarShowcase.tsx +35 -0
  41. package/src/components/sections/CardShowcase.tsx +159 -0
  42. package/src/components/sections/ChartShowcase.tsx +190 -0
  43. package/src/components/sections/CodeMockupShowcase.tsx +56 -0
  44. package/src/components/sections/ColorPalette.tsx +117 -0
  45. package/src/components/sections/CommandPaletteShowcase.tsx +48 -0
  46. package/src/components/sections/CountdownShowcase.tsx +43 -0
  47. package/src/components/sections/DashboardShowcase.tsx +191 -0
  48. package/src/components/sections/DiffShowcase.tsx +70 -0
  49. package/src/components/sections/DrawerShowcase.tsx +97 -0
  50. package/src/components/sections/DropdownShowcase.tsx +98 -0
  51. package/src/components/sections/EmptyStateShowcase.tsx +50 -0
  52. package/src/components/sections/FilterChipsShowcase.tsx +98 -0
  53. package/src/components/sections/FormShowcase.tsx +127 -0
  54. package/src/components/sections/HoverCardShowcase.tsx +50 -0
  55. package/src/components/sections/IconShowcase.tsx +127 -0
  56. package/src/components/sections/IndicatorShowcase.tsx +52 -0
  57. package/src/components/sections/KbdShowcase.tsx +57 -0
  58. package/src/components/sections/ModalShowcase.tsx +211 -0
  59. package/src/components/sections/NavigationShowcase.tsx +199 -0
  60. package/src/components/sections/ProductLayoutsShowcase.tsx +138 -0
  61. package/src/components/sections/ProgressShowcase.tsx +82 -0
  62. package/src/components/sections/SegmentedControlShowcase.tsx +49 -0
  63. package/src/components/sections/SetupThemeShowcase.tsx +262 -0
  64. package/src/components/sections/StackShowcase.tsx +33 -0
  65. package/src/components/sections/StatShowcase.tsx +129 -0
  66. package/src/components/sections/StepperShowcase.tsx +37 -0
  67. package/src/components/sections/SwapShowcase.tsx +99 -0
  68. package/src/components/sections/TabShowcase.tsx +84 -0
  69. package/src/components/sections/TableShowcase.tsx +140 -0
  70. package/src/components/sections/TimelineShowcase.tsx +83 -0
  71. package/src/components/sections/ToastShowcase.tsx +108 -0
  72. package/src/components/sections/TooltipShowcase.tsx +44 -0
  73. package/src/components/sections/UtilityShowcase.tsx +81 -0
  74. package/src/components/ui/Accordion.tsx +77 -0
  75. package/src/components/ui/Alert.tsx +77 -0
  76. package/src/components/ui/AuthLayout.tsx +58 -0
  77. package/src/components/ui/Avatar.tsx +97 -0
  78. package/src/components/ui/Badge.tsx +60 -0
  79. package/src/components/ui/Button.tsx +120 -0
  80. package/src/components/ui/ButtonChrome.tsx +20 -0
  81. package/src/components/ui/Calendar.tsx +115 -0
  82. package/src/components/ui/Card.tsx +70 -0
  83. package/src/components/ui/ChatBubble.tsx +71 -0
  84. package/src/components/ui/CodeBlock.tsx +48 -0
  85. package/src/components/ui/CollapsibleSection.tsx +49 -0
  86. package/src/components/ui/CommandPalette.tsx +137 -0
  87. package/src/components/ui/ComponentDocs.tsx +52 -0
  88. package/src/components/ui/Drawer.tsx +71 -0
  89. package/src/components/ui/DropdownMenu.tsx +262 -0
  90. package/src/components/ui/EmptyState.tsx +33 -0
  91. package/src/components/ui/FilterBar.tsx +165 -0
  92. package/src/components/ui/FilterChips.tsx +80 -0
  93. package/src/components/ui/FilterControls.tsx +30 -0
  94. package/src/components/ui/FormField.tsx +69 -0
  95. package/src/components/ui/HoverCard.tsx +36 -0
  96. package/src/components/ui/Indicator.tsx +61 -0
  97. package/src/components/ui/Input.tsx +385 -0
  98. package/src/components/ui/Kbd.tsx +113 -0
  99. package/src/components/ui/Loader.tsx +96 -0
  100. package/src/components/ui/Modal.tsx +105 -0
  101. package/src/components/ui/Navigation.tsx +94 -0
  102. package/src/components/ui/Panel.tsx +18 -0
  103. package/src/components/ui/Progress.tsx +59 -0
  104. package/src/components/ui/Rating.tsx +65 -0
  105. package/src/components/ui/SearchInput.tsx +106 -0
  106. package/src/components/ui/SegmentedControl.tsx +44 -0
  107. package/src/components/ui/SettingsLayout.tsx +94 -0
  108. package/src/components/ui/ShowcaseWrapper.tsx +124 -0
  109. package/src/components/ui/Stat.tsx +39 -0
  110. package/src/components/ui/Stepper.tsx +74 -0
  111. package/src/components/ui/Table.tsx +55 -0
  112. package/src/components/ui/Tabs.tsx +53 -0
  113. package/src/components/ui/Terminal.tsx +54 -0
  114. package/src/components/ui/ThemeDomSync.tsx +17 -0
  115. package/src/components/ui/ThemeManager.tsx +18 -0
  116. package/src/components/ui/ThemeSwitcher.tsx +46 -0
  117. package/src/components/ui/Timeline.tsx +60 -0
  118. package/src/components/ui/Toast.tsx +70 -0
  119. package/src/components/ui/Typography.tsx +129 -0
  120. package/src/components/ui/first-slice.test.tsx +98 -0
  121. package/src/components/ui/index.ts +151 -0
  122. package/src/components/ui/interaction-primitives.test.tsx +77 -0
  123. package/src/components/ui/kbd-icons.test.tsx +90 -0
  124. package/src/components/ui/product-patterns.test.tsx +77 -0
  125. package/src/styles/themes.css +528 -0
  126. package/tailwind-preset.ts +7 -0
@@ -0,0 +1,129 @@
1
+ import { IconAreaChart, IconArrowUp, IconCohort, IconSurveys } from "@/components/icons";
2
+ import { Avatar, ChatBubble, ComponentDocs, Panel, Stat, ShowcaseWrapper } from "@/components/ui";
3
+
4
+ export default function StatShowcase() {
5
+ const code = `import { Avatar, ChatBubble, Panel, Stat } from "the-old-ui";
6
+
7
+ <div className="space-y-10">
8
+ <div className="grid gap-4 md:grid-cols-3">
9
+ <Stat icon={...} label="Total volume" value="2.6M" tone="brand" footer={...} />
10
+ </div>
11
+
12
+ <Panel className="grid gap-6 lg:grid-cols-2">
13
+ <ChatBubble
14
+ variant="from"
15
+ avatar={<Avatar label="M" color="bg-ph-muted text-ph-mutedtext" icon={...} />}
16
+ name="Max"
17
+ time="Today · 09:14"
18
+ footer="Synced with session replay"
19
+ >
20
+ Could you summarise the spike on /billing?
21
+ </ChatBubble>
22
+
23
+ <ChatBubble
24
+ variant="self"
25
+ avatar={<Avatar label="AI" color="bg-ph-purple text-white" />}
26
+ name="PostHog AI"
27
+ time="09:15"
28
+ footer="Tokens billed per org policy"
29
+ >
30
+ Checkout completion jumped 41%...
31
+ </ChatBubble>
32
+ </Panel>
33
+ </div>`;
34
+
35
+ return (
36
+ <ShowcaseWrapper
37
+ title="Stats & conversational UI"
38
+ description="Stat cards and chat bubble components for dashboards and AI interfaces."
39
+ code={code}
40
+ filename="StatExample.tsx"
41
+ docs={
42
+ <ComponentDocs
43
+ rows={[
44
+ { name: "Stat.icon", type: "ReactNode", description: "Leading icon." },
45
+ { name: "Stat.label", type: "ReactNode", description: "Metric label." },
46
+ { name: "Stat.value", type: "ReactNode", description: "Metric value." },
47
+ { name: "Stat.footer", type: "ReactNode", description: "Supporting text." },
48
+ { name: "Stat.tone", type: "default | brand | blue | purple | warning", defaultValue: "default", description: "Colour treatment." },
49
+ { name: "ChatBubble.variant", type: "from | self", defaultValue: "from", description: "Bubble alignment and colour." },
50
+ { name: "ChatBubble.avatar", type: "ReactNode", description: "Avatar element." },
51
+ { name: "ChatBubble.name", type: "string", description: "Sender name." },
52
+ { name: "ChatBubble.time", type: "string", description: "Timestamp." },
53
+ ]}
54
+ />
55
+ }
56
+ >
57
+ <div className="space-y-10">
58
+ <div className="grid gap-4 md:grid-cols-3">
59
+ <Stat icon={<IconArrowUp className="h-7 w-7" />} label="Total volume" value="2.6M" tone="brand" footer={<><span className="font-semibold text-emerald-700">up 21%</span> vs previous window</>} />
60
+ <Stat icon={<IconCohort className="h-7 w-7" />} label="Identified persons" value="89.4K" tone="blue" footer={<>Stable cohort churn <span className="tabular-nums text-ph-mutedtext">1.8%</span></>} />
61
+ <Stat icon={<IconAreaChart className="h-7 w-7" />} label="Activation" value="12.6%" tone="purple" footer={<>Powered by experimentation <strong className="text-ph-mutedtext">#flags</strong></>} />
62
+ </div>
63
+
64
+ <div className="grid gap-4 md:grid-cols-2">
65
+ <Stat label="Pageviews" value="1.2M" footer={<><span className="font-semibold text-emerald-700">up 15%</span> vs trailing 7-day</>} />
66
+ <Stat label="Experiment guardrail" value="42%" tone="warning" footer={<><span className="font-semibold text-ph-danger">down 3%</span> watch conversion</>} />
67
+ </div>
68
+
69
+ <Panel className="grid gap-6 lg:grid-cols-2">
70
+ <div>
71
+ <h3 className="mb-4 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Co-pilot replies</h3>
72
+ <div className="space-y-5">
73
+ <ChatBubble
74
+ variant="from"
75
+ avatar={<Avatar
76
+ size="sm"
77
+ icon={<IconSurveys className="h-5 w-5 text-ph-mutedtext" />}
78
+ color="bg-ph-muted text-ph-mutedtext"
79
+ />}
80
+ name="Max"
81
+ time="Today · 09:14"
82
+ footer="Synced with session replay 19ab…"
83
+ >
84
+ Could you summarise the spike on /billing?
85
+ </ChatBubble>
86
+ <ChatBubble
87
+ variant="self"
88
+ avatar={<Avatar
89
+ size="sm"
90
+ label="AI"
91
+ color="bg-ph-purple text-white"
92
+ />}
93
+ name="PostHog AI"
94
+ time="09:15"
95
+ footer="Tokens billed per org policy"
96
+ >
97
+ Checkout completion jumped 41% once you shipped the HogProvider race fix yesterday.
98
+ </ChatBubble>
99
+ </div>
100
+ </div>
101
+
102
+ <div>
103
+ <h3 className="mb-4 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">FAQ accordion</h3>
104
+ <div className="space-y-2">
105
+ <details className="ph-details">
106
+ <summary>What qualifies as an "active user"?</summary>
107
+ <div className="ph-details-inner leading-relaxed">
108
+ Any person emitting at least one non-$feature_flag_called event inside the timezone-aware window configured on the insight.
109
+ </div>
110
+ </details>
111
+ <details className="ph-details">
112
+ <summary>How do I ship safely with feature flags?</summary>
113
+ <div className="ph-details-inner leading-relaxed">
114
+ Pair multivariate payloads with enrichment properties, ramp traffic slowly, then wire the HogQL guardrail dashboards your team trusts.
115
+ </div>
116
+ </details>
117
+ <details className="ph-details">
118
+ <summary>Can I correlate LLM traces with revenue?</summary>
119
+ <div className="ph-details-inner leading-relaxed">
120
+ Yes — warehouse joins from <code className="ph-kbd">$ai_generation</code> to Shopify/Snowflake exports are the canonical path PostHog's team dogfoods.
121
+ </div>
122
+ </details>
123
+ </div>
124
+ </div>
125
+ </Panel>
126
+ </div>
127
+ </ShowcaseWrapper>
128
+ );
129
+ }
@@ -0,0 +1,37 @@
1
+ import { ComponentDocs, ShowcaseWrapper, Stepper } from "@/components/ui";
2
+
3
+ export default function StepperShowcase() {
4
+ const steps = [
5
+ { id: "1", label: "Account", description: "Login details" },
6
+ { id: "2", label: "Profile", description: "Personal info" },
7
+ { id: "3", label: "Settings", description: "Preferences" },
8
+ { id: "4", label: "Review", description: "Confirm" },
9
+ ];
10
+
11
+ const code = `import { Stepper } from "the-old-ui";
12
+
13
+ <div className="ph-panel">
14
+ <Stepper steps={steps} currentStep={1} />
15
+ </div>`;
16
+
17
+ return (
18
+ <ShowcaseWrapper
19
+ title="Stepper"
20
+ description="Step progress indicator for setup, onboarding, and multi-stage forms."
21
+ code={code}
22
+ filename="StepperExample.tsx"
23
+ docs={
24
+ <ComponentDocs
25
+ rows={[
26
+ { name: "steps", type: "{ id; label; description? }[]", description: "Steps shown in order." },
27
+ { name: "currentStep", type: "number", description: "Zero-based active step index." },
28
+ ]}
29
+ />
30
+ }
31
+ >
32
+ <div className="ph-panel">
33
+ <Stepper steps={steps} currentStep={1} />
34
+ </div>
35
+ </ShowcaseWrapper>
36
+ );
37
+ }
@@ -0,0 +1,99 @@
1
+ "use client";
2
+
3
+ import { IconBell, IconBellOff, IconMoon, IconSun, IconVolume, IconVolumeOff } from "@/components/icons";
4
+ import { cn } from "@/lib/cn";
5
+ import { ShowcaseWrapper } from "@/components/ui";
6
+
7
+ function IconSwapToggle({
8
+ offIcon: OffIcon,
9
+ onIcon: OnIcon,
10
+ offClassName,
11
+ onClassName,
12
+ label,
13
+ }: {
14
+ offIcon: typeof IconBellOff;
15
+ onIcon: typeof IconBell;
16
+ offClassName?: string;
17
+ onClassName?: string;
18
+ label: string;
19
+ }) {
20
+ return (
21
+ <label className="group relative inline-flex h-14 w-14 cursor-pointer items-center justify-center rounded-full bg-ph-surface shadow-ph focus-within:ring-2 focus-within:ring-ph-focus focus-within:ring-offset-2">
22
+ <input type="checkbox" className="sr-only" aria-label={label} />
23
+ <span className="ph-icon-swap h-7 w-7" aria-hidden>
24
+ <OffIcon
25
+ className={cn(
26
+ "opacity-100 transition group-has-[:checked]:pointer-events-none group-has-[:checked]:opacity-0",
27
+ offClassName,
28
+ )}
29
+ />
30
+ <OnIcon
31
+ className={cn(
32
+ "opacity-0 transition group-has-[:checked]:opacity-100",
33
+ onClassName,
34
+ )}
35
+ />
36
+ </span>
37
+ </label>
38
+ );
39
+ }
40
+
41
+ export default function SwapShowcase() {
42
+ const code = `import { IconBell, IconBellOff, IconMoon, IconSun, IconVolume, IconVolumeOff } from "@xenide-io/the-old-ui-theme";
43
+
44
+ <div className="flex flex-wrap gap-10 rounded-xl border border-ph-border bg-ph-muted p-8">
45
+ <IconSwapToggle
46
+ label="Mute audio"
47
+ offIcon={IconVolume}
48
+ onIcon={IconVolumeOff}
49
+ offClassName="text-ph-brand"
50
+ onClassName="text-ph-mutedtext"
51
+ />
52
+
53
+ <IconSwapToggle
54
+ label="Mute notifications"
55
+ offIcon={IconBell}
56
+ onIcon={IconBellOff}
57
+ offClassName="rotate-[-8deg] text-ph-mutedtext"
58
+ onClassName="text-ph-mutedtext"
59
+ />
60
+
61
+ <IconSwapToggle
62
+ label="Use light appearance"
63
+ offIcon={IconMoon}
64
+ onIcon={IconSun}
65
+ offClassName="text-ph-mutedtext"
66
+ onClassName="text-ph-warning"
67
+ />
68
+ </div>`;
69
+
70
+ return (
71
+ <ShowcaseWrapper title="Icon swap" code={code} filename="SwapExample.tsx">
72
+ <div className="flex flex-wrap gap-10 rounded-xl border border-ph-border bg-ph-muted p-8">
73
+ <IconSwapToggle
74
+ label="Mute audio"
75
+ offIcon={IconVolume}
76
+ onIcon={IconVolumeOff}
77
+ offClassName="text-ph-brand"
78
+ onClassName="text-ph-mutedtext"
79
+ />
80
+
81
+ <IconSwapToggle
82
+ label="Mute notifications"
83
+ offIcon={IconBell}
84
+ onIcon={IconBellOff}
85
+ offClassName="rotate-[-8deg] text-ph-mutedtext"
86
+ onClassName="text-ph-mutedtext"
87
+ />
88
+
89
+ <IconSwapToggle
90
+ label="Use light appearance"
91
+ offIcon={IconMoon}
92
+ onIcon={IconSun}
93
+ offClassName="text-ph-mutedtext"
94
+ onClassName="text-ph-warning"
95
+ />
96
+ </div>
97
+ </ShowcaseWrapper>
98
+ );
99
+ }
@@ -0,0 +1,84 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { ComponentDocs, ShowcaseWrapper, Tabs } from "@/components/ui";
5
+
6
+ const pillItems = [
7
+ { value: "overview", label: "Overview" },
8
+ { value: "activity", label: "Activity" },
9
+ { value: "settings", label: "Settings" },
10
+ ];
11
+
12
+ const insightItems = [
13
+ { value: "funnels", label: "Funnels" },
14
+ { value: "trends", label: "Trends" },
15
+ { value: "retention", label: "Retention" },
16
+ ];
17
+
18
+ const insightCopy: Record<string, string> = {
19
+ funnels: "Step-through conversion diagnostics for each stage of your onboarding.",
20
+ trends: "Arithmetic, formulas, breakdowns — the default insight landing state.",
21
+ retention: "Cohort resurfacing curves with optional granularity controls.",
22
+ };
23
+
24
+ export default function TabShowcase() {
25
+ const [pill, setPill] = useState("overview");
26
+ const [insight, setInsight] = useState("funnels");
27
+
28
+ const code = `import { Tabs } from "the-old-ui";
29
+
30
+ <div className="grid gap-8 lg:grid-cols-2">
31
+ <div className="ph-panel">
32
+ <h3 className="mb-4 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Floating pill track</h3>
33
+ <Tabs items={pillItems} value={pill} onChange={setPill} />
34
+ <p className="mt-6 text-sm text-ph-subtle">
35
+ Mirrors the softly inset control groups from the product chrome — muted rail + white thumb.
36
+ </p>
37
+ </div>
38
+
39
+ <div className="ph-panel space-y-4">
40
+ <h3 className="text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Underline tabs</h3>
41
+ <Tabs items={insightItems} value={insight} onChange={setInsight} variant="underline" />
42
+ <div className="rounded-lg border border-ph-border bg-ph-surface p-5 shadow-ph">
43
+ <p className="text-sm leading-relaxed text-ph-subtle">{insightCopy[insight]}</p>
44
+ </div>
45
+ </div>
46
+ </div>`;
47
+
48
+ return (
49
+ <ShowcaseWrapper
50
+ title="Tabs"
51
+ description="Controlled tab primitive with pill and underline variants."
52
+ code={code}
53
+ filename="TabsExample.tsx"
54
+ docs={
55
+ <ComponentDocs
56
+ rows={[
57
+ { name: "items", type: "{ value; label }[]", description: "Tab labels and values." },
58
+ { name: "value", type: "string", description: "Current tab value." },
59
+ { name: "onChange", type: "(value: string) => void", description: "Called when a tab is selected." },
60
+ { name: "variant", type: "pill | underline", defaultValue: "pill", description: "Visual tab style." },
61
+ ]}
62
+ />
63
+ }
64
+ >
65
+ <div className="grid gap-8 lg:grid-cols-2">
66
+ <div className="ph-panel">
67
+ <h3 className="mb-4 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Floating pill track</h3>
68
+ <Tabs items={pillItems} value={pill} onChange={setPill} />
69
+ <p className="mt-6 text-sm text-ph-subtle">
70
+ Mirrors the softly inset control groups from the product chrome — muted rail + white thumb.
71
+ </p>
72
+ </div>
73
+
74
+ <div className="ph-panel space-y-4">
75
+ <h3 className="text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Underline tabs</h3>
76
+ <Tabs items={insightItems} value={insight} onChange={setInsight} variant="underline" />
77
+ <div className="rounded-lg border border-ph-border bg-ph-surface p-5 shadow-ph">
78
+ <p className="text-sm leading-relaxed text-ph-subtle">{insightCopy[insight]}</p>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </ShowcaseWrapper>
83
+ );
84
+ }
@@ -0,0 +1,140 @@
1
+ import type { CSSProperties } from "react";
2
+ import { Badge, ComponentDocs, ShowcaseWrapper, Table, type TableColumn } from "@/components/ui";
3
+
4
+ const data = [
5
+ { id: 1, name: "Alice Johnson", role: "Developer", status: "Active", plan: "Pro" },
6
+ { id: 2, name: "Bob Smith", role: "Designer", status: "Offline", plan: "Basic" },
7
+ { id: 3, name: "Charlie Brown", role: "Manager", status: "Active", plan: "Enterprise" },
8
+ { id: 4, name: "Diana Prince", role: "Developer", status: "Busy", plan: "Pro" },
9
+ { id: 5, name: "Eve Davis", role: "QA", status: "Active", plan: "Basic" },
10
+ ];
11
+
12
+ const ribbonByPlan: Record<string, string> = {
13
+ Pro: "var(--ph-data-1)",
14
+ Basic: "var(--ph-data-4)",
15
+ Enterprise: "var(--ph-data-6)",
16
+ };
17
+
18
+ type UserRow = (typeof data)[number];
19
+
20
+ const statusVariant: Record<string, "success" | "neutral" | "warning"> = {
21
+ Active: "success",
22
+ Offline: "neutral",
23
+ Busy: "warning",
24
+ };
25
+
26
+ const columns: TableColumn<UserRow>[] = [
27
+ { key: "id", header: "#", cell: (row) => <span className="tabular-nums text-ph-mutedtext">{row.id}</span>, className: "w-12 text-center" },
28
+ { key: "name", header: "Name", cell: (row) => <span className="font-medium text-ph-ink">{row.name}</span> },
29
+ { key: "role", header: "Role", cell: (row) => row.role },
30
+ { key: "status", header: "Status", cell: (row) => <Badge variant={statusVariant[row.status] ?? "outline"}>{row.status}</Badge> },
31
+ { key: "plan", header: "Plan", cell: (row) => <span className="text-ph-subtle">{row.plan}</span> },
32
+ ];
33
+
34
+ const keyValueData = [
35
+ { key: "distinct_id", value: "019b2eef-..." },
36
+ { key: "capture_api_version", value: "2024-06-06" },
37
+ ];
38
+
39
+ const keyValueColumns: TableColumn<(typeof keyValueData)[number]>[] = [
40
+ { key: "key", header: "Key", cell: (row) => <span className="font-mono text-xs">{row.key}</span> },
41
+ { key: "value", header: "Value", cell: (row) => <span className="font-mono text-xs">{row.value}</span> },
42
+ ];
43
+
44
+ export default function TableShowcase() {
45
+ const code = `import { Badge, Table, type TableColumn } from "the-old-ui";
46
+
47
+ <div className="space-y-8">
48
+ <div>
49
+ <h3 className="mb-3 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Dashboard table</h3>
50
+ <p className="mb-4 max-w-2xl text-sm text-ph-subtle">
51
+ White canvas, tinted header strip, zebra optional — avoids DaisyUI&apos;s chunky row chrome.
52
+ </p>
53
+ <Table data={data} columns={columns} caption="Dashboard users" />
54
+ </div>
55
+
56
+ <div>
57
+ <h3 className="mb-3 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Ribbon column</h3>
58
+ <p className="mb-4 max-w-2xl text-sm text-ph-subtle">
59
+ Per-row accents via inherited <code className="ph-kbd">--row-ribbon</code> mimic Lemon insight list markers (
60
+ <code className="ph-kbd">.ph-table--ribbon</code>).
61
+ </p>
62
+ <Table
63
+ data={data}
64
+ columns={columns}
65
+ ribbon
66
+ getRowStyle={(row) => ({
67
+ "--row-ribbon": ribbonByPlan[row.plan] ?? "var(--ph-data-5)",
68
+ } as CSSProperties)}
69
+ />
70
+ </div>
71
+
72
+ <div>
73
+ <h3 className="mb-3 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Zebra rows</h3>
74
+ <Table data={data} columns={columns.slice(0, 4)} zebra />
75
+ </div>
76
+
77
+ <div>
78
+ <h3 className="mb-3 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Compact density</h3>
79
+ <Table data={keyValueData} columns={keyValueColumns} compact className="max-w-xl" />
80
+ </div>
81
+ </div>`;
82
+
83
+ return (
84
+ <ShowcaseWrapper
85
+ title="Tables"
86
+ description="Data table wrapper with typed columns, zebra rows, compact density, and component-based cell rendering."
87
+ code={code}
88
+ filename="TableExample.tsx"
89
+ docs={
90
+ <ComponentDocs
91
+ rows={[
92
+ { name: "data", type: "T[]", description: "Rows passed to each column cell renderer." },
93
+ { name: "columns", type: "TableColumn<T>[]", description: "Column definitions with header and cell render function." },
94
+ { name: "zebra", type: "boolean", defaultValue: "false", description: "Adds alternating row backgrounds." },
95
+ { name: "compact", type: "boolean", defaultValue: "false", description: "Reduces density for admin tables." },
96
+ { name: "ribbon", type: "boolean", defaultValue: "false", description: "Adds per-row accent ribbon support." },
97
+ { name: "getRowStyle", type: "(row: T) => CSSProperties", description: "Optional row-level CSS variables or inline style." },
98
+ { name: "caption", type: "string", description: "Accessible table caption rendered visually hidden." },
99
+ ]}
100
+ />
101
+ }
102
+ >
103
+ <div className="space-y-8">
104
+ <div>
105
+ <h3 className="mb-3 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Dashboard table</h3>
106
+ <p className="mb-4 max-w-2xl text-sm text-ph-subtle">
107
+ White canvas, tinted header strip, zebra optional — avoids DaisyUI&apos;s chunky row chrome.
108
+ </p>
109
+ <Table data={data} columns={columns} caption="Dashboard users" />
110
+ </div>
111
+
112
+ <div>
113
+ <h3 className="mb-3 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Ribbon column</h3>
114
+ <p className="mb-4 max-w-2xl text-sm text-ph-subtle">
115
+ Per-row accents via inherited <code className="ph-kbd">--row-ribbon</code> mimic Lemon insight list markers (
116
+ <code className="ph-kbd">.ph-table--ribbon</code>).
117
+ </p>
118
+ <Table
119
+ data={data}
120
+ columns={columns}
121
+ ribbon
122
+ getRowStyle={(row) => ({
123
+ "--row-ribbon": ribbonByPlan[row.plan] ?? "var(--ph-data-5)",
124
+ } as CSSProperties)}
125
+ />
126
+ </div>
127
+
128
+ <div>
129
+ <h3 className="mb-3 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Zebra rows</h3>
130
+ <Table data={data} columns={columns.slice(0, 4)} zebra />
131
+ </div>
132
+
133
+ <div>
134
+ <h3 className="mb-3 text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Compact density</h3>
135
+ <Table data={keyValueData} columns={keyValueColumns} compact className="max-w-xl" />
136
+ </div>
137
+ </div>
138
+ </ShowcaseWrapper>
139
+ );
140
+ }
@@ -0,0 +1,83 @@
1
+ import { IconAreaChart, IconBolt, IconGift, IconRocket } from "@/components/icons";
2
+ import { ComponentDocs, ShowcaseWrapper, Timeline } from "@/components/ui";
3
+
4
+ const events = [
5
+ {
6
+ id: "1",
7
+ icon: <IconRocket className="h-6 w-6" />,
8
+ color: "bg-ph-brand text-white",
9
+ title: "Project scaffolded",
10
+ desc: "Next.js ingestion keys issued for prod + staging.",
11
+ time: "2h ago",
12
+ },
13
+ {
14
+ id: "2",
15
+ icon: <IconBolt className="h-6 w-6" />,
16
+ color: "bg-ph-purple text-white",
17
+ title: "$pageview spikes",
18
+ desc: "CDN edge experiment rolled to 42% cohort.",
19
+ time: "1h ago",
20
+ },
21
+ {
22
+ id: "3",
23
+ icon: <IconAreaChart className="h-6 w-6" />,
24
+ color: "bg-ph-blue text-white",
25
+ title: "Dashboard shared",
26
+ desc: "Growth pack pinned to Slack #metrics.",
27
+ time: "45m ago",
28
+ },
29
+ {
30
+ id: "4",
31
+ icon: <IconGift className="h-6 w-6" />,
32
+ color: "bg-emerald-600 text-white",
33
+ title: "Feature flag LIVE",
34
+ desc: "pricing-test-v3 now default for EU traffic.",
35
+ time: "Just now",
36
+ },
37
+ ];
38
+
39
+ export default function TimelineShowcase() {
40
+ const code = `import { Timeline } from "the-old-ui";
41
+
42
+ <Timeline
43
+ events={[
44
+ {
45
+ id: "1",
46
+ title: "Project scaffolded",
47
+ description: "Keys issued for prod + staging.",
48
+ time: "2h ago",
49
+ icon: <IconRocket />,
50
+ color: "bg-ph-brand text-white",
51
+ },
52
+ ]}
53
+ alternate
54
+ />`;
55
+
56
+ return (
57
+ <ShowcaseWrapper
58
+ title="Timeline"
59
+ description="Event timeline with icons, descriptions, and alternating layout."
60
+ code={code}
61
+ filename="TimelineExample.tsx"
62
+ docs={
63
+ <ComponentDocs
64
+ rows={[
65
+ { name: "events", type: "TimelineEvent[]", description: "Timeline events to render." },
66
+ { name: "alternate", type: "boolean", defaultValue: "false", description: "Alternate left/right on large screens." },
67
+ { name: "event.id", type: "string", description: "Unique key." },
68
+ { name: "event.title", type: "string", description: "Event title." },
69
+ { name: "event.description", type: "string", description: "Event description." },
70
+ { name: "event.time", type: "string", description: "Timestamp." },
71
+ { name: "event.icon", type: "ReactNode", description: "Icon element." },
72
+ { name: "event.color", type: "string", description: "Tailwind colour classes." },
73
+ ]}
74
+ />
75
+ }
76
+ >
77
+ <div className="relative ph-panel overflow-hidden">
78
+ <div className="absolute bottom-8 left-[3.125rem] top-8 w-px bg-ph-border lg:left-[3.5rem]" aria-hidden />
79
+ <Timeline events={events} alternate />
80
+ </div>
81
+ </ShowcaseWrapper>
82
+ );
83
+ }