@syscore/ui-library 1.27.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,344 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import { Stepper } from "../../components/ui/stepper";
4
+ import { Button } from "../../components/ui/button";
5
+
6
+ const meta = {
7
+ title: "UI/Stepper",
8
+ component: Stepper,
9
+ tags: ["autodocs"],
10
+ parameters: {
11
+ layout: "padded",
12
+ docs: {
13
+ description: {
14
+ component: [
15
+ "A step indicator for multi-step flows — registration wizards, certification processes, onboarding.",
16
+ "",
17
+ "**Import:**",
18
+ "```tsx",
19
+ `import { Stepper } from "@syscore/ui-library"`,
20
+ "```",
21
+ "",
22
+ "**Key props:**",
23
+ "",
24
+ "| Prop | Type | Default | Effect |",
25
+ "|------|------|---------|--------|",
26
+ "| `steps` | `{ title, description? }[]` | — | The step definitions |",
27
+ "| `currentStep` | number | — | 0-indexed active step |",
28
+ "| `orientation` | `\"horizontal\"` \\| `\"vertical\"` | `\"horizontal\"` | Layout direction |",
29
+ "| `variant` | `\"default\"` \\| `\"outlined\"` | `\"default\"` | Filled vs outlined indicators |",
30
+ "| `size` | `\"sm\"` \\| `\"md\"` \\| `\"lg\"` | `\"md\"` | Size of indicators and labels |",
31
+ "| `onStepClick` | `(index) => void` | — | Makes completed steps clickable |",
32
+ ].join("\n"),
33
+ },
34
+ },
35
+ },
36
+ args: {
37
+ steps: [
38
+ { title: "Project details", description: "Basic information" },
39
+ { title: "Documents", description: "Upload required files" },
40
+ { title: "Review", description: "Check your submission" },
41
+ { title: "Submit", description: "Complete registration" },
42
+ ],
43
+ currentStep: 1,
44
+ },
45
+ } satisfies Meta<typeof Stepper>;
46
+
47
+ export default meta;
48
+ type Story = StoryObj<typeof meta>;
49
+
50
+ const REGISTRATION_STEPS = [
51
+ { title: "Project details", description: "Basic information" },
52
+ { title: "Documents", description: "Upload required files" },
53
+ { title: "Review", description: "Check your submission" },
54
+ { title: "Submit", description: "Complete registration" },
55
+ ];
56
+
57
+ const SIMPLE_STEPS = [
58
+ { title: "Account" },
59
+ { title: "Profile" },
60
+ { title: "Preferences" },
61
+ { title: "Done" },
62
+ ];
63
+
64
+ // ---------------------------------------------------------------------------
65
+
66
+ export const Default: Story = {
67
+ parameters: {
68
+ docs: {
69
+ source: {
70
+ code: `import { Stepper } from "@syscore/ui-library"
71
+
72
+ const steps = [
73
+ { title: "Project details", description: "Basic information" },
74
+ { title: "Documents", description: "Upload required files" },
75
+ { title: "Review", description: "Check your submission" },
76
+ { title: "Submit", description: "Complete registration" },
77
+ ]
78
+
79
+ <Stepper steps={steps} currentStep={1} />`,
80
+ },
81
+ },
82
+ },
83
+ render: () => (
84
+ <Stepper steps={REGISTRATION_STEPS} currentStep={1} />
85
+ ),
86
+ };
87
+
88
+ export const AllStepsState: Story = {
89
+ name: "Step states",
90
+ parameters: {
91
+ docs: {
92
+ source: {
93
+ code: `import { Stepper } from "@syscore/ui-library"
94
+
95
+ // currentStep=0 → all upcoming
96
+ <Stepper steps={steps} currentStep={0} />
97
+
98
+ // currentStep=2 → 2 complete, 1 current, 1 upcoming
99
+ <Stepper steps={steps} currentStep={2} />
100
+
101
+ // currentStep=4 → all complete
102
+ <Stepper steps={steps} currentStep={4} />`,
103
+ },
104
+ },
105
+ },
106
+ render: () => (
107
+ <div className="flex flex-col gap-10 w-full">
108
+ <div className="flex flex-col gap-2">
109
+ <span className="text-xs text-gray-400 font-medium">Not started (currentStep=0)</span>
110
+ <Stepper steps={SIMPLE_STEPS} currentStep={0} />
111
+ </div>
112
+ <div className="flex flex-col gap-2">
113
+ <span className="text-xs text-gray-400 font-medium">In progress (currentStep=2)</span>
114
+ <Stepper steps={SIMPLE_STEPS} currentStep={2} />
115
+ </div>
116
+ <div className="flex flex-col gap-2">
117
+ <span className="text-xs text-gray-400 font-medium">Complete (currentStep=4)</span>
118
+ <Stepper steps={SIMPLE_STEPS} currentStep={4} />
119
+ </div>
120
+ </div>
121
+ ),
122
+ };
123
+
124
+ export const Vertical: Story = {
125
+ parameters: {
126
+ docs: {
127
+ source: {
128
+ code: `import { Stepper } from "@syscore/ui-library"
129
+
130
+ <Stepper
131
+ steps={steps}
132
+ currentStep={1}
133
+ orientation="vertical"
134
+ />`,
135
+ },
136
+ },
137
+ },
138
+ render: () => (
139
+ <div className="max-w-xs">
140
+ <Stepper
141
+ steps={REGISTRATION_STEPS}
142
+ currentStep={1}
143
+ orientation="vertical"
144
+ />
145
+ </div>
146
+ ),
147
+ };
148
+
149
+ export const Outlined: Story = {
150
+ parameters: {
151
+ docs: {
152
+ source: {
153
+ code: `import { Stepper } from "@syscore/ui-library"
154
+
155
+ <Stepper steps={steps} currentStep={2} variant="outlined" />`,
156
+ },
157
+ },
158
+ },
159
+ render: () => (
160
+ <Stepper steps={REGISTRATION_STEPS} currentStep={2} variant="outlined" />
161
+ ),
162
+ };
163
+
164
+ export const Sizes: Story = {
165
+ parameters: {
166
+ docs: {
167
+ source: {
168
+ code: `import { Stepper } from "@syscore/ui-library"
169
+
170
+ <Stepper steps={steps} currentStep={1} size="sm" />
171
+ <Stepper steps={steps} currentStep={1} size="md" />
172
+ <Stepper steps={steps} currentStep={1} size="lg" />`,
173
+ },
174
+ },
175
+ },
176
+ render: () => (
177
+ <div className="flex flex-col gap-10 w-full">
178
+ {(["sm", "md", "lg"] as const).map((size) => (
179
+ <div key={size} className="flex flex-col gap-2">
180
+ <span className="text-xs text-gray-400 font-medium">size="{size}"</span>
181
+ <Stepper steps={SIMPLE_STEPS} currentStep={1} size={size} />
182
+ </div>
183
+ ))}
184
+ </div>
185
+ ),
186
+ };
187
+
188
+ export const Clickable: Story = {
189
+ name: "Clickable steps",
190
+ parameters: {
191
+ docs: {
192
+ source: {
193
+ code: `import { Stepper } from "@syscore/ui-library"
194
+ import { useState } from "react"
195
+
196
+ const [currentStep, setCurrentStep] = useState(2)
197
+
198
+ {/* Completed steps are clickable — lets users go back */}
199
+ <Stepper
200
+ steps={steps}
201
+ currentStep={currentStep}
202
+ onStepClick={setCurrentStep}
203
+ />`,
204
+ },
205
+ },
206
+ },
207
+ render: () => {
208
+ const [currentStep, setCurrentStep] = useState(2);
209
+ return (
210
+ <div className="flex flex-col gap-6 w-full">
211
+ <Stepper
212
+ steps={REGISTRATION_STEPS}
213
+ currentStep={currentStep}
214
+ onStepClick={setCurrentStep}
215
+ />
216
+ <p className="text-sm text-gray-500 text-center">
217
+ Click a completed step (green) to go back
218
+ </p>
219
+ </div>
220
+ );
221
+ },
222
+ };
223
+
224
+ export const WithNavigation: Story = {
225
+ name: "With next / back buttons",
226
+ parameters: {
227
+ docs: {
228
+ source: {
229
+ code: `import { Stepper } from "@syscore/ui-library"
230
+ import { Button, Stack } from "@syscore/ui-library"
231
+ import { useState } from "react"
232
+
233
+ const [currentStep, setCurrentStep] = useState(0)
234
+
235
+ <Stack gap={6}>
236
+ <Stepper steps={steps} currentStep={currentStep} />
237
+
238
+ <Stack direction="horizontal" justify="between">
239
+ <Button
240
+ variant="secondary-light"
241
+ size="large"
242
+ disabled={currentStep === 0}
243
+ onClick={() => setCurrentStep(s => s - 1)}
244
+ >
245
+ Back
246
+ </Button>
247
+ <Button
248
+ variant="primary-dark"
249
+ size="large"
250
+ disabled={currentStep === steps.length}
251
+ onClick={() => setCurrentStep(s => s + 1)}
252
+ >
253
+ {currentStep === steps.length - 1 ? "Submit" : "Next"}
254
+ </Button>
255
+ </Stack>
256
+ </Stack>`,
257
+ },
258
+ },
259
+ },
260
+ render: () => {
261
+ const [currentStep, setCurrentStep] = useState(0);
262
+ const steps = REGISTRATION_STEPS;
263
+
264
+ return (
265
+ <div className="w-full max-w-2xl border border-gray-100 rounded-xl p-8 flex flex-col gap-8">
266
+ <Stepper steps={steps} currentStep={currentStep} />
267
+
268
+ {/* Step content placeholder */}
269
+ <div className="min-h-[120px] flex items-center justify-center rounded-lg border border-dashed border-gray-200 bg-gray-50">
270
+ <span className="text-sm text-gray-400">
271
+ {currentStep < steps.length
272
+ ? `${steps[currentStep].title} — step content goes here`
273
+ : "All steps complete!"}
274
+ </span>
275
+ </div>
276
+
277
+ {/* Navigation */}
278
+ <div className="flex justify-between">
279
+ <Button
280
+ variant="secondary-light"
281
+ size="large"
282
+ disabled={currentStep === 0}
283
+ onClick={() => setCurrentStep((s) => s - 1)}
284
+ >
285
+ Back
286
+ </Button>
287
+ <Button
288
+ variant="primary-dark"
289
+ size="large"
290
+ disabled={currentStep >= steps.length}
291
+ onClick={() => setCurrentStep((s) => s + 1)}
292
+ >
293
+ {currentStep === steps.length - 1 ? "Submit" : "Next"}
294
+ </Button>
295
+ </div>
296
+ </div>
297
+ );
298
+ },
299
+ };
300
+
301
+ export const CertificationWizard: Story = {
302
+ name: "WELL certification wizard",
303
+ parameters: {
304
+ docs: {
305
+ source: {
306
+ code: `import { Stepper } from "@syscore/ui-library"
307
+
308
+ const certificationSteps = [
309
+ { title: "Registration", description: "Project & team details" },
310
+ { title: "Documentation", description: "Upload project files" },
311
+ { title: "Assessment", description: "WELL scorecard review" },
312
+ { title: "Verification", description: "On-site performance tests" },
313
+ { title: "Certification", description: "Award & seal issuance" },
314
+ ]
315
+
316
+ <Stepper
317
+ steps={certificationSteps}
318
+ currentStep={2}
319
+ orientation="vertical"
320
+ size="lg"
321
+ />`,
322
+ },
323
+ },
324
+ },
325
+ render: () => {
326
+ const certificationSteps = [
327
+ { title: "Registration", description: "Project & team details" },
328
+ { title: "Documentation", description: "Upload project files" },
329
+ { title: "Assessment", description: "WELL scorecard review" },
330
+ { title: "Verification", description: "On-site performance tests" },
331
+ { title: "Certification", description: "Award & seal issuance" },
332
+ ];
333
+ return (
334
+ <div className="max-w-xs border border-gray-100 rounded-xl p-6">
335
+ <Stepper
336
+ steps={certificationSteps}
337
+ currentStep={2}
338
+ orientation="vertical"
339
+ size="lg"
340
+ />
341
+ </div>
342
+ );
343
+ },
344
+ };
@@ -0,0 +1,179 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import {
3
+ SystemBar,
4
+ SystemBarSpacer,
5
+ SystemBarIcon,
6
+ SystemBarAction,
7
+ } from "../../components/ui/system-bar";
8
+
9
+ // ─── Inline SVG icons ─────────────────────────────────────────────────────────
10
+
11
+ function IconWifi({ strength = 4 }: { strength?: 1 | 2 | 3 | 4 }) {
12
+ // Simple wifi icon — bars filled based on strength
13
+ const bars = [1, 2, 3, 4];
14
+ return (
15
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
16
+ {strength >= 1 && <path d="M1 9l2 2c5-5 13-5 18 0l2-2C16.93 2.93 7.08 2.93 1 9z" opacity={strength === 1 ? 1 : 0.4} />}
17
+ {strength >= 2 && <path d="M5 13l2 2a7 7 0 0 1 10 0l2-2C15.14 9.14 8.87 9.14 5 13z" opacity={strength === 2 ? 1 : 0.6} />}
18
+ {strength >= 3 && <path d="M9 17l3 3 3-3a4.237 4.237 0 0 0-6 0z" />}
19
+ <circle cx="12" cy="20" r="1" />
20
+ </svg>
21
+ );
22
+ }
23
+
24
+ function IconSignal() {
25
+ return (
26
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
27
+ <rect x="2" y="16" width="3" height="6" rx="0.5" />
28
+ <rect x="7" y="12" width="3" height="10" rx="0.5" />
29
+ <rect x="12" y="8" width="3" height="14" rx="0.5" />
30
+ <rect x="17" y="4" width="3" height="18" rx="0.5" opacity="0.3" />
31
+ </svg>
32
+ );
33
+ }
34
+
35
+ function IconBattery() {
36
+ return (
37
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
38
+ <rect x="2" y="7" width="18" height="11" rx="2" stroke="currentColor" strokeWidth="1.5" fill="none" />
39
+ <rect x="3.5" y="8.5" width="12" height="8" rx="1" />
40
+ <path d="M20 10h2v5h-2z" />
41
+ </svg>
42
+ );
43
+ }
44
+
45
+ function IconMessage() {
46
+ return (
47
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
48
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
49
+ </svg>
50
+ );
51
+ }
52
+
53
+ function IconMinus() {
54
+ return (
55
+ <svg width="12" height="12" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" fill="none" aria-hidden="true">
56
+ <line x1="5" y1="12" x2="19" y2="12" />
57
+ </svg>
58
+ );
59
+ }
60
+
61
+ function IconMaximize() {
62
+ return (
63
+ <svg width="12" height="12" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" fill="none" aria-hidden="true">
64
+ <rect x="3" y="3" width="18" height="18" rx="1" />
65
+ </svg>
66
+ );
67
+ }
68
+
69
+ function IconClose() {
70
+ return (
71
+ <svg width="12" height="12" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" fill="none" aria-hidden="true">
72
+ <line x1="18" y1="6" x2="6" y2="18" />
73
+ <line x1="6" y1="6" x2="18" y2="18" />
74
+ </svg>
75
+ );
76
+ }
77
+
78
+ // ─────────────────────────────────────────────────────────────────────────────
79
+
80
+ const meta = {
81
+ title: "UI/SystemBar",
82
+ component: SystemBar,
83
+ tags: ["autodocs"],
84
+ parameters: {
85
+ layout: "fullscreen",
86
+ },
87
+ argTypes: {
88
+ color: { control: "color" },
89
+ window: { control: "boolean" },
90
+ },
91
+ } satisfies Meta<typeof SystemBar>;
92
+
93
+ export default meta;
94
+
95
+ type Story = StoryObj<typeof meta>;
96
+
97
+ // ─── Default ─────────────────────────────────────────────────────────────────
98
+ // Android-style status bar with wifi, signal, battery and a clock.
99
+
100
+ export const Default: Story = {
101
+ render: () => (
102
+ <SystemBar>
103
+ <SystemBarIcon>
104
+ <IconWifi strength={4} />
105
+ </SystemBarIcon>
106
+ <SystemBarIcon>
107
+ <IconSignal />
108
+ </SystemBarIcon>
109
+ <SystemBarIcon>
110
+ <IconBattery />
111
+ </SystemBarIcon>
112
+ <SystemBarSpacer />
113
+ <span className="system-bar-time">3:13 PM</span>
114
+ </SystemBar>
115
+ ),
116
+ };
117
+
118
+ // ─── Colors ───────────────────────────────────────────────────────────────────
119
+ // Three bars with different background colors — text/icons auto-switch to white.
120
+
121
+ export const Colors: Story = {
122
+ render: () => (
123
+ <div style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
124
+ {/* Primary blue */}
125
+ <SystemBar color="#0284c7">
126
+ <SystemBarIcon><IconWifi strength={4} /></SystemBarIcon>
127
+ <SystemBarIcon><IconSignal /></SystemBarIcon>
128
+ <SystemBarIcon><IconBattery /></SystemBarIcon>
129
+ <SystemBarSpacer />
130
+ <span className="system-bar-time">08:30</span>
131
+ </SystemBar>
132
+
133
+ {/* Red */}
134
+ <SystemBar color="#f87171">
135
+ <SystemBarIcon><IconWifi strength={2} /></SystemBarIcon>
136
+ <SystemBarIcon><IconSignal /></SystemBarIcon>
137
+ <SystemBarIcon><IconBattery /></SystemBarIcon>
138
+ <SystemBarSpacer />
139
+ <span className="system-bar-time">18:30</span>
140
+ </SystemBar>
141
+
142
+ {/* Indigo */}
143
+ <SystemBar color="#3730a3">
144
+ <SystemBarIcon><IconWifi strength={3} /></SystemBarIcon>
145
+ <SystemBarIcon><IconSignal /></SystemBarIcon>
146
+ <SystemBarIcon><IconBattery /></SystemBarIcon>
147
+ <SystemBarSpacer />
148
+ <span className="system-bar-time">13:24</span>
149
+ </SystemBar>
150
+ </div>
151
+ ),
152
+ };
153
+
154
+ // ─── Window ───────────────────────────────────────────────────────────────────
155
+ // Desktop-style title bar: message on the left, window controls on the right.
156
+
157
+ export const Window: Story = {
158
+ render: () => (
159
+ <SystemBar window>
160
+ <SystemBarIcon>
161
+ <IconMessage />
162
+ </SystemBarIcon>
163
+ <span className="system-bar-label">10 unread messages</span>
164
+ <SystemBarSpacer />
165
+ <SystemBarAction aria-label="Minimise window">
166
+ <IconMinus />
167
+ </SystemBarAction>
168
+ <SystemBarAction aria-label="Maximise window">
169
+ <IconMaximize />
170
+ </SystemBarAction>
171
+ <SystemBarAction
172
+ aria-label="Close window"
173
+ className="system-bar-action--close"
174
+ >
175
+ <IconClose />
176
+ </SystemBarAction>
177
+ </SystemBar>
178
+ ),
179
+ };