@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.
- package/README.md +1 -1
- package/dist/chunk-FDMD3IIN.mjs +3831 -0
- package/dist/index-D1RTT2Ap.d.mts +1044 -0
- package/dist/index-D1RTT2Ap.d.ts +1044 -0
- package/dist/index.d.mts +11 -758
- package/dist/index.d.ts +11 -758
- package/dist/index.js +1749 -768
- package/dist/index.mjs +312 -2846
- package/dist/tailwind-preset.js +7 -0
- package/dist/tailwind-preset.mjs +7 -0
- package/dist/ui.d.mts +4 -0
- package/dist/ui.d.ts +4 -0
- package/dist/ui.js +3864 -0
- package/dist/ui.mjs +169 -0
- package/package.json +28 -6
- package/src/app/globals.css +1005 -227
- package/src/components/charts/index.ts +8 -0
- package/src/components/charts/ph-insight-charts.tsx +162 -0
- package/src/components/dashboard/DashboardFiltersBar.tsx +19 -0
- package/src/components/dashboard/DashboardGrid.tsx +23 -0
- package/src/components/dashboard/DashboardInsightPlaceholder.tsx +37 -0
- package/src/components/dashboard/DashboardKpiCard.tsx +25 -0
- package/src/components/dashboard/DashboardToolbar.tsx +23 -0
- package/src/components/dashboard/DashboardWell.tsx +10 -0
- package/src/components/dashboard/InsightMiniCard.tsx +26 -0
- package/src/components/dashboard/InsightShell.tsx +37 -0
- package/src/components/dashboard/InsightShellHeader.tsx +22 -0
- package/src/components/dashboard/MiniTrendBars.tsx +51 -0
- package/src/components/dashboard/index.ts +31 -0
- package/src/components/dashboard/types.ts +9 -0
- package/src/components/icons/IconBase.tsx +47 -0
- package/src/components/icons/createIconBase.tsx +32 -0
- package/src/components/icons/icons.tsx +557 -0
- package/src/components/icons/index.ts +3 -0
- package/src/components/sections/AccordionShowcase.tsx +45 -0
- package/src/components/sections/AlertShowcase.tsx +40 -0
- package/src/components/sections/AvatarShowcase.tsx +80 -0
- package/src/components/sections/BadgeShowcase.tsx +65 -0
- package/src/components/sections/ButtonShowcase.tsx +308 -0
- package/src/components/sections/CalendarShowcase.tsx +35 -0
- package/src/components/sections/CardShowcase.tsx +159 -0
- package/src/components/sections/ChartShowcase.tsx +190 -0
- package/src/components/sections/CodeMockupShowcase.tsx +56 -0
- package/src/components/sections/ColorPalette.tsx +117 -0
- package/src/components/sections/CommandPaletteShowcase.tsx +48 -0
- package/src/components/sections/CountdownShowcase.tsx +43 -0
- package/src/components/sections/DashboardShowcase.tsx +191 -0
- package/src/components/sections/DiffShowcase.tsx +70 -0
- package/src/components/sections/DrawerShowcase.tsx +97 -0
- package/src/components/sections/DropdownShowcase.tsx +98 -0
- package/src/components/sections/EmptyStateShowcase.tsx +50 -0
- package/src/components/sections/FilterChipsShowcase.tsx +98 -0
- package/src/components/sections/FormShowcase.tsx +127 -0
- package/src/components/sections/HoverCardShowcase.tsx +50 -0
- package/src/components/sections/IconShowcase.tsx +127 -0
- package/src/components/sections/IndicatorShowcase.tsx +52 -0
- package/src/components/sections/KbdShowcase.tsx +57 -0
- package/src/components/sections/ModalShowcase.tsx +211 -0
- package/src/components/sections/NavigationShowcase.tsx +199 -0
- package/src/components/sections/ProductLayoutsShowcase.tsx +138 -0
- package/src/components/sections/ProgressShowcase.tsx +82 -0
- package/src/components/sections/SegmentedControlShowcase.tsx +49 -0
- package/src/components/sections/SetupThemeShowcase.tsx +262 -0
- package/src/components/sections/StackShowcase.tsx +33 -0
- package/src/components/sections/StatShowcase.tsx +129 -0
- package/src/components/sections/StepperShowcase.tsx +37 -0
- package/src/components/sections/SwapShowcase.tsx +99 -0
- package/src/components/sections/TabShowcase.tsx +84 -0
- package/src/components/sections/TableShowcase.tsx +140 -0
- package/src/components/sections/TimelineShowcase.tsx +83 -0
- package/src/components/sections/ToastShowcase.tsx +108 -0
- package/src/components/sections/TooltipShowcase.tsx +44 -0
- package/src/components/sections/UtilityShowcase.tsx +81 -0
- package/src/components/ui/Accordion.tsx +77 -0
- package/src/components/ui/Alert.tsx +77 -0
- package/src/components/ui/AuthLayout.tsx +58 -0
- package/src/components/ui/Avatar.tsx +97 -0
- package/src/components/ui/Badge.tsx +60 -0
- package/src/components/ui/Button.tsx +120 -0
- package/src/components/ui/ButtonChrome.tsx +20 -0
- package/src/components/ui/Calendar.tsx +115 -0
- package/src/components/ui/Card.tsx +70 -0
- package/src/components/ui/ChatBubble.tsx +71 -0
- package/src/components/ui/CodeBlock.tsx +48 -0
- package/src/components/ui/CollapsibleSection.tsx +49 -0
- package/src/components/ui/CommandPalette.tsx +137 -0
- package/src/components/ui/ComponentDocs.tsx +52 -0
- package/src/components/ui/Drawer.tsx +71 -0
- package/src/components/ui/DropdownMenu.tsx +262 -0
- package/src/components/ui/EmptyState.tsx +33 -0
- package/src/components/ui/FilterBar.tsx +165 -0
- package/src/components/ui/FilterChips.tsx +80 -0
- package/src/components/ui/FilterControls.tsx +30 -0
- package/src/components/ui/FormField.tsx +69 -0
- package/src/components/ui/HoverCard.tsx +36 -0
- package/src/components/ui/Indicator.tsx +61 -0
- package/src/components/ui/Input.tsx +385 -0
- package/src/components/ui/Kbd.tsx +113 -0
- package/src/components/ui/Loader.tsx +96 -0
- package/src/components/ui/Modal.tsx +105 -0
- package/src/components/ui/Navigation.tsx +94 -0
- package/src/components/ui/Panel.tsx +18 -0
- package/src/components/ui/Progress.tsx +59 -0
- package/src/components/ui/Rating.tsx +65 -0
- package/src/components/ui/SearchInput.tsx +106 -0
- package/src/components/ui/SegmentedControl.tsx +44 -0
- package/src/components/ui/SettingsLayout.tsx +94 -0
- package/src/components/ui/ShowcaseWrapper.tsx +124 -0
- package/src/components/ui/Stat.tsx +39 -0
- package/src/components/ui/Stepper.tsx +74 -0
- package/src/components/ui/Table.tsx +55 -0
- package/src/components/ui/Tabs.tsx +53 -0
- package/src/components/ui/Terminal.tsx +54 -0
- package/src/components/ui/ThemeDomSync.tsx +17 -0
- package/src/components/ui/ThemeManager.tsx +18 -0
- package/src/components/ui/ThemeSwitcher.tsx +46 -0
- package/src/components/ui/Timeline.tsx +60 -0
- package/src/components/ui/Toast.tsx +70 -0
- package/src/components/ui/Typography.tsx +129 -0
- package/src/components/ui/first-slice.test.tsx +98 -0
- package/src/components/ui/index.ts +151 -0
- package/src/components/ui/interaction-primitives.test.tsx +77 -0
- package/src/components/ui/kbd-icons.test.tsx +90 -0
- package/src/components/ui/product-patterns.test.tsx +77 -0
- package/src/styles/themes.css +528 -0
- 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'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'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
|
+
}
|