@xenide-io/the-old-ui-theme 0.2.6 → 0.2.9
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-SHF57J7U.mjs +2910 -0
- package/dist/index-CmS6hcUk.d.mts +753 -0
- package/dist/index-CmS6hcUk.d.ts +753 -0
- package/dist/index.d.mts +5 -755
- package/dist/index.d.ts +5 -755
- package/dist/index.js +43 -23
- package/dist/index.mjs +252 -2846
- package/dist/ui.d.mts +3 -0
- package/dist/ui.d.ts +3 -0
- package/dist/ui.js +2946 -0
- package/dist/ui.mjs +139 -0
- package/package.json +11 -3
- 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 +39 -0
- package/src/components/icons/createIconBase.tsx +27 -0
- package/src/components/icons/icons.tsx +367 -0
- package/src/components/icons/index.ts +3 -0
- package/src/components/sections/AccordionShowcase.tsx +45 -0
- package/src/components/sections/AlertShowcase.tsx +39 -0
- package/src/components/sections/AvatarShowcase.tsx +80 -0
- package/src/components/sections/BadgeShowcase.tsx +65 -0
- package/src/components/sections/ButtonShowcase.tsx +312 -0
- package/src/components/sections/CalendarShowcase.tsx +35 -0
- package/src/components/sections/CardShowcase.tsx +143 -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 +100 -0
- package/src/components/sections/EmptyStateShowcase.tsx +50 -0
- package/src/components/sections/FilterChipsShowcase.tsx +50 -0
- package/src/components/sections/FormShowcase.tsx +126 -0
- package/src/components/sections/HoverCardShowcase.tsx +50 -0
- package/src/components/sections/IconShowcase.tsx +247 -0
- package/src/components/sections/IndicatorShowcase.tsx +52 -0
- package/src/components/sections/KbdShowcase.tsx +31 -0
- package/src/components/sections/ModalShowcase.tsx +210 -0
- package/src/components/sections/NavigationShowcase.tsx +199 -0
- package/src/components/sections/ProgressShowcase.tsx +85 -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 +91 -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 +68 -0
- package/src/components/ui/Avatar.tsx +97 -0
- package/src/components/ui/Badge.tsx +55 -0
- package/src/components/ui/Button.tsx +110 -0
- package/src/components/ui/ButtonChrome.tsx +20 -0
- package/src/components/ui/Calendar.tsx +116 -0
- package/src/components/ui/Card.tsx +60 -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 +138 -0
- package/src/components/ui/EmptyState.tsx +33 -0
- package/src/components/ui/FilterChips.tsx +50 -0
- package/src/components/ui/HoverCard.tsx +36 -0
- package/src/components/ui/Indicator.tsx +61 -0
- package/src/components/ui/Input.tsx +359 -0
- package/src/components/ui/Kbd.tsx +38 -0
- package/src/components/ui/Modal.tsx +75 -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/ShowcaseWrapper.tsx +136 -0
- package/src/components/ui/Stat.tsx +39 -0
- package/src/components/ui/Stepper.tsx +75 -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/index.ts +92 -0
- package/src/styles/themes.css +118 -0
package/dist/ui.mjs
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Accordion,
|
|
3
|
+
Alert,
|
|
4
|
+
Avatar,
|
|
5
|
+
AvatarGroup,
|
|
6
|
+
Badge,
|
|
7
|
+
Breadcrumbs,
|
|
8
|
+
Button,
|
|
9
|
+
ButtonChrome,
|
|
10
|
+
Calendar,
|
|
11
|
+
Caption,
|
|
12
|
+
Card,
|
|
13
|
+
ChatBubble,
|
|
14
|
+
Checkbox,
|
|
15
|
+
CodeBlock,
|
|
16
|
+
CollapsibleSection,
|
|
17
|
+
CommandPalette,
|
|
18
|
+
ComponentDocs,
|
|
19
|
+
Display,
|
|
20
|
+
Drawer,
|
|
21
|
+
DropdownButton,
|
|
22
|
+
DropdownItem,
|
|
23
|
+
DropdownMenu,
|
|
24
|
+
EmptyState,
|
|
25
|
+
FileUpload,
|
|
26
|
+
FilterChips,
|
|
27
|
+
H1,
|
|
28
|
+
H2,
|
|
29
|
+
H3,
|
|
30
|
+
H4,
|
|
31
|
+
H5,
|
|
32
|
+
HoverCard,
|
|
33
|
+
IconBase,
|
|
34
|
+
IconByName,
|
|
35
|
+
Indicator,
|
|
36
|
+
Input,
|
|
37
|
+
Kbd,
|
|
38
|
+
Label,
|
|
39
|
+
Lead,
|
|
40
|
+
Modal,
|
|
41
|
+
Mono,
|
|
42
|
+
Overline,
|
|
43
|
+
P,
|
|
44
|
+
Pagination,
|
|
45
|
+
Panel,
|
|
46
|
+
Progress,
|
|
47
|
+
Radio,
|
|
48
|
+
Range,
|
|
49
|
+
Rating,
|
|
50
|
+
SearchGroup,
|
|
51
|
+
SearchInput,
|
|
52
|
+
SegmentedControl,
|
|
53
|
+
Select,
|
|
54
|
+
ShowcaseWrapper,
|
|
55
|
+
Small,
|
|
56
|
+
Stat,
|
|
57
|
+
Stepper,
|
|
58
|
+
Table,
|
|
59
|
+
Tabs,
|
|
60
|
+
Terminal,
|
|
61
|
+
Textarea,
|
|
62
|
+
ThemeDomSync,
|
|
63
|
+
ThemeManager,
|
|
64
|
+
ThemeSwitcher,
|
|
65
|
+
Timeline,
|
|
66
|
+
Toast,
|
|
67
|
+
ToastStack,
|
|
68
|
+
Toggle
|
|
69
|
+
} from "./chunk-SHF57J7U.mjs";
|
|
70
|
+
import "./chunk-FWCSY2DS.mjs";
|
|
71
|
+
export {
|
|
72
|
+
Accordion,
|
|
73
|
+
Alert,
|
|
74
|
+
Avatar,
|
|
75
|
+
AvatarGroup,
|
|
76
|
+
Badge,
|
|
77
|
+
Breadcrumbs,
|
|
78
|
+
Button,
|
|
79
|
+
ButtonChrome,
|
|
80
|
+
Calendar,
|
|
81
|
+
Caption,
|
|
82
|
+
Card,
|
|
83
|
+
ChatBubble,
|
|
84
|
+
Checkbox,
|
|
85
|
+
CodeBlock,
|
|
86
|
+
CollapsibleSection,
|
|
87
|
+
CommandPalette,
|
|
88
|
+
ComponentDocs,
|
|
89
|
+
Display,
|
|
90
|
+
Drawer,
|
|
91
|
+
DropdownButton,
|
|
92
|
+
DropdownItem,
|
|
93
|
+
DropdownMenu,
|
|
94
|
+
EmptyState,
|
|
95
|
+
FileUpload,
|
|
96
|
+
FilterChips,
|
|
97
|
+
H1,
|
|
98
|
+
H2,
|
|
99
|
+
H3,
|
|
100
|
+
H4,
|
|
101
|
+
H5,
|
|
102
|
+
HoverCard,
|
|
103
|
+
IconBase as Icon,
|
|
104
|
+
IconByName,
|
|
105
|
+
Indicator,
|
|
106
|
+
Input,
|
|
107
|
+
Kbd,
|
|
108
|
+
Label,
|
|
109
|
+
Lead,
|
|
110
|
+
Modal,
|
|
111
|
+
Mono,
|
|
112
|
+
Overline,
|
|
113
|
+
P,
|
|
114
|
+
Pagination,
|
|
115
|
+
Panel,
|
|
116
|
+
Progress,
|
|
117
|
+
Radio,
|
|
118
|
+
Range,
|
|
119
|
+
Rating,
|
|
120
|
+
SearchGroup,
|
|
121
|
+
SearchInput,
|
|
122
|
+
SegmentedControl,
|
|
123
|
+
Select,
|
|
124
|
+
ShowcaseWrapper,
|
|
125
|
+
Small,
|
|
126
|
+
Stat,
|
|
127
|
+
Stepper,
|
|
128
|
+
Table,
|
|
129
|
+
Tabs,
|
|
130
|
+
Terminal,
|
|
131
|
+
Textarea,
|
|
132
|
+
ThemeDomSync,
|
|
133
|
+
ThemeManager,
|
|
134
|
+
ThemeSwitcher,
|
|
135
|
+
Timeline,
|
|
136
|
+
Toast,
|
|
137
|
+
ToastStack,
|
|
138
|
+
Toggle
|
|
139
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xenide-io/the-old-ui-theme",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Props-driven React components and theme tokens inspired by classic interface design — optimized for Next.js apps and internal tools.",
|
|
5
5
|
"author": "Xenide",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"node": ">=18.0.0"
|
|
23
23
|
},
|
|
24
24
|
"sideEffects": [
|
|
25
|
-
"*.css"
|
|
25
|
+
"*.css",
|
|
26
|
+
"src/lib/chart/register-chartjs.ts"
|
|
26
27
|
],
|
|
27
28
|
"main": "./dist/index.js",
|
|
28
29
|
"module": "./dist/index.mjs",
|
|
@@ -36,6 +37,12 @@
|
|
|
36
37
|
},
|
|
37
38
|
"./styles.css": "./src/app/globals.css",
|
|
38
39
|
"./themes.css": "./src/styles/themes.css",
|
|
40
|
+
"./ui": {
|
|
41
|
+
"types": "./dist/ui.d.ts",
|
|
42
|
+
"import": "./dist/ui.mjs",
|
|
43
|
+
"require": "./dist/ui.js",
|
|
44
|
+
"default": "./dist/ui.mjs"
|
|
45
|
+
},
|
|
39
46
|
"./tailwind-preset": {
|
|
40
47
|
"types": "./dist/tailwind-preset.d.ts",
|
|
41
48
|
"import": "./dist/tailwind-preset.mjs",
|
|
@@ -46,13 +53,14 @@
|
|
|
46
53
|
"files": [
|
|
47
54
|
"dist",
|
|
48
55
|
"src/app/globals.css",
|
|
56
|
+
"src/components",
|
|
49
57
|
"src/styles",
|
|
50
58
|
"tailwind-preset.ts"
|
|
51
59
|
],
|
|
52
60
|
"scripts": {
|
|
53
61
|
"dev": "next dev",
|
|
54
62
|
"build": "next build",
|
|
55
|
-
"build:lib": "tsup --entry.index src/index.ts --entry.tailwind-preset tailwind-preset.ts --format cjs,esm --dts --external react,react-dom,next --clean --tsconfig ./tsconfig.lib.json",
|
|
63
|
+
"build:lib": "tsup --entry.index src/index.ts --entry.ui src/components/ui/index.ts --entry.tailwind-preset tailwind-preset.ts --format cjs,esm --dts --external react,react-dom,next --clean --tsconfig ./tsconfig.lib.json",
|
|
56
64
|
"start": "next start",
|
|
57
65
|
"lint": "next lint",
|
|
58
66
|
"test": "tsc --noEmit",
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import "@/lib/chart/register-chartjs";
|
|
5
|
+
import { Bar, Doughnut, PolarArea } from "react-chartjs-2";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
buildDoughnutDemo,
|
|
9
|
+
buildHorizontalBarDemo,
|
|
10
|
+
buildPolarAreaDemo,
|
|
11
|
+
buildStackedBarDemo,
|
|
12
|
+
buildVerticalBarDemo,
|
|
13
|
+
} from "@/lib/chart/ph-chart-options";
|
|
14
|
+
import { useChartTokens } from "@/lib/chart/use-ph-chart-tokens";
|
|
15
|
+
import { buildHogChartTheme } from "@/lib/hog-charts-lite/build-theme";
|
|
16
|
+
import { CanvasTrendLineChart } from "@/lib/hog-charts-lite/CanvasTrendLineChart";
|
|
17
|
+
|
|
18
|
+
const DEMO_LABELS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
19
|
+
|
|
20
|
+
function useClientMounted(): boolean {
|
|
21
|
+
const [mounted, setMounted] = useState(false);
|
|
22
|
+
|
|
23
|
+
useEffect(() => setMounted(true), []);
|
|
24
|
+
|
|
25
|
+
return mounted;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface ChartSkeletonProps {
|
|
29
|
+
className?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function ChartSkeleton({ className = "h-[280px]" }: ChartSkeletonProps) {
|
|
33
|
+
return (
|
|
34
|
+
<div
|
|
35
|
+
className={`w-full animate-pulse rounded-lemon bg-ph-muted ${className}`}
|
|
36
|
+
aria-hidden
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function HogTrendLineChart() {
|
|
42
|
+
const mounted = useClientMounted();
|
|
43
|
+
const tokens = useChartTokens();
|
|
44
|
+
const theme = useMemo(() => buildHogChartTheme(tokens), [tokens]);
|
|
45
|
+
|
|
46
|
+
const series = useMemo(
|
|
47
|
+
() => [
|
|
48
|
+
{
|
|
49
|
+
key: "pageview",
|
|
50
|
+
label: "$pageview",
|
|
51
|
+
data: [4200, 5100, 4800, 6200, 5900, 3100, 2800],
|
|
52
|
+
color: tokens.series[0],
|
|
53
|
+
fill: { opacity: 0.35 },
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
key: "signup",
|
|
57
|
+
label: "signup_completed",
|
|
58
|
+
data: [920, 990, 940, 1040, 1012, 540, 510],
|
|
59
|
+
color: tokens.series[1],
|
|
60
|
+
stroke: { pattern: [5, 5] as number[] },
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
[tokens.series],
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
if (!mounted) {
|
|
67
|
+
return <ChartSkeleton />;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div className="relative h-[280px] w-full min-h-[220px]">
|
|
72
|
+
<CanvasTrendLineChart
|
|
73
|
+
labels={DEMO_LABELS}
|
|
74
|
+
series={series}
|
|
75
|
+
theme={theme}
|
|
76
|
+
showGrid
|
|
77
|
+
showCrosshair
|
|
78
|
+
className="h-full min-h-[220px]"
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function InsightVerticalBarChart() {
|
|
85
|
+
const mounted = useClientMounted();
|
|
86
|
+
const tokens = useChartTokens();
|
|
87
|
+
const config = useMemo(() => buildVerticalBarDemo(tokens), [tokens]);
|
|
88
|
+
|
|
89
|
+
if (!mounted) {
|
|
90
|
+
return <ChartSkeleton />;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div className="relative h-[280px] w-full min-h-[220px]">
|
|
95
|
+
<Bar data={config.data} options={config.options} redraw={false} />
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function InsightHorizontalBarChart() {
|
|
101
|
+
const mounted = useClientMounted();
|
|
102
|
+
const tokens = useChartTokens();
|
|
103
|
+
const config = useMemo(() => buildHorizontalBarDemo(tokens), [tokens]);
|
|
104
|
+
|
|
105
|
+
if (!mounted) {
|
|
106
|
+
return <ChartSkeleton className="h-[320px]" />;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<div className="relative h-[320px] w-full min-h-[260px]">
|
|
111
|
+
<Bar data={config.data} options={config.options} redraw={false} />
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function InsightStackedBarChart() {
|
|
117
|
+
const mounted = useClientMounted();
|
|
118
|
+
const tokens = useChartTokens();
|
|
119
|
+
const config = useMemo(() => buildStackedBarDemo(tokens), [tokens]);
|
|
120
|
+
|
|
121
|
+
if (!mounted) {
|
|
122
|
+
return <ChartSkeleton className="h-[300px]" />;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div className="relative h-[300px] w-full min-h-[240px]">
|
|
127
|
+
<Bar data={config.data} options={config.options} redraw={false} />
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function InsightDoughnutChart() {
|
|
133
|
+
const mounted = useClientMounted();
|
|
134
|
+
const tokens = useChartTokens();
|
|
135
|
+
const config = useMemo(() => buildDoughnutDemo(tokens), [tokens]);
|
|
136
|
+
|
|
137
|
+
if (!mounted) {
|
|
138
|
+
return <ChartSkeleton className="h-[260px]" />;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div className="relative mx-auto h-[260px] w-full max-w-[280px] min-h-[220px]">
|
|
143
|
+
<Doughnut data={config.data} options={config.options} redraw={false} />
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function InsightPolarAreaChart() {
|
|
149
|
+
const mounted = useClientMounted();
|
|
150
|
+
const tokens = useChartTokens();
|
|
151
|
+
const config = useMemo(() => buildPolarAreaDemo(tokens), [tokens]);
|
|
152
|
+
|
|
153
|
+
if (!mounted) {
|
|
154
|
+
return <ChartSkeleton className="h-[280px]" />;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<div className="relative mx-auto h-[280px] w-full max-w-[320px] min-h-[240px]">
|
|
159
|
+
<PolarArea data={config.data} options={config.options} redraw={false} />
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef } from "react";
|
|
2
|
+
import { cn } from "@/lib/cn";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Raised strip reminiscent of dashboard date-range / cohort / breakdown controls.
|
|
6
|
+
* Pass pills or selects as children.
|
|
7
|
+
*/
|
|
8
|
+
export function DashboardFiltersBar({ className, ...props }: ComponentPropsWithoutRef<"div">) {
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
className={cn(
|
|
12
|
+
"flex flex-wrap items-center gap-2 rounded-[var(--ph-radius-app)] border border-solid bg-ph-surface px-3 py-2 text-sm text-ph-subtle [border-color:var(--ph-border-control)]",
|
|
13
|
+
"[box-shadow:var(--ph-chrome-inset-face-top),var(--ph-chrome-inset-face-bottom),var(--ph-shadow-elevation-3000)]",
|
|
14
|
+
className,
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef } from "react";
|
|
2
|
+
import { cn } from "@/lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface DashboardGridProps extends ComponentPropsWithoutRef<"div"> {
|
|
5
|
+
/**
|
|
6
|
+
* Preset breakpoints: 1 col → md:2 cols → xl:3 (matches common insight grids).
|
|
7
|
+
* Override with your own `className` if you need 4-up or single column.
|
|
8
|
+
*/
|
|
9
|
+
preset?: "insights" | "dense";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const presetClass: Record<NonNullable<DashboardGridProps["preset"]>, string> = {
|
|
13
|
+
insights: "grid gap-4 md:grid-cols-2 xl:grid-cols-3",
|
|
14
|
+
dense: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function DashboardGrid({
|
|
18
|
+
preset = "insights",
|
|
19
|
+
className,
|
|
20
|
+
...props
|
|
21
|
+
}: DashboardGridProps) {
|
|
22
|
+
return <div className={cn(presetClass[preset], className)} {...props} />;
|
|
23
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef } from "react";
|
|
2
|
+
import { cn } from "@/lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface DashboardInsightPlaceholderProps extends ComponentPropsWithoutRef<"button"> {
|
|
5
|
+
/** Accessible name when `children` does not include visible text. */
|
|
6
|
+
label?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Mimics PostHog’s empty dashboard slot (“add insight”).
|
|
11
|
+
* Renders `<button>` so you can wire `onClick` → palette / modal.
|
|
12
|
+
*
|
|
13
|
+
* **`onClick` and other event props:** use this inside a Client Component (or a wrapper
|
|
14
|
+
* marked `'use client'`). Server Components cannot serialise function props.
|
|
15
|
+
*/
|
|
16
|
+
export function DashboardInsightPlaceholder({
|
|
17
|
+
label = "Add insight",
|
|
18
|
+
className,
|
|
19
|
+
children,
|
|
20
|
+
type = "button",
|
|
21
|
+
...props
|
|
22
|
+
}: DashboardInsightPlaceholderProps) {
|
|
23
|
+
return (
|
|
24
|
+
<button
|
|
25
|
+
type={type}
|
|
26
|
+
aria-label={label}
|
|
27
|
+
className={cn(
|
|
28
|
+
"ph-dashboard-placeholder group text-left outline-none transition",
|
|
29
|
+
"focus-visible:ring-2 focus-visible:ring-ph-brand focus-visible:ring-offset-2",
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
{children != null ? children : <span className="font-medium text-ph-subtle group-hover:text-ph-ink">{label}</span>}
|
|
35
|
+
</button>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
|
+
import { cn } from "@/lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface DashboardKpiCardProps extends ComponentPropsWithoutRef<"article"> {
|
|
5
|
+
/** Upper label (tabular small caps styling via `.ph-stat-label`). */
|
|
6
|
+
label: ReactNode;
|
|
7
|
+
/** Main KPI value. */
|
|
8
|
+
value: ReactNode;
|
|
9
|
+
/** Supporting line beneath the value (Δ %, qualifier, etc.). */
|
|
10
|
+
footer?: ReactNode;
|
|
11
|
+
/** Optional decorative icon aligned with headline stats across the PostHog app. */
|
|
12
|
+
icon?: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function DashboardKpiCard({ label, value, footer, icon, className, children, ...props }: DashboardKpiCardProps) {
|
|
16
|
+
return (
|
|
17
|
+
<article className={cn("ph-stat", className)} {...props}>
|
|
18
|
+
{icon != null ? <div className="mb-2 [&>svg]:h-7 [&>svg]:w-7">{icon}</div> : null}
|
|
19
|
+
<div className="ph-stat-label">{label}</div>
|
|
20
|
+
<div className="ph-stat-val">{value}</div>
|
|
21
|
+
{footer != null ? <p className="mt-1 text-xs text-ph-subtle">{footer}</p> : null}
|
|
22
|
+
{children}
|
|
23
|
+
</article>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { cn } from "@/lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface DashboardToolbarProps {
|
|
5
|
+
/** Dashboard / tab title (matches PostHog H3 weight on canvases). */
|
|
6
|
+
title: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
/** Right-aligned actions slot (buttons, dropdown triggers, etc.). */
|
|
9
|
+
actions?: ReactNode;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function DashboardToolbar({ title, description, actions, className }: DashboardToolbarProps) {
|
|
14
|
+
return (
|
|
15
|
+
<div className={cn("flex flex-wrap items-baseline justify-between gap-4", className)}>
|
|
16
|
+
<div className="min-w-0">
|
|
17
|
+
<h3 className="text-lg font-bold text-ph-ink">{title}</h3>
|
|
18
|
+
{description ? <p className="mt-0.5 text-sm text-ph-mutedtext">{description}</p> : null}
|
|
19
|
+
</div>
|
|
20
|
+
{actions != null ? <div className="flex shrink-0 flex-wrap items-center gap-2">{actions}</div> : null}
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef } from "react";
|
|
2
|
+
import { cn } from "@/lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface DashboardWellProps extends ComponentPropsWithoutRef<"div"> {
|
|
5
|
+
/** Passed through to inner div; merges with `.ph-dashboard-well` */
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function DashboardWell({ className, ...props }: DashboardWellProps) {
|
|
9
|
+
return <div className={cn("ph-dashboard-well", className)} {...props} />;
|
|
10
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { InsightShellProps } from "./InsightShell";
|
|
2
|
+
import { InsightShell } from "./InsightShell";
|
|
3
|
+
import { InsightShellHeader } from "./InsightShellHeader";
|
|
4
|
+
import { MiniTrendBars } from "./MiniTrendBars";
|
|
5
|
+
|
|
6
|
+
export interface InsightMiniCardProps extends Omit<InsightShellProps, "children"> {
|
|
7
|
+
title: string;
|
|
8
|
+
subtitle?: string;
|
|
9
|
+
heights: readonly number[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Opinionated insight tile: coloured ribbon + headline + subtitle + mini Hog-style bar sparkline. */
|
|
13
|
+
export function InsightMiniCard({
|
|
14
|
+
title,
|
|
15
|
+
subtitle,
|
|
16
|
+
heights,
|
|
17
|
+
ribbonColour,
|
|
18
|
+
...shellProps
|
|
19
|
+
}: InsightMiniCardProps) {
|
|
20
|
+
return (
|
|
21
|
+
<InsightShell ribbonColour={ribbonColour} {...shellProps}>
|
|
22
|
+
<InsightShellHeader title={title} subtitle={subtitle} />
|
|
23
|
+
<MiniTrendBars heights={heights} />
|
|
24
|
+
</InsightShell>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef } from "react";
|
|
2
|
+
import type { RibbonColour } from "./types";
|
|
3
|
+
import { cn } from "@/lib/cn";
|
|
4
|
+
|
|
5
|
+
export interface InsightShellProps extends ComponentPropsWithoutRef<"article"> {
|
|
6
|
+
/**
|
|
7
|
+
* Left ribbon fill; omit or pass empty string for a flat card (no ribbon column).
|
|
8
|
+
* Prefer `var(--ph-product-*)` or `var(--ph-data-*)` tokens from `globals.css`.
|
|
9
|
+
*/
|
|
10
|
+
ribbonColour?: RibbonColour | null;
|
|
11
|
+
/** Extra classes on the padded body column (e.g. `gap-4`). */
|
|
12
|
+
bodyClassName?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function InsightShell({
|
|
16
|
+
ribbonColour,
|
|
17
|
+
bodyClassName,
|
|
18
|
+
className,
|
|
19
|
+
children,
|
|
20
|
+
...props
|
|
21
|
+
}: InsightShellProps) {
|
|
22
|
+
const ribbon =
|
|
23
|
+
ribbonColour != null && String(ribbonColour).trim() !== "" ? String(ribbonColour).trim() : null;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<article className={cn("ph-insight-shell flex-row", className)} {...props}>
|
|
27
|
+
{ribbon != null ? (
|
|
28
|
+
<div
|
|
29
|
+
className="ph-insight-shell__ribbon min-h-[7rem] shrink-0 self-stretch"
|
|
30
|
+
style={{ background: ribbon }}
|
|
31
|
+
aria-hidden
|
|
32
|
+
/>
|
|
33
|
+
) : null}
|
|
34
|
+
<div className={cn("flex min-w-0 flex-1 flex-col gap-3 p-4", bodyClassName)}>{children}</div>
|
|
35
|
+
</article>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { cn } from "@/lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface InsightShellHeaderProps {
|
|
5
|
+
title: ReactNode;
|
|
6
|
+
subtitle?: ReactNode;
|
|
7
|
+
/** Trailing widgets (menus, fullscreen, cohort badge). */
|
|
8
|
+
actions?: ReactNode;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function InsightShellHeader({ title, subtitle, actions, className }: InsightShellHeaderProps) {
|
|
13
|
+
return (
|
|
14
|
+
<header className={cn("flex gap-3", actions != null ? "items-start justify-between" : "", className)}>
|
|
15
|
+
<div className="min-w-0 flex-1">
|
|
16
|
+
<h4 className="font-semibold leading-snug text-ph-ink">{title}</h4>
|
|
17
|
+
{subtitle != null ? <p className="mt-0.5 text-xs text-ph-mutedtext">{subtitle}</p> : null}
|
|
18
|
+
</div>
|
|
19
|
+
{actions != null ? <div className="flex shrink-0 items-center gap-1">{actions}</div> : null}
|
|
20
|
+
</header>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
import { cn } from "@/lib/cn";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_SERIES_CYCLE = [1, 2, 3, 4, 5, 6, 7] as const;
|
|
5
|
+
|
|
6
|
+
export interface MiniTrendBarsProps {
|
|
7
|
+
/** Normalised heights 0–100 for each bar (% of chart row). */
|
|
8
|
+
heights: readonly number[];
|
|
9
|
+
/** Tailwind height of the bars row (flex align-end). Default matches gallery `h-28`. */
|
|
10
|
+
rowClassName?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Map bar index → CSS `--ph-data-*` token index (1–7).
|
|
13
|
+
* Default cycles through PostHog’s default trend palette.
|
|
14
|
+
*/
|
|
15
|
+
seriesResolver?: (index: number, height: number) => number;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function clampPct(n: number): number {
|
|
20
|
+
if (!Number.isFinite(n)) {
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
return Math.max(0, Math.min(100, n));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function MiniTrendBars({
|
|
27
|
+
heights,
|
|
28
|
+
rowClassName = "h-28",
|
|
29
|
+
seriesResolver,
|
|
30
|
+
className,
|
|
31
|
+
}: MiniTrendBarsProps) {
|
|
32
|
+
const resolveSeries =
|
|
33
|
+
seriesResolver ??
|
|
34
|
+
((i: number) => DEFAULT_SERIES_CYCLE[i % DEFAULT_SERIES_CYCLE.length] ?? 1);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className={cn("flex items-end gap-0.5", rowClassName, className)}>
|
|
38
|
+
{heights.map((raw, i) => {
|
|
39
|
+
const pct = clampPct(raw);
|
|
40
|
+
const series = resolveSeries(i, pct);
|
|
41
|
+
const style = {
|
|
42
|
+
height: `${pct}%`,
|
|
43
|
+
background: `var(--ph-data-${String(series)})`,
|
|
44
|
+
minHeight: pct > 0 ? 4 : 0,
|
|
45
|
+
} satisfies CSSProperties;
|
|
46
|
+
|
|
47
|
+
return <div key={`bar-${String(i)}`} className="min-h-0 flex-1 rounded-t-[3px]" style={style} />;
|
|
48
|
+
})}
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export { cn } from "@/lib/cn";
|
|
2
|
+
export type { RibbonColour, Slot } from "./types";
|
|
3
|
+
|
|
4
|
+
export { DashboardWell } from "./DashboardWell";
|
|
5
|
+
export type { DashboardWellProps } from "./DashboardWell";
|
|
6
|
+
|
|
7
|
+
export { DashboardToolbar } from "./DashboardToolbar";
|
|
8
|
+
export type { DashboardToolbarProps } from "./DashboardToolbar";
|
|
9
|
+
|
|
10
|
+
export { DashboardGrid } from "./DashboardGrid";
|
|
11
|
+
export type { DashboardGridProps } from "./DashboardGrid";
|
|
12
|
+
|
|
13
|
+
export { DashboardFiltersBar } from "./DashboardFiltersBar";
|
|
14
|
+
|
|
15
|
+
export { DashboardKpiCard } from "./DashboardKpiCard";
|
|
16
|
+
export type { DashboardKpiCardProps } from "./DashboardKpiCard";
|
|
17
|
+
|
|
18
|
+
export { DashboardInsightPlaceholder } from "./DashboardInsightPlaceholder";
|
|
19
|
+
export type { DashboardInsightPlaceholderProps } from "./DashboardInsightPlaceholder";
|
|
20
|
+
|
|
21
|
+
export { InsightShell } from "./InsightShell";
|
|
22
|
+
export type { InsightShellProps } from "./InsightShell";
|
|
23
|
+
|
|
24
|
+
export { InsightShellHeader } from "./InsightShellHeader";
|
|
25
|
+
export type { InsightShellHeaderProps } from "./InsightShellHeader";
|
|
26
|
+
|
|
27
|
+
export { MiniTrendBars } from "./MiniTrendBars";
|
|
28
|
+
export type { MiniTrendBarsProps } from "./MiniTrendBars";
|
|
29
|
+
|
|
30
|
+
export { InsightMiniCard } from "./InsightMiniCard";
|
|
31
|
+
export type { InsightMiniCardProps } from "./InsightMiniCard";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Any CSS `<color>` usable in `background`, e.g. `var(--ph-product-session-replay)` or `#1d4aff`.
|
|
5
|
+
*/
|
|
6
|
+
export type RibbonColour = string;
|
|
7
|
+
|
|
8
|
+
/** Children slot pattern used across dashboard primitives */
|
|
9
|
+
export type Slot = ReactNode;
|