@xenide-io/the-old-ui-theme 0.3.0 → 0.3.2
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/chunk-5HSOBJ4F.mjs +5968 -0
- package/dist/{index-D1RTT2Ap.d.mts → index-BgG8Homu.d.mts} +419 -2
- package/dist/{index-D1RTT2Ap.d.ts → index-BgG8Homu.d.ts} +419 -2
- package/dist/index.d.mts +198 -95
- package/dist/index.d.ts +198 -95
- package/dist/index.js +2995 -1136
- package/dist/index.mjs +629 -735
- package/dist/ui.d.mts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +1986 -156
- package/dist/ui.mjs +76 -3
- package/package.json +4 -14
- package/src/app/globals.css +976 -2
- package/src/components/charts/index.ts +9 -8
- package/src/components/charts/quill/BarChart.tsx +85 -0
- package/src/components/charts/quill/FunnelChart.tsx +49 -0
- package/src/components/charts/quill/InsightShell.tsx +27 -0
- package/src/components/charts/quill/MetricCard.tsx +44 -0
- package/src/components/charts/quill/PieChart.tsx +81 -0
- package/src/components/charts/quill/Sparkline.tsx +57 -0
- package/src/components/charts/quill/TimeSeriesLineChart.tsx +62 -0
- package/src/components/charts/quill/index.ts +9 -0
- package/src/components/icons/icons.tsx +2 -0
- package/src/components/icons/index.ts +3 -0
- package/src/components/icons/lemon-brand-icons.tsx +16 -0
- package/src/components/icons/lemon-category-icons.tsx +72 -0
- package/src/components/icons/lemon-icons.tsx +57 -0
- package/src/components/sections/IconShowcase.tsx +110 -7
- package/src/components/sections/NewComponentsShowcase.tsx +1052 -0
- package/src/components/sections/ProductLayoutsShowcase.tsx +57 -117
- package/src/components/sections/QuillChartShowcase.tsx +92 -0
- package/src/components/sections/QuillDashboardShowcase.tsx +149 -0
- package/src/components/sections/SidebarShowcase.tsx +152 -0
- package/src/components/sections/TableShowcase.tsx +2 -0
- package/src/components/sections/TooltipShowcase.tsx +19 -25
- package/src/components/ui/AlertDialog.tsx +62 -0
- package/src/components/ui/AppLayout.tsx +27 -0
- package/src/components/ui/Autocomplete.tsx +106 -0
- package/src/components/ui/Banner.tsx +63 -0
- package/src/components/ui/ButtonGroup.tsx +16 -0
- package/src/components/ui/Chip.tsx +46 -0
- package/src/components/ui/Collapsible.tsx +46 -0
- package/src/components/ui/Combobox.tsx +119 -0
- package/src/components/ui/ContextMenu.tsx +81 -0
- package/src/components/ui/DataTable.tsx +133 -0
- package/src/components/ui/DatePicker.tsx +96 -0
- package/src/components/ui/Dialog.tsx +75 -0
- package/src/components/ui/Divider.tsx +39 -0
- package/src/components/ui/Dot.tsx +32 -0
- package/src/components/ui/InputGroup.tsx +16 -0
- package/src/components/ui/Lettermark.tsx +47 -0
- package/src/components/ui/Link.tsx +32 -0
- package/src/components/ui/LoadingBar.tsx +25 -0
- package/src/components/ui/Menubar.tsx +66 -0
- package/src/components/ui/Metric.tsx +34 -0
- package/src/components/ui/NumberField.tsx +76 -0
- package/src/components/ui/Popover.tsx +73 -0
- package/src/components/ui/ProgressCircle.tsx +63 -0
- package/src/components/ui/Resizable.tsx +71 -0
- package/src/components/ui/Row.tsx +51 -0
- package/src/components/ui/ScrollArea.tsx +54 -0
- package/src/components/ui/Sidebar.tsx +77 -0
- package/src/components/ui/Snack.tsx +48 -0
- package/src/components/ui/Spinner.tsx +30 -0
- package/src/components/ui/Splotch.tsx +33 -0
- package/src/components/ui/Table.tsx +2 -0
- package/src/components/ui/Tag.tsx +65 -0
- package/src/components/ui/Toggle.tsx +62 -0
- package/src/components/ui/Tooltip.test.tsx +58 -0
- package/src/components/ui/Tooltip.tsx +211 -0
- package/src/components/ui/Widget.tsx +31 -0
- package/src/components/ui/index.ts +275 -21
- package/dist/chunk-FDMD3IIN.mjs +0 -3831
- package/src/components/charts/ph-insight-charts.tsx +0 -162
- package/src/components/dashboard/DashboardFiltersBar.tsx +0 -19
- package/src/components/dashboard/DashboardGrid.tsx +0 -23
- package/src/components/dashboard/DashboardInsightPlaceholder.tsx +0 -37
- package/src/components/dashboard/DashboardKpiCard.tsx +0 -25
- package/src/components/dashboard/DashboardToolbar.tsx +0 -23
- package/src/components/dashboard/DashboardWell.tsx +0 -10
- package/src/components/dashboard/InsightMiniCard.tsx +0 -26
- package/src/components/dashboard/InsightShell.tsx +0 -37
- package/src/components/dashboard/InsightShellHeader.tsx +0 -22
- package/src/components/dashboard/MiniTrendBars.tsx +0 -51
- package/src/components/dashboard/index.ts +0 -31
- package/src/components/dashboard/types.ts +0 -9
- package/src/components/sections/ChartShowcase.tsx +0 -190
- package/src/components/sections/DashboardShowcase.tsx +0 -191
- package/src/components/sections/StatShowcase.tsx +0 -129
|
@@ -0,0 +1,1052 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import {
|
|
5
|
+
Banner, Divider, Tag, ProgressCircle, Row, Widget,
|
|
6
|
+
Lettermark, Splotch, Popover, LoadingBar, Link,
|
|
7
|
+
Spinner, Snack, Combobox, Autocomplete, ContextMenu,
|
|
8
|
+
Resizable, ScrollArea, NumberField, InputGroup, ButtonGroup,
|
|
9
|
+
ToggleButton, ToggleGroup, Menubar, AlertDialog, Dot,
|
|
10
|
+
Collapsible, Metric, Chip, DataTable, DatePicker,
|
|
11
|
+
ComponentDocs, ShowcaseWrapper,
|
|
12
|
+
Button, Card, Badge, Dialog, Input, Checkbox, Textarea, Select,
|
|
13
|
+
} from "@/components/ui";
|
|
14
|
+
import { Bell, Sliders, User, Search, Plus } from "lucide-react";
|
|
15
|
+
|
|
16
|
+
const bannerCode = `import { Banner } from "@xenide-io/the-old-ui-theme";
|
|
17
|
+
|
|
18
|
+
<Banner type="info">You are viewing read-only data.</Banner>
|
|
19
|
+
<Banner type="success" onClose={() => {}}>Export completed successfully.</Banner>
|
|
20
|
+
<Banner type="warning">Your trial ends in 3 days.</Banner>
|
|
21
|
+
<Banner type="danger">Pipeline disconnected — check credentials.</Banner>`;
|
|
22
|
+
|
|
23
|
+
const tagCode = `import { Tag } from "@xenide-io/the-old-ui-theme";
|
|
24
|
+
|
|
25
|
+
<Tag type="primary">Beta</Tag>
|
|
26
|
+
<Tag type="success" closable>Approved</Tag>
|
|
27
|
+
<Tag type="warning">Pending</Tag>
|
|
28
|
+
<Tag type="danger">Failed</Tag>`;
|
|
29
|
+
|
|
30
|
+
const dividerCode = `import { Divider } from "@xenide-io/the-old-ui-theme";
|
|
31
|
+
|
|
32
|
+
<Divider />
|
|
33
|
+
<Divider dashed />
|
|
34
|
+
<Divider label="OR" />
|
|
35
|
+
<Divider vertical />`;
|
|
36
|
+
|
|
37
|
+
const progressCircleCode = `import { ProgressCircle } from "@xenide-io/the-old-ui-theme";
|
|
38
|
+
|
|
39
|
+
<ProgressCircle progress={0.75} />
|
|
40
|
+
<ProgressCircle progress={0.5} size={60}>
|
|
41
|
+
<span>50%</span>
|
|
42
|
+
</ProgressCircle>`;
|
|
43
|
+
|
|
44
|
+
const rowCode = `import { Row } from "@xenide-io/the-old-ui-theme";
|
|
45
|
+
import { User, Sliders } from "lucide-react";
|
|
46
|
+
|
|
47
|
+
<Row icon={<User className="h-4 w-4" />}>User profile</Row>
|
|
48
|
+
<Row sideIcon={<Sliders className="h-4 w-4" />}>Settings</Row>`;
|
|
49
|
+
|
|
50
|
+
const widgetCode = `import { Widget } from "@xenide-io/the-old-ui-theme";
|
|
51
|
+
|
|
52
|
+
<Widget title="Active Users" onClose={() => {}}>
|
|
53
|
+
Content here
|
|
54
|
+
</Widget>`;
|
|
55
|
+
|
|
56
|
+
const lettermarkCode = `import { Lettermark } from "@xenide-io/the-old-ui-theme";
|
|
57
|
+
|
|
58
|
+
<Lettermark name="PostHog" />
|
|
59
|
+
<Lettermark name="42" />
|
|
60
|
+
<Lettermark name="A" rounded outlined />`;
|
|
61
|
+
|
|
62
|
+
const splotchCode = `import { Splotch } from "@xenide-io/the-old-ui-theme";
|
|
63
|
+
|
|
64
|
+
<Splotch color="purple" />
|
|
65
|
+
<Splotch color="blue" />
|
|
66
|
+
<Splotch color="green" />`;
|
|
67
|
+
|
|
68
|
+
const popoverCode = `import { Popover } from "@xenide-io/the-old-ui-theme";
|
|
69
|
+
|
|
70
|
+
<Popover trigger={<Button>Open</Button>}>
|
|
71
|
+
Popover content
|
|
72
|
+
</Popover>`;
|
|
73
|
+
|
|
74
|
+
const loadingBarCode = `import { LoadingBar } from "@xenide-io/the-old-ui-theme";
|
|
75
|
+
|
|
76
|
+
<LoadingBar active />`;
|
|
77
|
+
|
|
78
|
+
const linkCode = `import { Link } from "@xenide-io/the-old-ui-theme";
|
|
79
|
+
|
|
80
|
+
<Link href="/docs">Documentation</Link>`;
|
|
81
|
+
|
|
82
|
+
const spinnerCode = `import { Spinner } from "@xenide-io/the-old-ui-theme";
|
|
83
|
+
|
|
84
|
+
<Spinner />
|
|
85
|
+
<Spinner size="lg" />`;
|
|
86
|
+
|
|
87
|
+
const snackCode = `import { Snack } from "@xenide-io/the-old-ui-theme";
|
|
88
|
+
|
|
89
|
+
<Snack>app.posthog.com</Snack>
|
|
90
|
+
<Snack type="pill" onClose={() => {}}>Filter applied</Snack>`;
|
|
91
|
+
|
|
92
|
+
export default function NewComponentsShowcase() {
|
|
93
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
94
|
+
const [loading, setLoading] = useState(false);
|
|
95
|
+
|
|
96
|
+
const toggleLoading = () => {
|
|
97
|
+
setLoading(true);
|
|
98
|
+
setTimeout(() => setLoading(false), 2000);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<>
|
|
103
|
+
<ShowcaseWrapper
|
|
104
|
+
title="Banner"
|
|
105
|
+
description="Status banners for page-level alerts and notifications."
|
|
106
|
+
code={bannerCode}
|
|
107
|
+
filename="BannerExample.tsx"
|
|
108
|
+
docs={
|
|
109
|
+
<ComponentDocs
|
|
110
|
+
rows={[
|
|
111
|
+
{ name: "type", type: "'info' | 'success' | 'warning' | 'danger' | 'ai'", defaultValue: "info", description: "Controls the banner colour." },
|
|
112
|
+
{ name: "onClose", type: "() => void", description: "Shows a close button when provided." },
|
|
113
|
+
{ name: "action", type: "ReactNode", description: "Optional action element (e.g. a button)." },
|
|
114
|
+
{ name: "hideIcon", type: "boolean", defaultValue: "false", description: "Hides the type icon." },
|
|
115
|
+
{ name: "square", type: "boolean", defaultValue: "false", description: "Removes border-radius." },
|
|
116
|
+
]}
|
|
117
|
+
/>
|
|
118
|
+
}
|
|
119
|
+
>
|
|
120
|
+
<div className="space-y-3">
|
|
121
|
+
<Banner type="info">You are viewing read-only data from a previous export.</Banner>
|
|
122
|
+
<Banner type="success" onClose={() => {}}>Export completed successfully. Download available now.</Banner>
|
|
123
|
+
<Banner type="warning">Your trial period ends in 3 days. Add a billing method to continue.</Banner>
|
|
124
|
+
<Banner type="danger">Pipeline disconnected — check your source credentials.</Banner>
|
|
125
|
+
<Banner type="ai">PostHog AI has identified 3 optimization opportunities.</Banner>
|
|
126
|
+
</div>
|
|
127
|
+
</ShowcaseWrapper>
|
|
128
|
+
|
|
129
|
+
<ShowcaseWrapper
|
|
130
|
+
title="Tag"
|
|
131
|
+
description="Compact labels for status, categories, and metadata."
|
|
132
|
+
code={tagCode}
|
|
133
|
+
filename="TagExample.tsx"
|
|
134
|
+
docs={
|
|
135
|
+
<ComponentDocs
|
|
136
|
+
rows={[
|
|
137
|
+
{ name: "type", type: "'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'muted' | 'highlight'", defaultValue: "default", description: "Semantic colour." },
|
|
138
|
+
{ name: "size", type: "'sm' | 'md'", defaultValue: "md", description: "Tag size." },
|
|
139
|
+
{ name: "icon", type: "ReactNode", description: "Leading icon." },
|
|
140
|
+
{ name: "closable", type: "boolean", defaultValue: "false", description: "Shows a close button." },
|
|
141
|
+
{ name: "onClose", type: "() => void", description: "Close callback." },
|
|
142
|
+
]}
|
|
143
|
+
/>
|
|
144
|
+
}
|
|
145
|
+
>
|
|
146
|
+
<div className="flex flex-wrap gap-2">
|
|
147
|
+
<Tag type="default">Default</Tag>
|
|
148
|
+
<Tag type="primary">Beta</Tag>
|
|
149
|
+
<Tag type="success">Live</Tag>
|
|
150
|
+
<Tag type="warning">Pending</Tag>
|
|
151
|
+
<Tag type="danger">Failed</Tag>
|
|
152
|
+
<Tag type="info">Info</Tag>
|
|
153
|
+
<Tag type="muted">Archived</Tag>
|
|
154
|
+
<Tag type="highlight">New</Tag>
|
|
155
|
+
<Tag type="success" closable onClose={() => {}}>Approved</Tag>
|
|
156
|
+
<Tag type="primary" size="sm">Small</Tag>
|
|
157
|
+
</div>
|
|
158
|
+
</ShowcaseWrapper>
|
|
159
|
+
|
|
160
|
+
<ShowcaseWrapper
|
|
161
|
+
title="Divider"
|
|
162
|
+
description="Separates content sections horizontally or vertically."
|
|
163
|
+
code={dividerCode}
|
|
164
|
+
filename="DividerExample.tsx"
|
|
165
|
+
docs={
|
|
166
|
+
<ComponentDocs
|
|
167
|
+
rows={[
|
|
168
|
+
{ name: "vertical", type: "boolean", defaultValue: "false", description: "Render as a vertical divider." },
|
|
169
|
+
{ name: "dashed", type: "boolean", defaultValue: "false", description: "Dashed line style." },
|
|
170
|
+
{ name: "thick", type: "boolean", defaultValue: "false", description: "Thicker line." },
|
|
171
|
+
{ name: "label", type: "string", description: "Shows a label inside the divider." },
|
|
172
|
+
]}
|
|
173
|
+
/>
|
|
174
|
+
}
|
|
175
|
+
>
|
|
176
|
+
<div className="space-y-6">
|
|
177
|
+
<div>
|
|
178
|
+
<p className="text-sm text-ph-subtle">Content above</p>
|
|
179
|
+
<Divider />
|
|
180
|
+
<p className="text-sm text-ph-subtle">Content below</p>
|
|
181
|
+
</div>
|
|
182
|
+
<Divider dashed />
|
|
183
|
+
<Divider label="OR" />
|
|
184
|
+
<div className="flex h-20 items-center gap-4">
|
|
185
|
+
<span className="text-sm text-ph-subtle">Left</span>
|
|
186
|
+
<Divider vertical />
|
|
187
|
+
<span className="text-sm text-ph-subtle">Right</span>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
</ShowcaseWrapper>
|
|
191
|
+
|
|
192
|
+
<ShowcaseWrapper
|
|
193
|
+
title="Progress Circle"
|
|
194
|
+
description="Circular progress indicator with optional child content."
|
|
195
|
+
code={progressCircleCode}
|
|
196
|
+
filename="ProgressCircleExample.tsx"
|
|
197
|
+
docs={
|
|
198
|
+
<ComponentDocs
|
|
199
|
+
rows={[
|
|
200
|
+
{ name: "progress", type: "number", defaultValue: "0", description: "Progress value between 0 and 1." },
|
|
201
|
+
{ name: "size", type: "number", defaultValue: "40", description: "Diameter in pixels." },
|
|
202
|
+
{ name: "strokeWidth", type: "number", description: "Stroke width override." },
|
|
203
|
+
{ name: "children", type: "ReactNode", description: "Content rendered in the centre." },
|
|
204
|
+
]}
|
|
205
|
+
/>
|
|
206
|
+
}
|
|
207
|
+
>
|
|
208
|
+
<div className="flex flex-wrap items-end gap-8">
|
|
209
|
+
<div className="flex flex-col items-center gap-2">
|
|
210
|
+
<ProgressCircle progress={0.25} size={48} />
|
|
211
|
+
<span className="text-xs text-ph-mutedtext">25%</span>
|
|
212
|
+
</div>
|
|
213
|
+
<div className="flex flex-col items-center gap-2">
|
|
214
|
+
<ProgressCircle progress={0.5} size={48} />
|
|
215
|
+
<span className="text-xs text-ph-mutedtext">50%</span>
|
|
216
|
+
</div>
|
|
217
|
+
<div className="flex flex-col items-center gap-2">
|
|
218
|
+
<ProgressCircle progress={0.75} size={48} />
|
|
219
|
+
<span className="text-xs text-ph-mutedtext">75%</span>
|
|
220
|
+
</div>
|
|
221
|
+
<div className="flex flex-col items-center gap-2">
|
|
222
|
+
<ProgressCircle progress={1} size={48} />
|
|
223
|
+
<span className="text-xs text-ph-mutedtext">100%</span>
|
|
224
|
+
</div>
|
|
225
|
+
<div className="flex flex-col items-center gap-2">
|
|
226
|
+
<ProgressCircle progress={0.65} size={64}>
|
|
227
|
+
<span className="text-xs font-bold">65%</span>
|
|
228
|
+
</ProgressCircle>
|
|
229
|
+
<span className="text-xs text-ph-mutedtext">With label</span>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
</ShowcaseWrapper>
|
|
233
|
+
|
|
234
|
+
<ShowcaseWrapper
|
|
235
|
+
title="Row"
|
|
236
|
+
description="Horizontal layout container with icon and side-icon slots."
|
|
237
|
+
code={rowCode}
|
|
238
|
+
filename="RowExample.tsx"
|
|
239
|
+
docs={
|
|
240
|
+
<ComponentDocs
|
|
241
|
+
rows={[
|
|
242
|
+
{ name: "icon", type: "ReactElement", description: "Leading icon element." },
|
|
243
|
+
{ name: "sideIcon", type: "ReactElement", description: "Trailing icon element." },
|
|
244
|
+
{ name: "status", type: "'default' | 'success' | 'warning' | 'danger' | 'muted'", defaultValue: "default", description: "Semantic colour." },
|
|
245
|
+
{ name: "fullWidth", type: "boolean", defaultValue: "false", description: "Takes full width." },
|
|
246
|
+
{ name: "center", type: "boolean", defaultValue: "false", description: "Centers content." },
|
|
247
|
+
{ name: "size", type: "'sm' | 'md' | 'lg'", defaultValue: "md", description: "Row size." },
|
|
248
|
+
]}
|
|
249
|
+
/>
|
|
250
|
+
}
|
|
251
|
+
>
|
|
252
|
+
<div className="space-y-3">
|
|
253
|
+
<Row icon={<User className="h-4 w-4" />}>User profile</Row>
|
|
254
|
+
<Row sideIcon={<Sliders className="h-4 w-4" />}>Settings</Row>
|
|
255
|
+
<Row icon={<Bell className="h-4 w-4" />} sideIcon={<Badge variant="danger">3</Badge>}>
|
|
256
|
+
Notifications
|
|
257
|
+
</Row>
|
|
258
|
+
<Row status="success" fullWidth className="rounded-md bg-ph-muted px-3 py-2">
|
|
259
|
+
Operation completed successfully
|
|
260
|
+
</Row>
|
|
261
|
+
<Row center fullWidth>
|
|
262
|
+
Centered content
|
|
263
|
+
</Row>
|
|
264
|
+
</div>
|
|
265
|
+
</ShowcaseWrapper>
|
|
266
|
+
|
|
267
|
+
<ShowcaseWrapper
|
|
268
|
+
title="Widget"
|
|
269
|
+
description="Panel with a distinct header and content area."
|
|
270
|
+
code={widgetCode}
|
|
271
|
+
filename="WidgetExample.tsx"
|
|
272
|
+
docs={
|
|
273
|
+
<ComponentDocs
|
|
274
|
+
rows={[
|
|
275
|
+
{ name: "title", type: "ReactNode", description: "Widget header title." },
|
|
276
|
+
{ name: "onClose", type: "() => void", description: "Shows close button." },
|
|
277
|
+
{ name: "actions", type: "ReactNode", description: "Header action elements." },
|
|
278
|
+
]}
|
|
279
|
+
/>
|
|
280
|
+
}
|
|
281
|
+
>
|
|
282
|
+
<div className="grid gap-4 sm:grid-cols-2">
|
|
283
|
+
<Widget title="Active Users" onClose={() => {}}>
|
|
284
|
+
<div className="flex items-baseline gap-2">
|
|
285
|
+
<span className="text-2xl font-bold text-ph-ink">1,234</span>
|
|
286
|
+
<span className="text-xs text-ph-success">+12%</span>
|
|
287
|
+
</div>
|
|
288
|
+
<p className="mt-1 text-xs text-ph-mutedtext">Last 30 minutes</p>
|
|
289
|
+
</Widget>
|
|
290
|
+
<Widget title="Page Views" actions={<Tag type="primary">Live</Tag>}>
|
|
291
|
+
<div className="flex items-baseline gap-2">
|
|
292
|
+
<span className="text-2xl font-bold text-ph-ink">89.2k</span>
|
|
293
|
+
<span className="text-xs text-ph-mutedtext">today</span>
|
|
294
|
+
</div>
|
|
295
|
+
</Widget>
|
|
296
|
+
</div>
|
|
297
|
+
</ShowcaseWrapper>
|
|
298
|
+
|
|
299
|
+
<ShowcaseWrapper
|
|
300
|
+
title="Lettermark"
|
|
301
|
+
description="Avatar-style initial letter display."
|
|
302
|
+
code={lettermarkCode}
|
|
303
|
+
filename="LettermarkExample.tsx"
|
|
304
|
+
docs={
|
|
305
|
+
<ComponentDocs
|
|
306
|
+
rows={[
|
|
307
|
+
{ name: "name", type: "string | number", description: "Text to derive initial from." },
|
|
308
|
+
{ name: "index", type: "number", description: "Picks a deterministic colour." },
|
|
309
|
+
{ name: "rounded", type: "boolean", description: "Circular shape." },
|
|
310
|
+
{ name: "outlined", type: "boolean", description: "Outlined vs filled." },
|
|
311
|
+
{ name: "size", type: "'xs' | 'sm' | 'md' | 'lg'", defaultValue: "md" },
|
|
312
|
+
]}
|
|
313
|
+
/>
|
|
314
|
+
}
|
|
315
|
+
>
|
|
316
|
+
<div className="flex flex-wrap items-center gap-4">
|
|
317
|
+
<Lettermark name="PostHog" index={0} />
|
|
318
|
+
<Lettermark name="Analytics" index={1} />
|
|
319
|
+
<Lettermark name="Experiments" index={2} />
|
|
320
|
+
<Lettermark name="Replay" index={3} rounded />
|
|
321
|
+
<Lettermark name="Flags" index={4} rounded />
|
|
322
|
+
<Lettermark name="A" outlined />
|
|
323
|
+
<Lettermark name="42" index={5} rounded outlined />
|
|
324
|
+
<div className="flex items-center gap-2">
|
|
325
|
+
<Lettermark name="XS" size="xs" index={6} />
|
|
326
|
+
<Lettermark name="MD" index={7} />
|
|
327
|
+
<Lettermark name="LG" size="lg" index={0} />
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</ShowcaseWrapper>
|
|
331
|
+
|
|
332
|
+
<ShowcaseWrapper
|
|
333
|
+
title="Splotch"
|
|
334
|
+
description="Color swatch indicator for pickers and menus."
|
|
335
|
+
code={splotchCode}
|
|
336
|
+
filename="SplotchExample.tsx"
|
|
337
|
+
docs={
|
|
338
|
+
<ComponentDocs
|
|
339
|
+
rows={[
|
|
340
|
+
{ name: "color", type: "'purple' | 'blue' | 'green' | 'black' | 'white' | 'orange' | 'red' | 'yellow'", description: "Splotch colour." },
|
|
341
|
+
]}
|
|
342
|
+
/>
|
|
343
|
+
}
|
|
344
|
+
>
|
|
345
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
346
|
+
<Splotch color="purple" />
|
|
347
|
+
<Splotch color="blue" />
|
|
348
|
+
<Splotch color="green" />
|
|
349
|
+
<Splotch color="orange" />
|
|
350
|
+
<Splotch color="red" />
|
|
351
|
+
<Splotch color="yellow" />
|
|
352
|
+
<Splotch color="black" />
|
|
353
|
+
<Splotch color="white" />
|
|
354
|
+
</div>
|
|
355
|
+
</ShowcaseWrapper>
|
|
356
|
+
|
|
357
|
+
<ShowcaseWrapper
|
|
358
|
+
title="Popover"
|
|
359
|
+
description="Floating panel anchored to a trigger element."
|
|
360
|
+
code={popoverCode}
|
|
361
|
+
filename="PopoverExample.tsx"
|
|
362
|
+
docs={
|
|
363
|
+
<ComponentDocs
|
|
364
|
+
rows={[
|
|
365
|
+
{ name: "trigger", type: "ReactNode", description: "Element that opens the popover." },
|
|
366
|
+
{ name: "open", type: "boolean", description: "Controlled open state." },
|
|
367
|
+
{ name: "onOpenChange", type: "(open) => void", description: "Open state change handler." },
|
|
368
|
+
{ name: "placement", type: "'top' | 'bottom' | 'left' | 'right'", defaultValue: "bottom", description: "Popover placement." },
|
|
369
|
+
{ name: "matchTriggerWidth", type: "boolean", defaultValue: "false", description: "Match trigger width." },
|
|
370
|
+
{ name: "padded", type: "boolean", defaultValue: "true", description: "Add padding inside panel." },
|
|
371
|
+
]}
|
|
372
|
+
/>
|
|
373
|
+
}
|
|
374
|
+
>
|
|
375
|
+
<div className="flex flex-wrap gap-4">
|
|
376
|
+
<Popover trigger={<Button variant="secondary">Open popover</Button>}>
|
|
377
|
+
<div className="space-y-2 text-sm">
|
|
378
|
+
<p className="font-medium text-ph-ink">Popover Content</p>
|
|
379
|
+
<p className="text-ph-mutedtext">This is a basic popover with some content inside.</p>
|
|
380
|
+
</div>
|
|
381
|
+
</Popover>
|
|
382
|
+
<Popover trigger={<Button variant="primary">With list</Button>}>
|
|
383
|
+
<div className="space-y-1">
|
|
384
|
+
{["Profile", "Settings", "Help", "Log out"].map((item) => (
|
|
385
|
+
<button
|
|
386
|
+
key={item}
|
|
387
|
+
type="button"
|
|
388
|
+
className="w-full rounded-md px-3 py-1.5 text-left text-sm text-ph-subtle hover:bg-ph-muted hover:text-ph-ink"
|
|
389
|
+
>
|
|
390
|
+
{item}
|
|
391
|
+
</button>
|
|
392
|
+
))}
|
|
393
|
+
</div>
|
|
394
|
+
</Popover>
|
|
395
|
+
</div>
|
|
396
|
+
</ShowcaseWrapper>
|
|
397
|
+
|
|
398
|
+
<ShowcaseWrapper
|
|
399
|
+
title="Loading Bar"
|
|
400
|
+
description="Thin indeterminate progress bar for page-level loading."
|
|
401
|
+
code={loadingBarCode}
|
|
402
|
+
filename="LoadingBarExample.tsx"
|
|
403
|
+
docs={
|
|
404
|
+
<ComponentDocs
|
|
405
|
+
rows={[
|
|
406
|
+
{ name: "active", type: "boolean", defaultValue: "true", description: "Animates when active." },
|
|
407
|
+
]}
|
|
408
|
+
/>
|
|
409
|
+
}
|
|
410
|
+
>
|
|
411
|
+
<div className="space-y-3">
|
|
412
|
+
<LoadingBar active />
|
|
413
|
+
<LoadingBar active={false} />
|
|
414
|
+
<Button variant="primary" onClick={toggleLoading} loading={loading}>
|
|
415
|
+
{loading ? "Loading..." : "Trigger loading"}
|
|
416
|
+
</Button>
|
|
417
|
+
</div>
|
|
418
|
+
</ShowcaseWrapper>
|
|
419
|
+
|
|
420
|
+
<ShowcaseWrapper
|
|
421
|
+
title="Link"
|
|
422
|
+
description="Styled text link with brand colour and hover underline."
|
|
423
|
+
code={linkCode}
|
|
424
|
+
filename="LinkExample.tsx"
|
|
425
|
+
docs={
|
|
426
|
+
<ComponentDocs
|
|
427
|
+
rows={[
|
|
428
|
+
{ name: "disabled", type: "boolean", defaultValue: "false", description: "Disables the link." },
|
|
429
|
+
{ name: "href", type: "string", description: "Anchor href." },
|
|
430
|
+
]}
|
|
431
|
+
/>
|
|
432
|
+
}
|
|
433
|
+
>
|
|
434
|
+
<div className="flex flex-wrap items-center gap-4">
|
|
435
|
+
<Link href="#">Documentation</Link>
|
|
436
|
+
<Link href="#">API Reference</Link>
|
|
437
|
+
<Link href="#">Changelog</Link>
|
|
438
|
+
<Link href="#" disabled>Disabled link</Link>
|
|
439
|
+
</div>
|
|
440
|
+
</ShowcaseWrapper>
|
|
441
|
+
|
|
442
|
+
<ShowcaseWrapper
|
|
443
|
+
title="Spinner"
|
|
444
|
+
description="Animated loading spinner for inline loading states."
|
|
445
|
+
code={spinnerCode}
|
|
446
|
+
filename="SpinnerExample.tsx"
|
|
447
|
+
docs={
|
|
448
|
+
<ComponentDocs
|
|
449
|
+
rows={[
|
|
450
|
+
{ name: "size", type: "'sm' | 'md' | 'lg'", defaultValue: "sm", description: "Spinner size." },
|
|
451
|
+
{ name: "textColored", type: "boolean", defaultValue: "false", description: "Use text colour instead of brand." },
|
|
452
|
+
]}
|
|
453
|
+
/>
|
|
454
|
+
}
|
|
455
|
+
>
|
|
456
|
+
<div className="flex flex-wrap items-center gap-6">
|
|
457
|
+
<div className="flex flex-col items-center gap-2">
|
|
458
|
+
<Spinner size="sm" />
|
|
459
|
+
<span className="text-xs text-ph-mutedtext">Small</span>
|
|
460
|
+
</div>
|
|
461
|
+
<div className="flex flex-col items-center gap-2">
|
|
462
|
+
<Spinner size="md" />
|
|
463
|
+
<span className="text-xs text-ph-mutedtext">Medium</span>
|
|
464
|
+
</div>
|
|
465
|
+
<div className="flex flex-col items-center gap-2">
|
|
466
|
+
<Spinner size="lg" />
|
|
467
|
+
<span className="text-xs text-ph-mutedtext">Large</span>
|
|
468
|
+
</div>
|
|
469
|
+
<div className="flex flex-col items-center gap-2">
|
|
470
|
+
<Spinner textColored />
|
|
471
|
+
<span className="text-xs text-ph-mutedtext">Text colored</span>
|
|
472
|
+
</div>
|
|
473
|
+
</div>
|
|
474
|
+
</ShowcaseWrapper>
|
|
475
|
+
|
|
476
|
+
<ShowcaseWrapper
|
|
477
|
+
title="Snack"
|
|
478
|
+
description="Small chip for displaying removable filter values or selections."
|
|
479
|
+
code={snackCode}
|
|
480
|
+
filename="SnackExample.tsx"
|
|
481
|
+
docs={
|
|
482
|
+
<ComponentDocs
|
|
483
|
+
rows={[
|
|
484
|
+
{ name: "type", type: "'regular' | 'pill'", defaultValue: "regular", description: "Shape variant." },
|
|
485
|
+
{ name: "onClose", type: "() => void", description: "Shows close button." },
|
|
486
|
+
{ name: "wrap", type: "boolean", defaultValue: "false", description: "Allows text wrapping." },
|
|
487
|
+
]}
|
|
488
|
+
/>
|
|
489
|
+
}
|
|
490
|
+
>
|
|
491
|
+
<div className="flex flex-wrap gap-2">
|
|
492
|
+
<Snack>app.posthog.com</Snack>
|
|
493
|
+
<Snack>production</Snack>
|
|
494
|
+
<Snack type="pill">Filter: last 7 days</Snack>
|
|
495
|
+
<Snack type="pill" onClose={() => {}}>Status: active</Snack>
|
|
496
|
+
<Snack onClose={() => {}}>user_123@example.com</Snack>
|
|
497
|
+
</div>
|
|
498
|
+
</ShowcaseWrapper>
|
|
499
|
+
|
|
500
|
+
<ShowcaseWrapper
|
|
501
|
+
title="Dialog"
|
|
502
|
+
description="Confirmation dialog built on Modal with confirm/cancel buttons."
|
|
503
|
+
code={`import { Dialog } from "@xenide-io/the-old-ui-theme";
|
|
504
|
+
|
|
505
|
+
<Dialog
|
|
506
|
+
title="Confirm deletion"
|
|
507
|
+
confirmText="Delete"
|
|
508
|
+
confirmProps={{ variant: "danger" }}
|
|
509
|
+
onConfirm={() => console.log("confirmed")}
|
|
510
|
+
onClose={() => {}}
|
|
511
|
+
>
|
|
512
|
+
Are you sure?
|
|
513
|
+
</Dialog>`}
|
|
514
|
+
filename="DialogExample.tsx"
|
|
515
|
+
docs={
|
|
516
|
+
<ComponentDocs
|
|
517
|
+
rows={[
|
|
518
|
+
{ name: "title", type: "string", description: "Dialog title." },
|
|
519
|
+
{ name: "onConfirm", type: "() => void | Promise<void>", description: "Confirm callback." },
|
|
520
|
+
{ name: "onClose", type: "() => void", description: "Close callback." },
|
|
521
|
+
{ name: "confirmText", type: "string", defaultValue: "Confirm", description: "Confirm button label." },
|
|
522
|
+
{ name: "cancelText", type: "string", defaultValue: "Cancel", description: "Cancel button label." },
|
|
523
|
+
{ name: "confirmProps", type: "Partial<ButtonProps>", description: "Override confirm button." },
|
|
524
|
+
]}
|
|
525
|
+
/>
|
|
526
|
+
}
|
|
527
|
+
>
|
|
528
|
+
<div className="flex flex-wrap gap-4">
|
|
529
|
+
<Button variant="primary" onClick={() => setDialogOpen(true)}>
|
|
530
|
+
Open dialog
|
|
531
|
+
</Button>
|
|
532
|
+
<Dialog
|
|
533
|
+
title="Confirm action"
|
|
534
|
+
isOpen={dialogOpen}
|
|
535
|
+
onClose={() => setDialogOpen(false)}
|
|
536
|
+
onConfirm={() => {}}
|
|
537
|
+
confirmText="Continue"
|
|
538
|
+
>
|
|
539
|
+
<p className="text-sm text-ph-subtle">
|
|
540
|
+
Are you sure you want to proceed with this action? This cannot be undone.
|
|
541
|
+
</p>
|
|
542
|
+
</Dialog>
|
|
543
|
+
</div>
|
|
544
|
+
</ShowcaseWrapper>
|
|
545
|
+
|
|
546
|
+
<ShowcaseWrapper
|
|
547
|
+
title="Combobox"
|
|
548
|
+
description="Searchable multi-select with removable chips."
|
|
549
|
+
code={`import { Combobox } from "@xenide-io/the-old-ui-theme";
|
|
550
|
+
|
|
551
|
+
<Combobox
|
|
552
|
+
options={[
|
|
553
|
+
{ value: "1", label: "Option A" },
|
|
554
|
+
{ value: "2", label: "Option B" },
|
|
555
|
+
]}
|
|
556
|
+
value={["1"]}
|
|
557
|
+
onChange={(v) => console.log(v)}
|
|
558
|
+
/>`}
|
|
559
|
+
filename="ComboboxExample.tsx"
|
|
560
|
+
docs={<ComponentDocs rows={[
|
|
561
|
+
{ name: "options", type: "ComboboxOption[]", description: "Array of {value, label}." },
|
|
562
|
+
{ name: "value", type: "string[]", description: "Selected values." },
|
|
563
|
+
{ name: "onChange", type: "(value: string[]) => void", description: "Selection change handler." },
|
|
564
|
+
{ name: "placeholder", type: "string", defaultValue: "Search..." },
|
|
565
|
+
]} />}
|
|
566
|
+
>
|
|
567
|
+
<Combobox
|
|
568
|
+
options={[
|
|
569
|
+
{ value: "react", label: "React" },
|
|
570
|
+
{ value: "vue", label: "Vue" },
|
|
571
|
+
{ value: "svelte", label: "Svelte" },
|
|
572
|
+
{ value: "angular", label: "Angular" },
|
|
573
|
+
{ value: "solid", label: "Solid" },
|
|
574
|
+
]}
|
|
575
|
+
value={["react", "vue"]}
|
|
576
|
+
placeholder="Search frameworks..."
|
|
577
|
+
/>
|
|
578
|
+
</ShowcaseWrapper>
|
|
579
|
+
|
|
580
|
+
<ShowcaseWrapper
|
|
581
|
+
title="Autocomplete"
|
|
582
|
+
description="Search input with filtered suggestions dropdown."
|
|
583
|
+
code={`import { Autocomplete } from "@xenide-io/the-old-ui-theme";
|
|
584
|
+
|
|
585
|
+
<Autocomplete
|
|
586
|
+
options={[
|
|
587
|
+
{ value: "1", label: "Result A" },
|
|
588
|
+
{ value: "2", label: "Result B" },
|
|
589
|
+
]}
|
|
590
|
+
onSelect={(opt) => console.log(opt)}
|
|
591
|
+
/>`}
|
|
592
|
+
filename="AutocompleteExample.tsx"
|
|
593
|
+
docs={<ComponentDocs rows={[
|
|
594
|
+
{ name: "options", type: "AutocompleteOption[]", description: "Array of {value, label}." },
|
|
595
|
+
{ name: "onSelect", type: "(option) => void", description: "Selection handler." },
|
|
596
|
+
]} />}
|
|
597
|
+
>
|
|
598
|
+
<Autocomplete
|
|
599
|
+
options={[
|
|
600
|
+
{ value: "1", label: "Dashboard" },
|
|
601
|
+
{ value: "2", label: "Insights" },
|
|
602
|
+
{ value: "3", label: "Experiments" },
|
|
603
|
+
{ value: "4", label: "Feature Flags" },
|
|
604
|
+
]}
|
|
605
|
+
placeholder="Search pages..."
|
|
606
|
+
/>
|
|
607
|
+
</ShowcaseWrapper>
|
|
608
|
+
|
|
609
|
+
<ShowcaseWrapper
|
|
610
|
+
title="Context Menu"
|
|
611
|
+
description="Right-click triggered contextual menu."
|
|
612
|
+
code={`import { ContextMenu } from "@xenide-io/the-old-ui-theme";
|
|
613
|
+
|
|
614
|
+
<ContextMenu
|
|
615
|
+
items={[
|
|
616
|
+
{ label: "Edit", onClick: () => {} },
|
|
617
|
+
{ label: "Delete", onClick: () => {}, disabled: true },
|
|
618
|
+
]}
|
|
619
|
+
>
|
|
620
|
+
<div className="p-8 border rounded">Right-click me</div>
|
|
621
|
+
</ContextMenu>`}
|
|
622
|
+
filename="ContextMenuExample.tsx"
|
|
623
|
+
docs={<ComponentDocs rows={[
|
|
624
|
+
{ name: "items", type: "ContextMenuItem[]", description: "Menu items with label, onClick, disabled, separator, icon." },
|
|
625
|
+
]} />}
|
|
626
|
+
>
|
|
627
|
+
<ContextMenu
|
|
628
|
+
items={[
|
|
629
|
+
{ label: "Edit", onClick: () => alert("Edit") },
|
|
630
|
+
{ label: "Duplicate", onClick: () => alert("Duplicate") },
|
|
631
|
+
{ separator: true },
|
|
632
|
+
{ label: "Delete", onClick: () => alert("Delete") },
|
|
633
|
+
]}
|
|
634
|
+
>
|
|
635
|
+
<Card variant="elevated" className="p-8 text-center text-sm text-ph-mutedtext cursor-context-menu">
|
|
636
|
+
Right-click this area
|
|
637
|
+
</Card>
|
|
638
|
+
</ContextMenu>
|
|
639
|
+
</ShowcaseWrapper>
|
|
640
|
+
|
|
641
|
+
<ShowcaseWrapper
|
|
642
|
+
title="Resizable"
|
|
643
|
+
description="Drag-to-resize split panel layout."
|
|
644
|
+
code={`import { Resizable } from "@xenide-io/the-old-ui-theme";
|
|
645
|
+
|
|
646
|
+
<Resizable
|
|
647
|
+
left={<div>Left panel</div>}
|
|
648
|
+
right={<div>Right panel</div>}
|
|
649
|
+
defaultLeftWidth={40}
|
|
650
|
+
/>`}
|
|
651
|
+
filename="ResizableExample.tsx"
|
|
652
|
+
docs={<ComponentDocs rows={[
|
|
653
|
+
{ name: "left", type: "ReactNode", description: "Left panel content." },
|
|
654
|
+
{ name: "right", type: "ReactNode", description: "Right panel content." },
|
|
655
|
+
{ name: "defaultLeftWidth", type: "number", defaultValue: "50", description: "Initial left width percentage." },
|
|
656
|
+
{ name: "minLeftWidth", type: "number", defaultValue: "20", description: "Minimum left width %." },
|
|
657
|
+
{ name: "minRightWidth", type: "number", defaultValue: "20", description: "Minimum right width %." },
|
|
658
|
+
]} />}
|
|
659
|
+
>
|
|
660
|
+
<div className="h-48 rounded-lg border border-ph-border overflow-hidden">
|
|
661
|
+
<Resizable
|
|
662
|
+
left={<div className="flex h-full items-center justify-center text-sm text-ph-mutedtext bg-ph-muted">Left panel</div>}
|
|
663
|
+
right={<div className="flex h-full items-center justify-center text-sm text-ph-mutedtext">Right panel</div>}
|
|
664
|
+
defaultLeftWidth={40}
|
|
665
|
+
/>
|
|
666
|
+
</div>
|
|
667
|
+
</ShowcaseWrapper>
|
|
668
|
+
|
|
669
|
+
<ShowcaseWrapper
|
|
670
|
+
title="Scroll Area"
|
|
671
|
+
description="Scrollable container with edge shadow indicators."
|
|
672
|
+
code={`import { ScrollArea } from "@xenide-io/the-old-ui-theme";
|
|
673
|
+
|
|
674
|
+
<ScrollArea className="h-40">
|
|
675
|
+
{items.map((item) => <div key={item}>{item}</div>)}
|
|
676
|
+
</ScrollArea>`}
|
|
677
|
+
filename="ScrollAreaExample.tsx"
|
|
678
|
+
docs={<ComponentDocs rows={[
|
|
679
|
+
{ name: "orientation", type: "'vertical' | 'horizontal' | 'both'", defaultValue: "vertical" },
|
|
680
|
+
]} />}
|
|
681
|
+
>
|
|
682
|
+
<ScrollArea className="h-32 w-64 rounded-lg border border-ph-border">
|
|
683
|
+
<div className="space-y-2 p-3">
|
|
684
|
+
{Array.from({ length: 20 }, (_, i) => (
|
|
685
|
+
<div key={i} className="rounded-md bg-ph-muted px-3 py-2 text-sm text-ph-subtle">
|
|
686
|
+
Item {i + 1}
|
|
687
|
+
</div>
|
|
688
|
+
))}
|
|
689
|
+
</div>
|
|
690
|
+
</ScrollArea>
|
|
691
|
+
</ShowcaseWrapper>
|
|
692
|
+
|
|
693
|
+
<ShowcaseWrapper
|
|
694
|
+
title="Number Field"
|
|
695
|
+
description="Numeric input with increment/decrement buttons."
|
|
696
|
+
code={`import { NumberField } from "@xenide-io/the-old-ui-theme";
|
|
697
|
+
|
|
698
|
+
<NumberField
|
|
699
|
+
value={5}
|
|
700
|
+
onChange={(v) => console.log(v)}
|
|
701
|
+
min={0}
|
|
702
|
+
max={100}
|
|
703
|
+
step={1}
|
|
704
|
+
/>`}
|
|
705
|
+
filename="NumberFieldExample.tsx"
|
|
706
|
+
docs={<ComponentDocs rows={[
|
|
707
|
+
{ name: "value", type: "number" },
|
|
708
|
+
{ name: "onChange", type: "(value: number) => void" },
|
|
709
|
+
{ name: "min", type: "number" },
|
|
710
|
+
{ name: "max", type: "number" },
|
|
711
|
+
{ name: "step", type: "number", defaultValue: "1" },
|
|
712
|
+
{ name: "label", type: "string" },
|
|
713
|
+
]} />}
|
|
714
|
+
>
|
|
715
|
+
<div className="flex flex-wrap gap-6">
|
|
716
|
+
<NumberField label="Quantity" min={0} max={99} />
|
|
717
|
+
<NumberField label="Timeout (s)" value={30} step={5} />
|
|
718
|
+
</div>
|
|
719
|
+
</ShowcaseWrapper>
|
|
720
|
+
|
|
721
|
+
<ShowcaseWrapper
|
|
722
|
+
title="Input Group"
|
|
723
|
+
description="Input with leading/trailing add-on elements."
|
|
724
|
+
code={`import { InputGroup, Input, Button } from "@xenide-io/the-old-ui-theme";
|
|
725
|
+
|
|
726
|
+
<InputGroup>
|
|
727
|
+
<span className="px-3 py-2 text-sm bg-ph-muted text-ph-subtle border-r border-ph-border">https://</span>
|
|
728
|
+
<input className="ph-input flex-1 border-0 rounded-none" />
|
|
729
|
+
<Button>Go</Button>
|
|
730
|
+
</InputGroup>`}
|
|
731
|
+
filename="InputGroupExample.tsx"
|
|
732
|
+
docs={<ComponentDocs rows={[
|
|
733
|
+
{ name: "children", type: "ReactNode", description: "Elements to group together." },
|
|
734
|
+
]} />}
|
|
735
|
+
>
|
|
736
|
+
<div className="space-y-3">
|
|
737
|
+
<InputGroup>
|
|
738
|
+
<span className="inline-flex items-center px-3 text-sm text-ph-mutedtext bg-ph-muted border-r border-ph-border">https://</span>
|
|
739
|
+
<Input placeholder="example.com" className="rounded-none border-0" />
|
|
740
|
+
</InputGroup>
|
|
741
|
+
<InputGroup>
|
|
742
|
+
<Input placeholder="Search..." className="rounded-none border-0" />
|
|
743
|
+
<Button variant="primary" className="rounded-none"><Search className="h-4 w-4" /></Button>
|
|
744
|
+
</InputGroup>
|
|
745
|
+
</div>
|
|
746
|
+
</ShowcaseWrapper>
|
|
747
|
+
|
|
748
|
+
<ShowcaseWrapper
|
|
749
|
+
title="Button Group"
|
|
750
|
+
description="Segmented button row with separators."
|
|
751
|
+
code={`import { ButtonGroup, Button } from "@xenide-io/the-old-ui-theme";
|
|
752
|
+
|
|
753
|
+
<ButtonGroup>
|
|
754
|
+
<Button variant="secondary">Left</Button>
|
|
755
|
+
<Button variant="secondary">Center</Button>
|
|
756
|
+
<Button variant="secondary">Right</Button>
|
|
757
|
+
</ButtonGroup>`}
|
|
758
|
+
filename="ButtonGroupExample.tsx"
|
|
759
|
+
docs={<ComponentDocs rows={[
|
|
760
|
+
{ name: "children", type: "ReactNode", description: "Buttons to group." },
|
|
761
|
+
]} />}
|
|
762
|
+
>
|
|
763
|
+
<ButtonGroup>
|
|
764
|
+
<Button variant="secondary" size="sm">Day</Button>
|
|
765
|
+
<Button variant="secondary" size="sm">Week</Button>
|
|
766
|
+
<Button variant="secondary" size="sm">Month</Button>
|
|
767
|
+
<Button variant="secondary" size="sm">Year</Button>
|
|
768
|
+
</ButtonGroup>
|
|
769
|
+
</ShowcaseWrapper>
|
|
770
|
+
|
|
771
|
+
<ShowcaseWrapper
|
|
772
|
+
title="Toggle Button"
|
|
773
|
+
description="Pressable toggle button for toolbars and stateful controls."
|
|
774
|
+
code={`import { ToggleButton, ToggleGroup } from "@xenide-io/the-old-ui-theme";
|
|
775
|
+
|
|
776
|
+
<ToggleButton pressed={true} onPressedChange={(p) => console.log(p)}>
|
|
777
|
+
Bold
|
|
778
|
+
</ToggleButton>`}
|
|
779
|
+
filename="ToggleButtonExample.tsx"
|
|
780
|
+
docs={<ComponentDocs rows={[
|
|
781
|
+
{ name: "pressed", type: "boolean", defaultValue: "false" },
|
|
782
|
+
{ name: "onPressedChange", type: "(pressed: boolean) => void" },
|
|
783
|
+
{ name: "icon", type: "ReactNode", description: "Leading icon." },
|
|
784
|
+
]} />}
|
|
785
|
+
>
|
|
786
|
+
<div className="space-y-4">
|
|
787
|
+
<div className="flex flex-wrap gap-2">
|
|
788
|
+
<ToggleButton pressed={true}>Bold</ToggleButton>
|
|
789
|
+
<ToggleButton>Italic</ToggleButton>
|
|
790
|
+
<ToggleButton>Underline</ToggleButton>
|
|
791
|
+
</div>
|
|
792
|
+
</div>
|
|
793
|
+
</ShowcaseWrapper>
|
|
794
|
+
|
|
795
|
+
<ShowcaseWrapper
|
|
796
|
+
title="Menubar"
|
|
797
|
+
description="Horizontal menu bar with dropdown submenus."
|
|
798
|
+
code={`import { Menubar } from "@xenide-io/the-old-ui-theme";
|
|
799
|
+
|
|
800
|
+
<Menubar
|
|
801
|
+
items={[
|
|
802
|
+
{ label: "File", items: [{ label: "New" }, { label: "Open" }] },
|
|
803
|
+
{ label: "Edit", items: [{ label: "Cut" }, { label: "Copy" }] },
|
|
804
|
+
]}
|
|
805
|
+
/>`}
|
|
806
|
+
filename="MenubarExample.tsx"
|
|
807
|
+
docs={<ComponentDocs rows={[
|
|
808
|
+
{ name: "items", type: "MenubarItem[]", description: "Menu items with label and optional sub-items." },
|
|
809
|
+
]} />}
|
|
810
|
+
>
|
|
811
|
+
<Menubar
|
|
812
|
+
items={[
|
|
813
|
+
{
|
|
814
|
+
label: "File",
|
|
815
|
+
items: [
|
|
816
|
+
{ label: "New File", onClick: () => alert("New") },
|
|
817
|
+
{ label: "Open...", onClick: () => alert("Open") },
|
|
818
|
+
{ separator: true },
|
|
819
|
+
{ label: "Save", onClick: () => alert("Save") },
|
|
820
|
+
],
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
label: "Edit",
|
|
824
|
+
items: [
|
|
825
|
+
{ label: "Undo", onClick: () => alert("Undo") },
|
|
826
|
+
{ label: "Redo", onClick: () => alert("Redo") },
|
|
827
|
+
],
|
|
828
|
+
},
|
|
829
|
+
{ label: "View", items: [{ label: "Toggle Sidebar" }] },
|
|
830
|
+
]}
|
|
831
|
+
/>
|
|
832
|
+
</ShowcaseWrapper>
|
|
833
|
+
|
|
834
|
+
<ShowcaseWrapper
|
|
835
|
+
title="Alert Dialog"
|
|
836
|
+
description="Alert role confirmation dialog."
|
|
837
|
+
code={`import { AlertDialog, Button } from "@xenide-io/the-old-ui-theme";
|
|
838
|
+
|
|
839
|
+
<AlertDialog
|
|
840
|
+
title="Delete item"
|
|
841
|
+
confirmText="Delete"
|
|
842
|
+
confirmProps={{ variant: "danger" }}
|
|
843
|
+
onConfirm={() => {}}
|
|
844
|
+
>
|
|
845
|
+
<p>This action cannot be undone.</p>
|
|
846
|
+
</AlertDialog>`}
|
|
847
|
+
filename="AlertDialogExample.tsx"
|
|
848
|
+
docs={<ComponentDocs rows={[
|
|
849
|
+
{ name: "title", type: "string" },
|
|
850
|
+
{ name: "onConfirm", type: "() => void | Promise<void>" },
|
|
851
|
+
{ name: "confirmText", type: "string", defaultValue: "Confirm" },
|
|
852
|
+
{ name: "cancelText", type: "string", defaultValue: "Cancel" },
|
|
853
|
+
]} />}
|
|
854
|
+
>
|
|
855
|
+
<div>
|
|
856
|
+
<Button variant="danger" onClick={() => setDialogOpen(true)}>Delete item</Button>
|
|
857
|
+
<AlertDialog
|
|
858
|
+
title="Delete item"
|
|
859
|
+
isOpen={dialogOpen}
|
|
860
|
+
onClose={() => setDialogOpen(false)}
|
|
861
|
+
onConfirm={() => alert("Deleted!")}
|
|
862
|
+
confirmText="Delete"
|
|
863
|
+
confirmProps={{ variant: "danger" }}
|
|
864
|
+
>
|
|
865
|
+
<p className="text-sm text-ph-subtle">This action cannot be undone. Are you sure you want to delete this item?</p>
|
|
866
|
+
</AlertDialog>
|
|
867
|
+
</div>
|
|
868
|
+
</ShowcaseWrapper>
|
|
869
|
+
|
|
870
|
+
<ShowcaseWrapper
|
|
871
|
+
title="Dot"
|
|
872
|
+
description="Tiny presence and status indicator dot."
|
|
873
|
+
code={`import { Dot } from "@xenide-io/the-old-ui-theme";
|
|
874
|
+
|
|
875
|
+
<Dot color="green" />
|
|
876
|
+
<Dot color="yellow" />
|
|
877
|
+
<Dot color="red" />`}
|
|
878
|
+
filename="DotExample.tsx"
|
|
879
|
+
docs={<ComponentDocs rows={[
|
|
880
|
+
{ name: "color", type: "'green' | 'yellow' | 'red' | 'blue' | 'gray'", defaultValue: "gray" },
|
|
881
|
+
{ name: "size", type: "'sm' | 'md'", defaultValue: "md" },
|
|
882
|
+
]} />}
|
|
883
|
+
>
|
|
884
|
+
<div className="flex flex-wrap items-center gap-4">
|
|
885
|
+
{(["green", "yellow", "red", "blue", "gray"] as const).map((c) => (
|
|
886
|
+
<div key={c} className="flex items-center gap-2 text-sm text-ph-subtle">
|
|
887
|
+
<Dot color={c} />
|
|
888
|
+
<span className="capitalize">{c}</span>
|
|
889
|
+
</div>
|
|
890
|
+
))}
|
|
891
|
+
</div>
|
|
892
|
+
</ShowcaseWrapper>
|
|
893
|
+
|
|
894
|
+
<ShowcaseWrapper
|
|
895
|
+
title="Collapsible"
|
|
896
|
+
description="Single-section disclosure with chevron toggle."
|
|
897
|
+
code={`import { Collapsible } from "@xenide-io/the-old-ui-theme";
|
|
898
|
+
|
|
899
|
+
<Collapsible trigger={<span>Section title</span>}>
|
|
900
|
+
Hidden content here
|
|
901
|
+
</Collapsible>`}
|
|
902
|
+
filename="CollapsibleExample.tsx"
|
|
903
|
+
docs={<ComponentDocs rows={[
|
|
904
|
+
{ name: "trigger", type: "ReactNode", description: "Visible trigger content." },
|
|
905
|
+
{ name: "open", type: "boolean", description: "Controlled open state." },
|
|
906
|
+
{ name: "onOpenChange", type: "(open) => void" },
|
|
907
|
+
]} />}
|
|
908
|
+
>
|
|
909
|
+
<div className="space-y-2">
|
|
910
|
+
<Collapsible trigger={<span className="text-sm font-medium">Configuration</span>}>
|
|
911
|
+
<div className="space-y-2">
|
|
912
|
+
<Checkbox label="Enable analytics" />
|
|
913
|
+
<Checkbox label="Enable session recording" />
|
|
914
|
+
</div>
|
|
915
|
+
</Collapsible>
|
|
916
|
+
<Collapsible trigger={<span className="text-sm font-medium">Advanced</span>}>
|
|
917
|
+
<p className="text-ph-subtle">Advanced configuration options go here.</p>
|
|
918
|
+
</Collapsible>
|
|
919
|
+
</div>
|
|
920
|
+
</ShowcaseWrapper>
|
|
921
|
+
|
|
922
|
+
<ShowcaseWrapper
|
|
923
|
+
title="Metric"
|
|
924
|
+
description="Composable stat tile with label, value, badge, and trend."
|
|
925
|
+
code={`import { Metric, Badge } from "@xenide-io/the-old-ui-theme";
|
|
926
|
+
|
|
927
|
+
<Metric
|
|
928
|
+
value="1,234"
|
|
929
|
+
label="Active Users"
|
|
930
|
+
badge={<Badge variant="success">+12%</Badge>}
|
|
931
|
+
/>`}
|
|
932
|
+
filename="MetricExample.tsx"
|
|
933
|
+
docs={<ComponentDocs rows={[
|
|
934
|
+
{ name: "value", type: "string | number", description: "Primary metric value." },
|
|
935
|
+
{ name: "label", type: "string", description: "Metric label." },
|
|
936
|
+
{ name: "badge", type: "ReactNode", description: "Optional badge/chip." },
|
|
937
|
+
{ name: "trend", type: "ReactNode", description: "Trend indicator." },
|
|
938
|
+
{ name: "icon", type: "ReactNode", description: "Leading icon." },
|
|
939
|
+
]} />}
|
|
940
|
+
>
|
|
941
|
+
<div className="grid gap-4 sm:grid-cols-3">
|
|
942
|
+
<Metric
|
|
943
|
+
value="89.2k"
|
|
944
|
+
label="Page Views"
|
|
945
|
+
badge={<Badge variant="success">+12%</Badge>}
|
|
946
|
+
trend={<span>vs 79.6k last week</span>}
|
|
947
|
+
/>
|
|
948
|
+
<Metric
|
|
949
|
+
value="2,847"
|
|
950
|
+
label="Active Users"
|
|
951
|
+
badge={<Badge variant="warning">+3%</Badge>}
|
|
952
|
+
trend={<span>vs 2,764 last week</span>}
|
|
953
|
+
/>
|
|
954
|
+
<Metric
|
|
955
|
+
value="23"
|
|
956
|
+
label="Avg. Session (min)"
|
|
957
|
+
badge={<Badge variant="danger">-5%</Badge>}
|
|
958
|
+
trend={<span>vs 24.2 last week</span>}
|
|
959
|
+
/>
|
|
960
|
+
</div>
|
|
961
|
+
</ShowcaseWrapper>
|
|
962
|
+
|
|
963
|
+
<ShowcaseWrapper
|
|
964
|
+
title="Chip"
|
|
965
|
+
description="Removable token for filters, tags, and multi-select values."
|
|
966
|
+
code={`import { Chip } from "@xenide-io/the-old-ui-theme";
|
|
967
|
+
|
|
968
|
+
<Chip onRemove={() => {}}>React</Chip>
|
|
969
|
+
<Chip selected>Vue</Chip>`}
|
|
970
|
+
filename="ChipExample.tsx"
|
|
971
|
+
docs={<ComponentDocs rows={[
|
|
972
|
+
{ name: "selected", type: "boolean", defaultValue: "false" },
|
|
973
|
+
{ name: "onRemove", type: "() => void", description: "Shows remove button." },
|
|
974
|
+
]} />}
|
|
975
|
+
>
|
|
976
|
+
<div className="flex flex-wrap gap-2">
|
|
977
|
+
<Chip>React</Chip>
|
|
978
|
+
<Chip selected>Vue</Chip>
|
|
979
|
+
<Chip>Svelte</Chip>
|
|
980
|
+
<Chip selected onRemove={() => {}}>Angular</Chip>
|
|
981
|
+
<Chip onRemove={() => {}}>Solid</Chip>
|
|
982
|
+
</div>
|
|
983
|
+
</ShowcaseWrapper>
|
|
984
|
+
|
|
985
|
+
<ShowcaseWrapper
|
|
986
|
+
title="Data Table"
|
|
987
|
+
description="Sortable, paginated, searchable data table."
|
|
988
|
+
code={`import { DataTable } from "@xenide-io/the-old-ui-theme";
|
|
989
|
+
|
|
990
|
+
<DataTable
|
|
991
|
+
columns={[
|
|
992
|
+
{ key: "name", label: "Name", sortable: true },
|
|
993
|
+
{ key: "status", label: "Status" },
|
|
994
|
+
]}
|
|
995
|
+
data={[
|
|
996
|
+
{ name: "Item 1", status: "Active" },
|
|
997
|
+
]}
|
|
998
|
+
searchable
|
|
999
|
+
pageSize={5}
|
|
1000
|
+
/>`}
|
|
1001
|
+
filename="DataTableExample.tsx"
|
|
1002
|
+
docs={<ComponentDocs rows={[
|
|
1003
|
+
{ name: "columns", type: "DataTableColumn[]", description: "Column definitions with key, label, render, sortable." },
|
|
1004
|
+
{ name: "data", type: "T[]", description: "Array of data objects." },
|
|
1005
|
+
{ name: "pageSize", type: "number", defaultValue: "10" },
|
|
1006
|
+
{ name: "searchable", type: "boolean", defaultValue: "false" },
|
|
1007
|
+
]} />}
|
|
1008
|
+
>
|
|
1009
|
+
<DataTable
|
|
1010
|
+
columns={[
|
|
1011
|
+
{ key: "name", label: "Name", sortable: true },
|
|
1012
|
+
{ key: "email", label: "Email", sortable: true },
|
|
1013
|
+
{ key: "role", label: "Role", sortable: true },
|
|
1014
|
+
{ key: "status", label: "Status" },
|
|
1015
|
+
]}
|
|
1016
|
+
data={[
|
|
1017
|
+
{ name: "Alice Johnson", email: "alice@example.com", role: "Admin", status: "Active" },
|
|
1018
|
+
{ name: "Bob Smith", email: "bob@example.com", role: "Editor", status: "Active" },
|
|
1019
|
+
{ name: "Charlie Lee", email: "charlie@example.com", role: "Viewer", status: "Inactive" },
|
|
1020
|
+
{ name: "Diana Ross", email: "diana@example.com", role: "Admin", status: "Active" },
|
|
1021
|
+
{ name: "Eve Wilson", email: "eve@example.com", role: "Editor", status: "Active" },
|
|
1022
|
+
{ name: "Frank Brown", email: "frank@example.com", role: "Viewer", status: "Inactive" },
|
|
1023
|
+
{ name: "Grace Chen", email: "grace@example.com", role: "Admin", status: "Active" },
|
|
1024
|
+
]}
|
|
1025
|
+
searchable
|
|
1026
|
+
pageSize={3}
|
|
1027
|
+
/>
|
|
1028
|
+
</ShowcaseWrapper>
|
|
1029
|
+
|
|
1030
|
+
<ShowcaseWrapper
|
|
1031
|
+
title="Date Picker"
|
|
1032
|
+
description="Calendar date picker dropdown."
|
|
1033
|
+
code={`import { DatePicker } from "@xenide-io/the-old-ui-theme";
|
|
1034
|
+
|
|
1035
|
+
<DatePicker
|
|
1036
|
+
value={new Date()}
|
|
1037
|
+
onChange={(date) => console.log(date)}
|
|
1038
|
+
/>`}
|
|
1039
|
+
filename="DatePickerExample.tsx"
|
|
1040
|
+
docs={<ComponentDocs rows={[
|
|
1041
|
+
{ name: "value", type: "Date", description: "Selected date." },
|
|
1042
|
+
{ name: "onChange", type: "(date: Date) => void", description: "Date selection handler." },
|
|
1043
|
+
]} />}
|
|
1044
|
+
>
|
|
1045
|
+
<DatePicker
|
|
1046
|
+
value={new Date()}
|
|
1047
|
+
onChange={(date) => console.log(date.toDateString())}
|
|
1048
|
+
/>
|
|
1049
|
+
</ShowcaseWrapper>
|
|
1050
|
+
</>
|
|
1051
|
+
);
|
|
1052
|
+
}
|