@xenide-io/the-old-ui-theme 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +47 -34
  2. package/dist/{index-Od4pIP4F.d.mts → Chip-C5hRFAhe.d.mts} +10 -606
  3. package/dist/{index-Od4pIP4F.d.ts → Chip-C5hRFAhe.d.ts} +10 -606
  4. package/dist/{chunk-G53YLHVE.mjs → chunk-E5ZBQYEX.mjs} +59 -9
  5. package/dist/{chunk-RZXHZWUB.mjs → chunk-SBBKXV35.mjs} +174 -222
  6. package/dist/index.d.mts +4 -80
  7. package/dist/index.d.ts +4 -80
  8. package/dist/index.js +562 -3746
  9. package/dist/index.mjs +3 -765
  10. package/dist/suite.d.mts +56 -25
  11. package/dist/suite.d.ts +56 -25
  12. package/dist/suite.js +248 -107
  13. package/dist/suite.mjs +217 -105
  14. package/dist/ui.d.mts +608 -2
  15. package/dist/ui.d.ts +608 -2
  16. package/dist/ui.js +232 -230
  17. package/dist/ui.mjs +2 -2
  18. package/package.json +2 -6
  19. package/scripts/install-theme-dev.sh +60 -0
  20. package/scripts/sync-posthog-icons.mjs +75 -0
  21. package/src/app/globals.css +1 -0
  22. package/src/components/ui/Chip.tsx +36 -18
  23. package/src/components/ui/CommandPalette.test.tsx +41 -0
  24. package/src/components/ui/CommandPalette.tsx +58 -9
  25. package/src/components/ui/DropdownMenu.tsx +50 -11
  26. package/src/components/ui/SegmentedControl.tsx +2 -1
  27. package/src/components/ui/SettingsLayout.tsx +19 -5
  28. package/src/components/ui/ThemeSwitcher.tsx +12 -3
  29. package/src/components/ui/interaction-primitives.test.tsx +52 -0
  30. package/src/styles/suite-skin.css +53 -1
  31. package/src/styles/themes.css +445 -1656
  32. package/src/suite/components/app-switcher.tsx +32 -25
  33. package/src/suite/components/command-palette-host.tsx +20 -8
  34. package/src/suite/components/suite-app-layout.tsx +31 -10
  35. package/src/suite/components/suite-bottom-nav.tsx +23 -18
  36. package/src/suite/components/suite-layout.test.tsx +44 -0
  37. package/src/suite/components/suite-layout.tsx +3 -3
  38. package/src/suite/components/suite-mobile-drawer.tsx +12 -0
  39. package/src/suite/components/suite-mobile-header.tsx +15 -9
  40. package/src/suite/components/suite-notification-bell.tsx +23 -11
  41. package/src/suite/components/suite-settings-mobile-nav.tsx +2 -0
  42. package/src/suite/components/suite-sidebar.tsx +54 -43
  43. package/src/suite/components/suite-user-menu.tsx +54 -26
  44. package/src/suite/components/today-page-frame.tsx +1 -1
  45. package/src/suite/icons/app-accents.ts +38 -43
  46. package/src/suite/icons/glyph-parts.tsx +16 -9
  47. package/src/suite/icons/glyphs.ts +130 -99
  48. package/src/suite/lib/apps.ts +1 -1
  49. package/src/suite/lib/injected.ts +12 -11
  50. package/src/suite/lib/use-sidebar-width.ts +23 -8
  51. package/src/components/charts/index.ts +0 -9
  52. package/src/components/charts/quill/BarChart.tsx +0 -85
  53. package/src/components/charts/quill/FunnelChart.tsx +0 -49
  54. package/src/components/charts/quill/InsightShell.tsx +0 -27
  55. package/src/components/charts/quill/MetricCard.tsx +0 -44
  56. package/src/components/charts/quill/PieChart.tsx +0 -81
  57. package/src/components/charts/quill/Sparkline.tsx +0 -57
  58. package/src/components/charts/quill/TimeSeriesLineChart.tsx +0 -62
  59. package/src/components/charts/quill/index.ts +0 -9
@@ -14,13 +14,14 @@
14
14
 
15
15
  export type SuiteGlyphElement =
16
16
  | {
17
- kind: 'path';
17
+ kind: "path";
18
18
  d: string;
19
19
  accent?: boolean;
20
20
  filled?: boolean;
21
+ transform?: string;
21
22
  }
22
23
  | {
23
- kind: 'circle';
24
+ kind: "circle";
24
25
  cx: number;
25
26
  cy: number;
26
27
  r: number;
@@ -28,7 +29,7 @@ export type SuiteGlyphElement =
28
29
  filled?: boolean;
29
30
  }
30
31
  | {
31
- kind: 'rect';
32
+ kind: "rect";
32
33
  x: number;
33
34
  y: number;
34
35
  width: number;
@@ -50,120 +51,147 @@ export interface SuiteGlyph {
50
51
  }
51
52
 
52
53
  export type SuiteIconName =
53
- | 'timer-shell'
54
- | 'kanban-wave'
55
- | 'squid-doc'
56
- | 'stack-hex'
57
- | 'crew-bot'
58
- | 'workspace'
59
- | 'organisation'
60
- | 'integration-plug'
61
- | 'suite-grid'
62
- | 'bell-wave'
63
- | 'today-sun';
54
+ | "timer-shell"
55
+ | "kanban-wave"
56
+ | "squid-doc"
57
+ | "stack-hex"
58
+ | "crew-bot"
59
+ | "nakama-fist"
60
+ | "workspace"
61
+ | "organisation"
62
+ | "integration-plug"
63
+ | "suite-grid"
64
+ | "bell-wave"
65
+ | "today-sun";
64
66
 
65
67
  export const SUITE_GLYPHS: Record<SuiteIconName, SuiteGlyph> = {
66
- 'timer-shell': {
67
- description: 'TurtleTime — turtle-shell dome holding clock hands',
68
+ "timer-shell": {
69
+ description: "TurtleTime — turtle-shell dome holding clock hands",
68
70
  elements: [
69
- { kind: 'path', d: 'M3 17h18' },
70
- { kind: 'path', d: 'M6.5 17a5.5 5.5 0 0 1 11 0' },
71
- { kind: 'circle', cx: 4, cy: 15.3, r: 1.5 },
72
- { kind: 'path', d: 'M12 17v-3.4', accent: true },
73
- { kind: 'path', d: 'M12 17l2.4-1.4', accent: true },
71
+ { kind: "path", d: "M3 17h18" },
72
+ { kind: "path", d: "M6.5 17a5.5 5.5 0 0 1 11 0" },
73
+ { kind: "circle", cx: 4, cy: 15.3, r: 1.5 },
74
+ { kind: "path", d: "M12 17v-3.4", accent: true },
75
+ { kind: "path", d: "M12 17l2.4-1.4", accent: true },
74
76
  ],
75
77
  },
76
- 'kanban-wave': {
77
- description: 'Tides — three kanban bars riding a wave',
78
+ "kanban-wave": {
79
+ description: "Tides — three kanban bars riding a wave",
78
80
  elements: [
79
- { kind: 'rect', x: 5.5, y: 8, width: 3, height: 8, rx: 1.5 },
80
- { kind: 'rect', x: 10.5, y: 5.5, width: 3, height: 10.5, rx: 1.5 },
81
- { kind: 'rect', x: 15.5, y: 9.5, width: 3, height: 6.5, rx: 1.5 },
81
+ { kind: "rect", x: 5.5, y: 8, width: 3, height: 8, rx: 1.5 },
82
+ { kind: "rect", x: 10.5, y: 5.5, width: 3, height: 10.5, rx: 1.5 },
83
+ { kind: "rect", x: 15.5, y: 9.5, width: 3, height: 6.5, rx: 1.5 },
82
84
  {
83
- kind: 'path',
84
- d: 'M3 19.5q2.25-2.4 4.5 0t4.5 0t4.5 0t4.5 0',
85
+ kind: "path",
86
+ d: "M3 19.5q2.25-2.4 4.5 0t4.5 0t4.5 0t4.5 0",
85
87
  accent: true,
86
88
  },
87
89
  ],
88
90
  },
89
- 'squid-doc': {
90
- description: 'Kraken — squid with a pen-nib centre tentacle',
91
+ "squid-doc": {
92
+ description: "Kraken — squid with a pen-nib centre tentacle",
91
93
  elements: [
92
94
  {
93
- kind: 'path',
94
- d: 'M12 3.2c2.7 2.3 4.2 5.3 4.2 9.3H7.8c0-4 1.5-7 4.2-9.3Z',
95
+ kind: "path",
96
+ d: "M12 3.2c2.7 2.3 4.2 5.3 4.2 9.3H7.8c0-4 1.5-7 4.2-9.3Z",
95
97
  },
96
- { kind: 'circle', cx: 10.1, cy: 9, r: 0.9, filled: true },
97
- { kind: 'circle', cx: 13.9, cy: 9, r: 0.9, filled: true },
98
- { kind: 'path', d: 'M9.4 12.4v4.8q0 2.3-2.3 2.3' },
99
- { kind: 'path', d: 'M14.6 12.4v4.8q0 2.3 2.3 2.3' },
100
- { kind: 'path', d: 'M12 12.4v7.1', accent: true },
98
+ { kind: "circle", cx: 10.1, cy: 9, r: 0.9, filled: true },
99
+ { kind: "circle", cx: 13.9, cy: 9, r: 0.9, filled: true },
100
+ { kind: "path", d: "M9.4 12.4v4.8q0 2.3-2.3 2.3" },
101
+ { kind: "path", d: "M14.6 12.4v4.8q0 2.3 2.3 2.3" },
102
+ { kind: "path", d: "M12 12.4v7.1", accent: true },
101
103
  ],
102
104
  },
103
- 'stack-hex': {
104
- description: 'ShellStack — hexagon with the resolving spiral',
105
+ "stack-hex": {
106
+ description: "ShellStack — hexagon with the resolving spiral",
105
107
  elements: [
106
- { kind: 'path', d: 'M18 12l-3 5.2H9l-3-5.2 3-5.2h6Z' },
107
- { kind: 'path', d: 'M14.2 9.3H9.8v5.4h4.4v-2.7h-2.2', accent: true },
108
+ { kind: "path", d: "M18 12l-3 5.2H9l-3-5.2 3-5.2h6Z" },
109
+ { kind: "path", d: "M14.2 9.3H9.8v5.4h4.4v-2.7h-2.2", accent: true },
108
110
  ],
109
111
  },
110
- 'crew-bot': {
111
- description: 'Crew — friendly robot head with antenna and smile',
112
+ "crew-bot": {
113
+ description: "Crew — friendly robot head with antenna and smile",
112
114
  elements: [
113
- { kind: 'path', d: 'M12 7.5V5' },
114
- { kind: 'circle', cx: 12, cy: 4, r: 1, filled: true },
115
- { kind: 'rect', x: 5, y: 8, width: 14, height: 9.5, rx: 3 },
116
- { kind: 'path', d: 'M5 11.8H3.4' },
117
- { kind: 'path', d: 'M19 11.8h1.6' },
118
- { kind: 'circle', cx: 9.2, cy: 11.8, r: 1, filled: true },
119
- { kind: 'circle', cx: 14.8, cy: 11.8, r: 1, filled: true },
120
- { kind: 'path', d: 'M9.6 14.6q2.4 1.7 4.8 0', accent: true },
115
+ { kind: "path", d: "M12 7.5V5" },
116
+ { kind: "circle", cx: 12, cy: 4, r: 1, filled: true },
117
+ { kind: "rect", x: 5, y: 8, width: 14, height: 9.5, rx: 3 },
118
+ { kind: "path", d: "M5 11.8H3.4" },
119
+ { kind: "path", d: "M19 11.8h1.6" },
120
+ { kind: "circle", cx: 9.2, cy: 11.8, r: 1, filled: true },
121
+ { kind: "circle", cx: 14.8, cy: 11.8, r: 1, filled: true },
122
+ { kind: "path", d: "M9.6 14.6q2.4 1.7 4.8 0", accent: true },
123
+ ],
124
+ },
125
+ "nakama-fist": {
126
+ description: "Nakama — raised fist with neon crew signal lines",
127
+ elements: [
128
+ {
129
+ kind: "path",
130
+ d: "M192 0c17.7 0 32 14.3 32 32v112h-64V32c0-17.7 14.3-32 32-32zM64 64c0-17.7-32-32 32-32s32 14.3 32 32v80H64V64zm192 0c0-17.7 14.3-32 32-32s32 14.3 32 32v96c0 17.7-14.3 32-32 32s-32-14.3-32-32V64zm96 64c0-17.7 14.3-32 32-32s32 14.3 32 32v64c0 17.7-14.3 32-32 32s-32-14.3-32-32v-64zm-96 88v-.6c9.4 5.4 20.3 8.6 32 8.6 13.2 0 25.4-4 35.6-10.8 8.7 24.9 32.5 42.8 60.4 42.8 11.7 0 22.6-3.1 32-8.6v8.6c0 52.3-25.1 98.8-64 128v96c0 17.7-14.3 32-32 32H160c-17.7 0-32-14.3-32-32v-78.4c-17.3-7.9-33.2-18.8-46.9-32.5L69.5 357.5C45.5 333.5 32 300.9 32 267v-27c0-35.3 28.7-64 64-64h88c22.1 0 40 17.9 40 40s-17.9 40-40 40h-56c-8.8 0-16 7.2-16 16s7.2 16 16 16h56c39.8 0 72-32.2 72-72z",
131
+ filled: true,
132
+ transform: "translate(2.5 1.75) scale(.0425 .0375)",
133
+ },
134
+ { kind: "path", d: "M7 18h3.5M12.2 18h3M8.5 19.5h6", accent: true },
135
+ {
136
+ kind: "path",
137
+ d: "M4.8 5.8h2.7M4.8 7.4h1.4M18.5 17.1h1.7",
138
+ accent: true,
139
+ },
140
+ { kind: "circle", cx: 18.5, cy: 4.5, r: 0.8, accent: true, filled: true },
121
141
  ],
122
142
  },
123
143
  workspace: {
124
- description: 'Workspace container — panel with header bar and content',
144
+ description: "Workspace container — panel with header bar and content",
125
145
  elements: [
126
- { kind: 'rect', x: 3.5, y: 4.5, width: 17, height: 15, rx: 2.5 },
127
- { kind: 'path', d: 'M3.5 8.7h17' },
128
- { kind: 'circle', cx: 6.2, cy: 6.6, r: 0.9, accent: true, filled: true },
129
- { kind: 'path', d: 'M6.4 12.2h7' },
130
- { kind: 'path', d: 'M6.4 15.4h10.5' },
146
+ { kind: "rect", x: 3.5, y: 4.5, width: 17, height: 15, rx: 2.5 },
147
+ { kind: "path", d: "M3.5 8.7h17" },
148
+ { kind: "circle", cx: 6.2, cy: 6.6, r: 0.9, accent: true, filled: true },
149
+ { kind: "path", d: "M6.4 12.2h7" },
150
+ { kind: "path", d: "M6.4 15.4h10.5" },
131
151
  ],
132
152
  },
133
153
  organisation: {
134
- description: 'Organisation chart — root node with two members',
154
+ description: "Organisation chart — root node with two members",
135
155
  elements: [
136
- { kind: 'rect', x: 9.5, y: 3.5, width: 5, height: 5, rx: 1.4, accent: true },
137
- { kind: 'path', d: 'M12 8.5v2.5' },
138
- { kind: 'path', d: 'M6.75 13.5v-2.5h10.5v2.5' },
139
- { kind: 'rect', x: 4.25, y: 13.5, width: 5, height: 5, rx: 1.4 },
140
- { kind: 'rect', x: 14.75, y: 13.5, width: 5, height: 5, rx: 1.4 },
156
+ {
157
+ kind: "rect",
158
+ x: 9.5,
159
+ y: 3.5,
160
+ width: 5,
161
+ height: 5,
162
+ rx: 1.4,
163
+ accent: true,
164
+ },
165
+ { kind: "path", d: "M12 8.5v2.5" },
166
+ { kind: "path", d: "M6.75 13.5v-2.5h10.5v2.5" },
167
+ { kind: "rect", x: 4.25, y: 13.5, width: 5, height: 5, rx: 1.4 },
168
+ { kind: "rect", x: 14.75, y: 13.5, width: 5, height: 5, rx: 1.4 },
141
169
  ],
142
170
  },
143
- 'integration-plug': {
144
- description: 'Integration — two plug halves meeting under a spark',
171
+ "integration-plug": {
172
+ description: "Integration — two plug halves meeting under a spark",
145
173
  elements: [
146
- { kind: 'rect', x: 3.2, y: 9.8, width: 5.6, height: 4.6, rx: 1.4 },
147
- { kind: 'path', d: 'M8.8 11h2.4' },
148
- { kind: 'path', d: 'M8.8 13.2h2.4' },
149
- { kind: 'rect', x: 15.2, y: 9.8, width: 5.6, height: 4.6, rx: 1.4 },
150
- { kind: 'path', d: 'M15.2 11h-2.4' },
151
- { kind: 'path', d: 'M15.2 13.2h-2.4' },
152
- { kind: 'path', d: 'M6 14.4v2q0 1.4-1.4 1.4H3.4' },
153
- { kind: 'path', d: 'M18 14.4v2q0 1.4 1.4 1.4h1.2' },
154
- { kind: 'path', d: 'M12 4.4v2.4', accent: true },
155
- { kind: 'path', d: 'M10.4 5l3.2 1.2', accent: true },
156
- { kind: 'path', d: 'M13.6 5l-3.2 1.2', accent: true },
174
+ { kind: "rect", x: 3.2, y: 9.8, width: 5.6, height: 4.6, rx: 1.4 },
175
+ { kind: "path", d: "M8.8 11h2.4" },
176
+ { kind: "path", d: "M8.8 13.2h2.4" },
177
+ { kind: "rect", x: 15.2, y: 9.8, width: 5.6, height: 4.6, rx: 1.4 },
178
+ { kind: "path", d: "M15.2 11h-2.4" },
179
+ { kind: "path", d: "M15.2 13.2h-2.4" },
180
+ { kind: "path", d: "M6 14.4v2q0 1.4-1.4 1.4H3.4" },
181
+ { kind: "path", d: "M18 14.4v2q0 1.4 1.4 1.4h1.2" },
182
+ { kind: "path", d: "M12 4.4v2.4", accent: true },
183
+ { kind: "path", d: "M10.4 5l3.2 1.2", accent: true },
184
+ { kind: "path", d: "M13.6 5l-3.2 1.2", accent: true },
157
185
  ],
158
186
  },
159
- 'suite-grid': {
160
- description: 'Suite app switcher — four tiles, the active app filled',
187
+ "suite-grid": {
188
+ description: "Suite app switcher — four tiles, the active app filled",
161
189
  elements: [
162
- { kind: 'rect', x: 4, y: 4, width: 7, height: 7, rx: 2 },
163
- { kind: 'rect', x: 13, y: 4, width: 7, height: 7, rx: 2 },
164
- { kind: 'rect', x: 4, y: 13, width: 7, height: 7, rx: 2 },
190
+ { kind: "rect", x: 4, y: 4, width: 7, height: 7, rx: 2 },
191
+ { kind: "rect", x: 13, y: 4, width: 7, height: 7, rx: 2 },
192
+ { kind: "rect", x: 4, y: 13, width: 7, height: 7, rx: 2 },
165
193
  {
166
- kind: 'rect',
194
+ kind: "rect",
167
195
  x: 13,
168
196
  y: 13,
169
197
  width: 7,
@@ -174,28 +202,31 @@ export const SUITE_GLYPHS: Record<SuiteIconName, SuiteGlyph> = {
174
202
  },
175
203
  ],
176
204
  },
177
- 'bell-wave': {
178
- description: 'Notifications — bell with sound waves',
205
+ "bell-wave": {
206
+ description: "Notifications — bell with sound waves",
179
207
  elements: [
180
- { kind: 'path', d: 'M7 9.2a5 5 0 0 1 10 0c0 5.3 2.4 7.3 2.4 7.3H4.6S7 14.5 7 9.2' },
181
- { kind: 'path', d: 'M10.7 19.3a1.5 1.5 0 0 0 2.6 0' },
182
- { kind: 'path', d: 'M17.9 6.4a5.5 5.5 0 0 1 1.4 3.7', accent: true },
183
- { kind: 'path', d: 'M15.7 4a9.4 9.4 0 0 1 2.4 6', accent: true },
208
+ {
209
+ kind: "path",
210
+ d: "M7 9.2a5 5 0 0 1 10 0c0 5.3 2.4 7.3 2.4 7.3H4.6S7 14.5 7 9.2",
211
+ },
212
+ { kind: "path", d: "M10.7 19.3a1.5 1.5 0 0 0 2.6 0" },
213
+ { kind: "path", d: "M17.9 6.4a5.5 5.5 0 0 1 1.4 3.7", accent: true },
214
+ { kind: "path", d: "M15.7 4a9.4 9.4 0 0 1 2.4 6", accent: true },
184
215
  ],
185
216
  },
186
- 'today-sun': {
187
- description: 'Today — sun with slowly turning rays (motion-safe)',
217
+ "today-sun": {
218
+ description: "Today — sun with slowly turning rays (motion-safe)",
188
219
  animatedAccent: true,
189
220
  elements: [
190
- { kind: 'circle', cx: 12, cy: 12, r: 4 },
191
- { kind: 'path', d: 'M12 2.8v2.4', accent: true },
192
- { kind: 'path', d: 'M12 18.8v2.4', accent: true },
193
- { kind: 'path', d: 'M2.8 12h2.4', accent: true },
194
- { kind: 'path', d: 'M18.8 12h2.4', accent: true },
195
- { kind: 'path', d: 'M5.5 5.5l1.7 1.7', accent: true },
196
- { kind: 'path', d: 'M16.8 16.8l1.7 1.7', accent: true },
197
- { kind: 'path', d: 'M5.5 18.5l1.7-1.7', accent: true },
198
- { kind: 'path', d: 'M16.8 7.2l1.7-1.7', accent: true },
221
+ { kind: "circle", cx: 12, cy: 12, r: 4 },
222
+ { kind: "path", d: "M12 2.8v2.4", accent: true },
223
+ { kind: "path", d: "M12 18.8v2.4", accent: true },
224
+ { kind: "path", d: "M2.8 12h2.4", accent: true },
225
+ { kind: "path", d: "M18.8 12h2.4", accent: true },
226
+ { kind: "path", d: "M5.5 5.5l1.7 1.7", accent: true },
227
+ { kind: "path", d: "M16.8 16.8l1.7 1.7", accent: true },
228
+ { kind: "path", d: "M5.5 18.5l1.7-1.7", accent: true },
229
+ { kind: "path", d: "M16.8 7.2l1.7-1.7", accent: true },
199
230
  ],
200
231
  },
201
232
  };
@@ -16,7 +16,7 @@ export interface SuiteAppDefinition {
16
16
  slug: SuiteAppSlug;
17
17
  name: string;
18
18
  description: string;
19
- /** Public asset path — every app hosts all four icons. */
19
+ /** Public asset path — every app hosts its suite icon. */
20
20
  icon: string;
21
21
  /** Landing route used after cross-app SSO redirect. */
22
22
  landing: string;
@@ -1,8 +1,4 @@
1
- import type {
2
- ButtonHTMLAttributes,
3
- ComponentType,
4
- ReactNode,
5
- } from 'react';
1
+ import type { ButtonHTMLAttributes, ComponentType, ReactNode } from "react";
6
2
 
7
3
  /**
8
4
  * Structural types for theme primitives each app injects from
@@ -12,10 +8,12 @@ import type {
12
8
 
13
9
  export interface SuiteDropdownMenuProps {
14
10
  trigger: ReactNode;
11
+ triggerId?: string;
12
+ triggerDataTest?: string;
15
13
  children: ReactNode;
16
- 'aria-label': string;
17
- align?: 'start' | 'center' | 'end';
18
- side?: 'top' | 'right' | 'bottom' | 'left';
14
+ "aria-label": string;
15
+ align?: "start" | "center" | "end";
16
+ side?: "top" | "right" | "bottom" | "left";
19
17
  sideOffset?: number;
20
18
  className?: string;
21
19
  panelClassName?: string;
@@ -23,8 +21,7 @@ export interface SuiteDropdownMenuProps {
23
21
  onOpenChange?: (open: boolean) => void;
24
22
  }
25
23
 
26
- export type SuiteDropdownMenuComponent =
27
- ComponentType<SuiteDropdownMenuProps>;
24
+ export type SuiteDropdownMenuComponent = ComponentType<SuiteDropdownMenuProps>;
28
25
 
29
26
  export type SuiteDropdownItemComponent = ComponentType<
30
27
  ButtonHTMLAttributes<HTMLButtonElement>
@@ -44,11 +41,15 @@ export type SuiteCommandPaletteComponent = ComponentType<{
44
41
  onClose: () => void;
45
42
  placeholder?: string;
46
43
  className?: string;
44
+ id?: string;
45
+ dataTest?: string;
46
+ inputId?: string;
47
+ inputDataTest?: string;
47
48
  }>;
48
49
 
49
50
  /** App-branded spinner (each app has its own AppSpinner design). */
50
51
  export type SuiteSpinnerComponent = ComponentType<{
51
- size?: 'sm' | 'md' | 'lg';
52
+ size?: "sm" | "md" | "lg";
52
53
  className?: string;
53
54
  label?: string;
54
55
  }>;
@@ -27,8 +27,12 @@ function snapWidth(width: number): number {
27
27
  * Shared suite sidebar resizing: drag below the threshold to collapse,
28
28
  * drag the rail edge out to expand, and double-click to reset.
29
29
  */
30
- export function useSidebarWidth(storageKey: string) {
31
- const [width, setWidth] = useState(SIDEBAR_DEFAULT_WIDTH);
30
+ export function useSidebarWidth(
31
+ storageKey: string,
32
+ defaultWidth = SIDEBAR_DEFAULT_WIDTH,
33
+ ) {
34
+ const fallbackWidth = snapWidth(clampWidth(defaultWidth));
35
+ const [width, setWidth] = useState(fallbackWidth);
32
36
  const [narrowViewport, setNarrowViewport] = useState(false);
33
37
  const collapsed = narrowViewport || width <= SIDEBAR_COLLAPSE_THRESHOLD;
34
38
 
@@ -71,19 +75,19 @@ export function useSidebarWidth(storageKey: string) {
71
75
  const next = value
72
76
  ? SIDEBAR_RAIL_WIDTH
73
77
  : current <= SIDEBAR_COLLAPSE_THRESHOLD
74
- ? SIDEBAR_DEFAULT_WIDTH
78
+ ? fallbackWidth
75
79
  : snapWidth(current);
76
80
  persist(next);
77
81
  return next;
78
82
  });
79
83
  },
80
- [persist],
84
+ [fallbackWidth, persist],
81
85
  );
82
86
 
83
87
  const resetWidth = useCallback(() => {
84
- setWidth(SIDEBAR_DEFAULT_WIDTH);
85
- persist(SIDEBAR_DEFAULT_WIDTH);
86
- }, [persist]);
88
+ setWidth(fallbackWidth);
89
+ persist(fallbackWidth);
90
+ }, [fallbackWidth, persist]);
87
91
 
88
92
  const startResize = useCallback(
89
93
  (event: ReactPointerEvent<HTMLDivElement>) => {
@@ -110,5 +114,16 @@ export function useSidebarWidth(storageKey: string) {
110
114
  [persist, width],
111
115
  );
112
116
 
113
- return { width, collapsed, setCollapsed, resetWidth, startResize };
117
+ const resizeBy = useCallback(
118
+ (delta: number) => {
119
+ setWidth((current) => {
120
+ const next = clampWidth(current + delta);
121
+ persist(next);
122
+ return next;
123
+ });
124
+ },
125
+ [persist],
126
+ );
127
+
128
+ return { width, collapsed, setCollapsed, resetWidth, startResize, resizeBy };
114
129
  }
@@ -1,9 +0,0 @@
1
- "use client";
2
-
3
- export { TimeSeriesLineChart, BarChart, PieChart, Sparkline, MetricCard, FunnelChart } from "./quill";
4
- export type { TimeSeriesLineChartProps, TimeSeriesLineChartSeries } from "./quill";
5
- export type { BarChartProps, BarChartSeries } from "./quill";
6
- export type { PieChartProps, PieChartSlice } from "./quill";
7
- export type { SparklineProps } from "./quill";
8
- export type { MetricCardProps } from "./quill";
9
- export type { FunnelChartProps, FunnelStep } from "./quill/FunnelChart";
@@ -1,85 +0,0 @@
1
- "use client";
2
-
3
- import { useMemo } from "react";
4
- import { useChartTokens } from "@/lib/chart/use-ph-chart-tokens";
5
- import { cn } from "@/lib/cn";
6
-
7
- export interface BarChartSeries {
8
- key: string;
9
- label: string;
10
- data: number[];
11
- color?: string;
12
- }
13
-
14
- export interface BarChartProps {
15
- series: BarChartSeries[];
16
- labels: string[];
17
- className?: string;
18
- height?: number;
19
- }
20
-
21
- export function BarChart({ series, labels, className, height = 280 }: BarChartProps) {
22
- const tokens = useChartTokens();
23
- const groupCount = labels.length;
24
- const barCount = series.length;
25
-
26
- const { maxVal, chartData } = useMemo(() => {
27
- let mx = 0;
28
- const flat = series.flatMap((s) => s.data);
29
- for (const v of flat) if (v > mx) mx = v;
30
- return {
31
- maxVal: mx || 1,
32
- chartData: series.map((s, i) => ({
33
- label: s.label,
34
- color: s.color ?? tokens.series[i % tokens.series.length],
35
- values: s.data,
36
- })),
37
- };
38
- }, [series, tokens.series]);
39
-
40
- const padding = { top: 16, right: 8, bottom: 24, left: 8 };
41
- const innerW = 400 - padding.left - padding.right;
42
- const innerH = height - padding.top - padding.bottom;
43
- const groupW = innerW / groupCount;
44
- const barW = Math.max(4, (groupW * 0.7) / barCount);
45
- const gap = groupW * 0.15;
46
-
47
- return (
48
- <div className={cn("relative w-full", className)} style={{ height }}>
49
- <svg viewBox={`0 0 400 ${height}`} className="h-full w-full" preserveAspectRatio="xMidYMid meet" suppressHydrationWarning>
50
- {chartData.map((bar, bi) =>
51
- bar.values.map((v, li) => {
52
- const x = padding.left + li * groupW + gap / 2 + bi * barW;
53
- const barH = (v / maxVal) * (innerH - 4);
54
- const y = padding.top + innerH - barH;
55
- return (
56
- <rect
57
- key={`${bi}-${li}`}
58
- x={x}
59
- y={y}
60
- width={barW}
61
- height={barH}
62
- fill={bar.color}
63
- rx={2}
64
- >
65
- <title>{`${bar.label}: ${v}`}</title>
66
- </rect>
67
- );
68
- })
69
- )}
70
- {labels.map((l, i) => (
71
- <text
72
- key={l}
73
- x={padding.left + i * groupW + groupW / 2}
74
- y={height - 4}
75
- textAnchor="middle"
76
- className="fill-ph-mutedtext"
77
- fontSize="10"
78
- >
79
- {l}
80
- </text>
81
- ))}
82
- </svg>
83
- </div>
84
- );
85
- }
@@ -1,49 +0,0 @@
1
- "use client";
2
-
3
- import { useMemo } from "react";
4
- import { useChartTokens } from "@/lib/chart/use-ph-chart-tokens";
5
- import { cn } from "@/lib/cn";
6
-
7
- export interface FunnelStep {
8
- label: string;
9
- count: number;
10
- }
11
-
12
- export interface FunnelChartProps {
13
- steps: FunnelStep[];
14
- className?: string;
15
- }
16
-
17
- export function FunnelChart({ steps, className }: FunnelChartProps) {
18
- const tokens = useChartTokens();
19
-
20
- const maxCount = useMemo(() => Math.max(...steps.map((s) => s.count)), [steps]);
21
-
22
- return (
23
- <div className={cn("space-y-2", className)} suppressHydrationWarning>
24
- {steps.map((step, i) => {
25
- const pct = (step.count / maxCount) * 100;
26
- const prev = i > 0 ? steps[i - 1].count : step.count;
27
- const dropoff = prev > 0 ? ((prev - step.count) / prev) * 100 : 0;
28
-
29
- return (
30
- <div key={step.label} className="space-y-1">
31
- <div className="flex items-center justify-between text-sm">
32
- <span className="font-medium text-ph-ink">{step.label}</span>
33
- <span className="tabular-nums text-ph-subtle">{step.count.toLocaleString()}</span>
34
- </div>
35
- <div className="relative h-8 overflow-hidden rounded-md" style={{ background: tokens.muted }}>
36
- <div
37
- className="h-full rounded-md transition-all duration-500"
38
- style={{ width: `${pct}%`, background: i === 0 ? tokens.series[0] : tokens.series[i % tokens.series.length] }}
39
- />
40
- </div>
41
- {i > 0 && dropoff > 0 && (
42
- <p className="text-xs text-ph-danger">-{dropoff.toFixed(1)}% dropoff</p>
43
- )}
44
- </div>
45
- );
46
- })}
47
- </div>
48
- );
49
- }
@@ -1,27 +0,0 @@
1
- "use client";
2
-
3
- import type { ReactNode } from "react";
4
- import { cn } from "@/lib/cn";
5
-
6
- export interface InsightShellProps {
7
- title: string;
8
- subtitle?: string;
9
- ribbonColor?: string;
10
- children: ReactNode;
11
- className?: string;
12
- }
13
-
14
- export function InsightShell({ title, subtitle, ribbonColor, children, className }: InsightShellProps) {
15
- return (
16
- <div className={cn("ph-insight-shell", className)}>
17
- <div className="flex items-start gap-3 px-4 pt-4">
18
- {ribbonColor && <div className="ph-insight-shell__ribbon" style={{ background: ribbonColor }} />}
19
- <div className="min-w-0 flex-1">
20
- <h3 className="text-sm font-semibold text-ph-ink truncate">{title}</h3>
21
- {subtitle && <p className="text-xs text-ph-mutedtext mt-0.5">{subtitle}</p>}
22
- </div>
23
- </div>
24
- <div className="p-4 pt-3">{children}</div>
25
- </div>
26
- );
27
- }
@@ -1,44 +0,0 @@
1
- "use client";
2
-
3
- import type { ReactNode } from "react";
4
- import { cn } from "@/lib/cn";
5
- import { Sparkline } from "@/components/charts/quill/Sparkline";
6
- import { Badge } from "@/components/ui/Badge";
7
-
8
- export interface MetricCardProps {
9
- value: string | number;
10
- label: string;
11
- change?: { value: number; positive?: boolean };
12
- sparklineData?: number[];
13
- icon?: ReactNode;
14
- className?: string;
15
- }
16
-
17
- export function MetricCard({
18
- value,
19
- label,
20
- change,
21
- sparklineData,
22
- icon,
23
- className,
24
- }: MetricCardProps) {
25
- return (
26
- <div className={cn("ph-metric", className)}>
27
- <div className="ph-metric__header">
28
- {icon && <span className="ph-metric__icon">{icon}</span>}
29
- <span className="ph-metric__label">{label}</span>
30
- {change && (
31
- <Badge variant={change.positive ?? change.value >= 0 ? "success" : "danger"}>
32
- {change.value >= 0 ? "+" : ""}{change.value}%
33
- </Badge>
34
- )}
35
- </div>
36
- <div className="ph-metric__value">{value}</div>
37
- {sparklineData && sparklineData.length > 0 && (
38
- <div className="mt-2">
39
- <Sparkline data={sparklineData} />
40
- </div>
41
- )}
42
- </div>
43
- );
44
- }