@fragments-sdk/ui 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fragments.json +1 -1
- package/package.json +9 -4
- package/src/components/Accordion/Accordion.fragment.tsx +186 -0
- package/src/components/Accordion/Accordion.module.scss +111 -0
- package/src/components/Accordion/index.tsx +271 -0
- package/src/components/Alert/Alert.fragment.tsx +66 -41
- package/src/components/Alert/Alert.module.scss +31 -21
- package/src/components/Alert/index.tsx +202 -73
- package/src/components/AppShell/AppShell.fragment.tsx +315 -0
- package/src/components/AppShell/AppShell.module.scss +213 -0
- package/src/components/AppShell/index.tsx +398 -0
- package/src/components/Avatar/index.tsx +8 -9
- package/src/components/Badge/Badge.module.scss +16 -10
- package/src/components/Badge/index.tsx +20 -6
- package/src/components/Box/Box.fragment.tsx +168 -0
- package/src/components/Box/Box.module.scss +84 -0
- package/src/components/Box/index.tsx +78 -0
- package/src/components/Button/Button.module.scss +42 -0
- package/src/components/Button/index.tsx +67 -33
- package/src/components/ButtonGroup/ButtonGroup.module.scss +37 -0
- package/src/components/ButtonGroup/index.tsx +40 -0
- package/src/components/Card/Card.fragment.tsx +51 -25
- package/src/components/Card/Card.module.scss +52 -5
- package/src/components/Card/index.tsx +154 -53
- package/src/components/Checkbox/Checkbox.module.scss +4 -4
- package/src/components/Checkbox/index.tsx +3 -4
- package/src/components/CodeBlock/CodeBlock.fragment.tsx +201 -0
- package/src/components/CodeBlock/CodeBlock.module.scss +224 -0
- package/src/components/CodeBlock/index.tsx +385 -0
- package/src/components/ColorChip/ColorChip.module.scss +165 -0
- package/src/components/ColorChip/index.tsx +157 -0
- package/src/components/ColorPicker/ColorPicker.module.scss +109 -0
- package/src/components/ColorPicker/index.tsx +107 -0
- package/src/components/Dialog/Dialog.fragment.tsx +9 -0
- package/src/components/Dialog/Dialog.module.scss +26 -7
- package/src/components/Dialog/index.tsx +12 -15
- package/src/components/EmptyState/EmptyState.fragment.tsx +54 -71
- package/src/components/EmptyState/EmptyState.module.scss +9 -9
- package/src/components/EmptyState/index.tsx +104 -69
- package/src/components/Field/Field.fragment.tsx +165 -0
- package/src/components/Field/Field.module.scss +31 -0
- package/src/components/Field/index.tsx +143 -0
- package/src/components/Fieldset/Fieldset.fragment.tsx +166 -0
- package/src/components/Fieldset/Fieldset.module.scss +22 -0
- package/src/components/Fieldset/index.tsx +47 -0
- package/src/components/Form/Form.fragment.tsx +286 -0
- package/src/components/Form/Form.module.scss +8 -0
- package/src/components/Form/index.tsx +53 -0
- package/src/components/Grid/Grid.fragment.tsx +17 -17
- package/src/components/Grid/index.tsx +6 -1
- package/src/components/Header/Header.fragment.tsx +192 -0
- package/src/components/Header/Header.module.scss +209 -0
- package/src/components/Header/index.tsx +363 -0
- package/src/components/Icon/Icon.fragment.tsx +138 -0
- package/src/components/Icon/Icon.module.scss +38 -0
- package/src/components/Icon/index.tsx +58 -0
- package/src/components/Image/Image.fragment.tsx +195 -0
- package/src/components/Image/Image.module.scss +77 -0
- package/src/components/Image/index.tsx +95 -0
- package/src/components/Input/Input.module.scss +75 -2
- package/src/components/Input/index.tsx +60 -21
- package/src/components/Link/Link.fragment.tsx +132 -0
- package/src/components/Link/Link.module.scss +67 -0
- package/src/components/Link/index.tsx +57 -0
- package/src/components/List/List.fragment.tsx +152 -0
- package/src/components/List/List.module.scss +71 -0
- package/src/components/List/index.tsx +106 -0
- package/src/components/Listbox/Listbox.fragment.tsx +191 -0
- package/src/components/Listbox/Listbox.module.scss +97 -0
- package/src/components/Listbox/index.tsx +121 -0
- package/src/components/Menu/Menu.fragment.tsx +9 -0
- package/src/components/Menu/Menu.module.scss +17 -1
- package/src/components/Menu/index.tsx +3 -3
- package/src/components/Popover/Popover.fragment.tsx +9 -0
- package/src/components/Popover/Popover.module.scss +33 -10
- package/src/components/Popover/index.tsx +9 -11
- package/src/components/Progress/Progress.module.scss +11 -11
- package/src/components/Progress/index.tsx +34 -7
- package/src/components/Prompt/Prompt.fragment.tsx +231 -0
- package/src/components/Prompt/Prompt.module.scss +243 -0
- package/src/components/Prompt/index.tsx +439 -0
- package/src/components/RadioGroup/RadioGroup.module.scss +3 -3
- package/src/components/RadioGroup/index.tsx +3 -4
- package/src/components/Select/Select.fragment.tsx +9 -0
- package/src/components/Select/index.tsx +6 -7
- package/src/components/Separator/index.tsx +7 -3
- package/src/components/Sidebar/Sidebar.fragment.tsx +9 -0
- package/src/components/Sidebar/Sidebar.module.scss +72 -47
- package/src/components/Sidebar/index.tsx +5 -3
- package/src/components/Skeleton/Skeleton.fragment.tsx +5 -5
- package/src/components/Skeleton/Skeleton.module.scss +11 -0
- package/src/components/Slider/Slider.module.scss +87 -0
- package/src/components/Slider/index.tsx +88 -0
- package/src/components/Stack/Stack.module.scss +120 -0
- package/src/components/Stack/index.tsx +148 -0
- package/src/components/Table/Table.fragment.tsx +7 -0
- package/src/components/Table/Table.module.scss +57 -0
- package/src/components/Table/index.tsx +44 -6
- package/src/components/Tabs/Tabs.fragment.tsx +9 -0
- package/src/components/Tabs/Tabs.module.scss +25 -10
- package/src/components/Tabs/index.tsx +11 -8
- package/src/components/Text/Text.module.scss +82 -0
- package/src/components/Text/index.tsx +58 -0
- package/src/components/Textarea/index.tsx +3 -7
- package/src/components/Theme/Theme.fragment.tsx +128 -0
- package/src/components/Theme/ThemeToggle.module.scss +82 -0
- package/src/components/Theme/index.tsx +343 -0
- package/src/components/Toast/Toast.fragment.tsx +5 -5
- package/src/components/Toast/Toast.module.scss +16 -1
- package/src/components/Toast/index.tsx +27 -11
- package/src/components/Toggle/Toggle.module.scss +25 -10
- package/src/components/Toggle/index.tsx +12 -0
- package/src/components/ToggleGroup/ToggleGroup.module.scss +134 -0
- package/src/components/ToggleGroup/index.tsx +144 -0
- package/src/components/Tooltip/Tooltip.module.scss +4 -4
- package/src/components/Tooltip/index.tsx +4 -2
- package/src/components/VisuallyHidden/VisuallyHidden.fragment.tsx +134 -0
- package/src/components/VisuallyHidden/VisuallyHidden.module.scss +13 -0
- package/src/components/VisuallyHidden/index.tsx +29 -0
- package/src/index.ts +195 -3
- package/src/recipes/AppShell.recipe.ts +175 -0
- package/src/recipes/CardGrid.recipe.ts +6 -2
- package/src/recipes/ChatInterface.recipe.ts +87 -0
- package/src/recipes/CodeExamples.recipe.ts +66 -0
- package/src/recipes/DashboardLayout.recipe.ts +46 -12
- package/src/recipes/DashboardNav.recipe.ts +183 -0
- package/src/recipes/LoginForm.recipe.ts +8 -1
- package/src/recipes/SettingsPage.recipe.ts +37 -20
- package/src/styles/globals.scss +31 -0
- package/src/tokens/_index.scss +3 -0
- package/src/tokens/_mixins.scss +54 -1
- package/src/tokens/_variables.scss +429 -64
- package/src/utils/a11y.tsx +439 -0
|
@@ -12,18 +12,11 @@
|
|
|
12
12
|
width: var(--sidebar-width);
|
|
13
13
|
height: 100vh;
|
|
14
14
|
background-color: var(--fui-bg-primary, $fui-bg-primary);
|
|
15
|
-
border-right: 1px solid var(--fui-border, $fui-border);
|
|
16
15
|
transition: width var(--fui-transition-normal, $fui-transition-normal);
|
|
17
16
|
overflow: hidden;
|
|
18
17
|
flex-shrink: 0;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
// Right position variant
|
|
22
|
-
.positionRight {
|
|
23
|
-
border-right: none;
|
|
24
|
-
border-left: 1px solid var(--fui-border, $fui-border);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
20
|
// Desktop collapsed state
|
|
28
21
|
.collapsed {
|
|
29
22
|
width: var(--sidebar-collapsed-width);
|
|
@@ -91,10 +84,9 @@
|
|
|
91
84
|
display: flex;
|
|
92
85
|
align-items: center;
|
|
93
86
|
gap: var(--fui-space-3, $fui-space-3);
|
|
94
|
-
padding: var(--fui-space-
|
|
95
|
-
border-bottom: 1px solid var(--fui-border, $fui-border);
|
|
96
|
-
min-height: 60px;
|
|
87
|
+
padding: var(--fui-space-2, $fui-space-2) var(--fui-space-2, $fui-space-2);
|
|
97
88
|
flex-shrink: 0;
|
|
89
|
+
height: var(--appshell-header-height, 56px);
|
|
98
90
|
}
|
|
99
91
|
|
|
100
92
|
// ============================================
|
|
@@ -122,7 +114,7 @@
|
|
|
122
114
|
display: flex;
|
|
123
115
|
align-items: center;
|
|
124
116
|
justify-content: space-between;
|
|
125
|
-
padding: var(--fui-space-
|
|
117
|
+
padding: var(--fui-space-1, $fui-space-1) var(--fui-space-2, $fui-space-2);
|
|
126
118
|
}
|
|
127
119
|
|
|
128
120
|
.sectionLabel {
|
|
@@ -145,8 +137,8 @@
|
|
|
145
137
|
display: flex;
|
|
146
138
|
align-items: center;
|
|
147
139
|
justify-content: center;
|
|
148
|
-
width:
|
|
149
|
-
height:
|
|
140
|
+
width: $fui-touch-sm;
|
|
141
|
+
height: $fui-touch-sm;
|
|
150
142
|
border-radius: var(--fui-radius-sm, $fui-radius-sm);
|
|
151
143
|
color: var(--fui-text-tertiary, $fui-text-tertiary);
|
|
152
144
|
|
|
@@ -156,8 +148,8 @@
|
|
|
156
148
|
}
|
|
157
149
|
|
|
158
150
|
svg {
|
|
159
|
-
width:
|
|
160
|
-
height:
|
|
151
|
+
width: $fui-icon-md;
|
|
152
|
+
height: $fui-icon-md;
|
|
161
153
|
}
|
|
162
154
|
}
|
|
163
155
|
|
|
@@ -184,11 +176,11 @@
|
|
|
184
176
|
align-items: center;
|
|
185
177
|
gap: var(--fui-space-3, $fui-space-3);
|
|
186
178
|
width: 100%;
|
|
187
|
-
padding: var(--fui-space-
|
|
179
|
+
padding: var(--fui-space-1, $fui-space-1) var(--fui-space-2, $fui-space-2);
|
|
188
180
|
border-radius: var(--fui-radius-md, $fui-radius-md);
|
|
189
181
|
color: var(--fui-text-secondary, $fui-text-secondary);
|
|
190
182
|
text-decoration: none;
|
|
191
|
-
min-height:
|
|
183
|
+
min-height: $fui-sidebar-item-height;
|
|
192
184
|
|
|
193
185
|
&:hover:not(.itemDisabled) {
|
|
194
186
|
background-color: var(--fui-bg-hover, $fui-bg-hover);
|
|
@@ -224,14 +216,14 @@
|
|
|
224
216
|
display: flex;
|
|
225
217
|
align-items: center;
|
|
226
218
|
justify-content: center;
|
|
227
|
-
width:
|
|
228
|
-
height:
|
|
219
|
+
width: $fui-icon-lg;
|
|
220
|
+
height: $fui-icon-lg;
|
|
229
221
|
flex-shrink: 0;
|
|
230
222
|
color: var(--fui-text-tertiary, $fui-text-tertiary);
|
|
231
223
|
|
|
232
224
|
svg {
|
|
233
|
-
width:
|
|
234
|
-
height:
|
|
225
|
+
width: $fui-icon-lg;
|
|
226
|
+
height: $fui-icon-lg;
|
|
235
227
|
}
|
|
236
228
|
|
|
237
229
|
.item:hover:not(.itemDisabled) & {
|
|
@@ -258,7 +250,7 @@
|
|
|
258
250
|
background-color: var(--fui-color-accent-hover, $fui-color-accent-hover);
|
|
259
251
|
color: var(--fui-text-inverse, $fui-text-inverse);
|
|
260
252
|
border-radius: var(--fui-radius-full, $fui-radius-full);
|
|
261
|
-
min-width:
|
|
253
|
+
min-width: $fui-icon-lg;
|
|
262
254
|
line-height: 1;
|
|
263
255
|
|
|
264
256
|
.itemActive & {
|
|
@@ -271,14 +263,14 @@
|
|
|
271
263
|
display: flex;
|
|
272
264
|
align-items: center;
|
|
273
265
|
justify-content: center;
|
|
274
|
-
width:
|
|
275
|
-
height:
|
|
266
|
+
width: $fui-icon-md;
|
|
267
|
+
height: $fui-icon-md;
|
|
276
268
|
flex-shrink: 0;
|
|
277
269
|
transition: transform var(--fui-transition-fast, $fui-transition-fast);
|
|
278
270
|
|
|
279
271
|
svg {
|
|
280
|
-
width:
|
|
281
|
-
height:
|
|
272
|
+
width: $fui-icon-md;
|
|
273
|
+
height: $fui-icon-md;
|
|
282
274
|
}
|
|
283
275
|
}
|
|
284
276
|
|
|
@@ -325,12 +317,12 @@
|
|
|
325
317
|
display: flex;
|
|
326
318
|
align-items: center;
|
|
327
319
|
width: 100%;
|
|
328
|
-
padding: var(--fui-space-
|
|
320
|
+
padding: var(--fui-space-1, $fui-space-1) var(--fui-space-2, $fui-space-2);
|
|
329
321
|
border-radius: var(--fui-radius-md, $fui-radius-md);
|
|
330
322
|
color: var(--fui-text-secondary, $fui-text-secondary);
|
|
331
323
|
text-decoration: none;
|
|
332
324
|
font-size: var(--fui-font-size-sm, $fui-font-size-sm);
|
|
333
|
-
min-height:
|
|
325
|
+
min-height: $fui-sidebar-subitem-height;
|
|
334
326
|
position: relative;
|
|
335
327
|
|
|
336
328
|
// Left border indicator
|
|
@@ -340,8 +332,8 @@
|
|
|
340
332
|
left: 0;
|
|
341
333
|
top: 50%;
|
|
342
334
|
transform: translateY(-50%);
|
|
343
|
-
width:
|
|
344
|
-
height:
|
|
335
|
+
width: $fui-sidebar-indicator-width;
|
|
336
|
+
height: $fui-sidebar-indicator-height;
|
|
345
337
|
background-color: transparent;
|
|
346
338
|
border-radius: var(--fui-radius-full, $fui-radius-full);
|
|
347
339
|
transition: background-color var(--fui-transition-fast, $fui-transition-fast);
|
|
@@ -381,7 +373,6 @@
|
|
|
381
373
|
flex-direction: column;
|
|
382
374
|
gap: var(--fui-space-2, $fui-space-2);
|
|
383
375
|
padding: var(--fui-space-4, $fui-space-4);
|
|
384
|
-
border-top: 1px solid var(--fui-border, $fui-border);
|
|
385
376
|
margin-top: auto;
|
|
386
377
|
flex-shrink: 0;
|
|
387
378
|
}
|
|
@@ -397,8 +388,8 @@
|
|
|
397
388
|
display: flex;
|
|
398
389
|
align-items: center;
|
|
399
390
|
justify-content: center;
|
|
400
|
-
width:
|
|
401
|
-
height:
|
|
391
|
+
width: $fui-touch-lg;
|
|
392
|
+
height: $fui-touch-lg;
|
|
402
393
|
border-radius: var(--fui-radius-md, $fui-radius-md);
|
|
403
394
|
color: var(--fui-text-primary, $fui-text-primary);
|
|
404
395
|
|
|
@@ -407,8 +398,8 @@
|
|
|
407
398
|
}
|
|
408
399
|
|
|
409
400
|
svg {
|
|
410
|
-
width:
|
|
411
|
-
height:
|
|
401
|
+
width: $fui-icon-xl;
|
|
402
|
+
height: $fui-icon-xl;
|
|
412
403
|
}
|
|
413
404
|
}
|
|
414
405
|
|
|
@@ -440,8 +431,8 @@
|
|
|
440
431
|
display: flex;
|
|
441
432
|
align-items: center;
|
|
442
433
|
justify-content: center;
|
|
443
|
-
width:
|
|
444
|
-
height:
|
|
434
|
+
width: $fui-touch-md;
|
|
435
|
+
height: $fui-touch-md;
|
|
445
436
|
border-radius: var(--fui-radius-md, $fui-radius-md);
|
|
446
437
|
color: var(--fui-text-secondary, $fui-text-secondary);
|
|
447
438
|
margin-left: auto;
|
|
@@ -452,8 +443,8 @@
|
|
|
452
443
|
}
|
|
453
444
|
|
|
454
445
|
svg {
|
|
455
|
-
width:
|
|
456
|
-
height:
|
|
446
|
+
width: $fui-icon-lg;
|
|
447
|
+
height: $fui-icon-lg;
|
|
457
448
|
}
|
|
458
449
|
}
|
|
459
450
|
|
|
@@ -480,7 +471,7 @@
|
|
|
480
471
|
position: absolute;
|
|
481
472
|
top: 0;
|
|
482
473
|
right: 0;
|
|
483
|
-
width:
|
|
474
|
+
width: $fui-sidebar-rail-width;
|
|
484
475
|
height: 100%;
|
|
485
476
|
cursor: pointer;
|
|
486
477
|
z-index: 10;
|
|
@@ -502,9 +493,9 @@
|
|
|
502
493
|
position: absolute;
|
|
503
494
|
top: 50%;
|
|
504
495
|
transform: translateY(-50%);
|
|
505
|
-
right:
|
|
506
|
-
width:
|
|
507
|
-
height:
|
|
496
|
+
right: $fui-sidebar-rail-indicator-width;
|
|
497
|
+
width: $fui-sidebar-rail-indicator-width;
|
|
498
|
+
height: $fui-sidebar-rail-indicator-height;
|
|
508
499
|
border-radius: var(--fui-radius-full, $fui-radius-full);
|
|
509
500
|
background-color: var(--fui-border, $fui-border);
|
|
510
501
|
opacity: 0;
|
|
@@ -515,14 +506,14 @@
|
|
|
515
506
|
|
|
516
507
|
.positionRight & {
|
|
517
508
|
right: auto;
|
|
518
|
-
left:
|
|
509
|
+
left: $fui-sidebar-rail-indicator-width;
|
|
519
510
|
}
|
|
520
511
|
}
|
|
521
512
|
|
|
522
513
|
// Show indicator on hover
|
|
523
514
|
&:hover::before {
|
|
524
515
|
opacity: 1;
|
|
525
|
-
height:
|
|
516
|
+
height: $fui-sidebar-rail-indicator-height-hover;
|
|
526
517
|
background-color: var(--fui-color-accent, $fui-color-accent);
|
|
527
518
|
}
|
|
528
519
|
|
|
@@ -552,10 +543,44 @@
|
|
|
552
543
|
display: flex;
|
|
553
544
|
align-items: center;
|
|
554
545
|
gap: var(--fui-space-3, $fui-space-3);
|
|
555
|
-
padding: var(--fui-space-
|
|
556
|
-
min-height:
|
|
546
|
+
padding: var(--fui-space-1, $fui-space-1) var(--fui-space-2, $fui-space-2);
|
|
547
|
+
min-height: $fui-sidebar-item-height;
|
|
557
548
|
}
|
|
558
549
|
|
|
559
550
|
.skeletonLabel {
|
|
560
551
|
flex: 1;
|
|
561
552
|
}
|
|
553
|
+
|
|
554
|
+
// ============================================
|
|
555
|
+
// Accessibility: Reduced Motion
|
|
556
|
+
// ============================================
|
|
557
|
+
|
|
558
|
+
@media (prefers-reduced-motion: reduce) {
|
|
559
|
+
.root {
|
|
560
|
+
transition: none;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.mobile {
|
|
564
|
+
transition: none;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.overlay {
|
|
568
|
+
transition: none;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.submenu {
|
|
572
|
+
transition: none;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.itemChevron {
|
|
576
|
+
transition: none;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.rail::before {
|
|
580
|
+
transition: none;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.subItem::before {
|
|
584
|
+
transition: none;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
@@ -38,7 +38,7 @@ export interface SidebarProviderProps {
|
|
|
38
38
|
enableKeyboardShortcut?: boolean;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export interface SidebarProps {
|
|
41
|
+
export interface SidebarProps extends React.HTMLAttributes<HTMLElement> {
|
|
42
42
|
children: React.ReactNode;
|
|
43
43
|
/** Icon-only mode (desktop) - controlled */
|
|
44
44
|
collapsed?: boolean;
|
|
@@ -60,8 +60,6 @@ export interface SidebarProps {
|
|
|
60
60
|
position?: 'left' | 'right';
|
|
61
61
|
/** Collapse behavior: 'icon' (default), 'offcanvas', or 'none' */
|
|
62
62
|
collapsible?: SidebarCollapsible;
|
|
63
|
-
/** Additional class name */
|
|
64
|
-
className?: string;
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
export interface SidebarHeaderProps {
|
|
@@ -475,6 +473,8 @@ function SidebarRoot({
|
|
|
475
473
|
position = 'left',
|
|
476
474
|
collapsible = 'icon',
|
|
477
475
|
className,
|
|
476
|
+
style: styleProp,
|
|
477
|
+
...htmlProps
|
|
478
478
|
}: SidebarProps) {
|
|
479
479
|
// Check if we're inside a SidebarProvider
|
|
480
480
|
const existingContext = React.useContext(SidebarContext);
|
|
@@ -570,10 +570,12 @@ function SidebarRoot({
|
|
|
570
570
|
const style: React.CSSProperties = {
|
|
571
571
|
'--sidebar-width': resolvedWidth,
|
|
572
572
|
'--sidebar-collapsed-width': resolvedCollapsedWidth,
|
|
573
|
+
...styleProp,
|
|
573
574
|
} as React.CSSProperties;
|
|
574
575
|
|
|
575
576
|
const content = (
|
|
576
577
|
<aside
|
|
578
|
+
{...htmlProps}
|
|
577
579
|
className={classes}
|
|
578
580
|
style={style}
|
|
579
581
|
data-state={isMobile ? (open ? 'open' : 'closed') : (collapsed ? 'collapsed' : 'expanded')}
|
|
@@ -110,7 +110,7 @@ export default defineSegment({
|
|
|
110
110
|
name: 'Semantic Variants',
|
|
111
111
|
description: 'Pre-configured shapes for common elements',
|
|
112
112
|
render: () => (
|
|
113
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: '
|
|
113
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--fui-space-2)' }}>
|
|
114
114
|
<Skeleton variant="heading" width={200} />
|
|
115
115
|
<Skeleton variant="text" width="100%" />
|
|
116
116
|
<Skeleton variant="text" width="80%" />
|
|
@@ -121,7 +121,7 @@ export default defineSegment({
|
|
|
121
121
|
name: 'Avatar Skeleton',
|
|
122
122
|
description: 'Circular placeholder for avatars',
|
|
123
123
|
render: () => (
|
|
124
|
-
<div style={{ display: 'flex', gap: '
|
|
124
|
+
<div style={{ display: 'flex', gap: 'var(--fui-space-1)', alignItems: 'center' }}>
|
|
125
125
|
<Skeleton.Circle size="sm" />
|
|
126
126
|
<Skeleton.Circle size="md" />
|
|
127
127
|
<Skeleton.Circle size="lg" />
|
|
@@ -132,12 +132,12 @@ export default defineSegment({
|
|
|
132
132
|
name: 'Card Skeleton',
|
|
133
133
|
description: 'Composed skeleton for a card layout',
|
|
134
134
|
render: () => (
|
|
135
|
-
<div style={{ width: 300, padding:
|
|
135
|
+
<div style={{ width: 300, padding: 'var(--fui-space-2)', border: '1px solid var(--fui-border)', borderRadius: 'var(--fui-radius-lg)' }}>
|
|
136
136
|
<Skeleton variant="rect" height={120} radius="md" />
|
|
137
|
-
<div style={{ marginTop:
|
|
137
|
+
<div style={{ marginTop: 'var(--fui-space-2)' }}>
|
|
138
138
|
<Skeleton variant="heading" width="60%" />
|
|
139
139
|
</div>
|
|
140
|
-
<div style={{ marginTop:
|
|
140
|
+
<div style={{ marginTop: 'var(--fui-space-1)' }}>
|
|
141
141
|
<Skeleton.Text lines={2} />
|
|
142
142
|
</div>
|
|
143
143
|
</div>
|
|
@@ -164,3 +164,14 @@
|
|
|
164
164
|
background-color: var(--fui-bg-tertiary, #{$fui-dark-bg-tertiary});
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
+
|
|
168
|
+
// ============================================
|
|
169
|
+
// Accessibility: Reduced Motion
|
|
170
|
+
// ============================================
|
|
171
|
+
|
|
172
|
+
@media (prefers-reduced-motion: reduce) {
|
|
173
|
+
.skeleton,
|
|
174
|
+
.textLine {
|
|
175
|
+
animation: none;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
@use '../../tokens/variables' as *;
|
|
2
|
+
@use '../../tokens/mixins' as *;
|
|
3
|
+
|
|
4
|
+
.wrapper {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.header {
|
|
10
|
+
display: flex;
|
|
11
|
+
justify-content: space-between;
|
|
12
|
+
align-items: center;
|
|
13
|
+
margin-bottom: var(--fui-space-2, $fui-space-2);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.label {
|
|
17
|
+
@include label-text;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.value {
|
|
21
|
+
font-family: var(--fui-font-mono, $fui-font-mono);
|
|
22
|
+
font-size: var(--fui-font-size-sm, $fui-font-size-sm);
|
|
23
|
+
color: var(--fui-text-secondary, $fui-text-secondary);
|
|
24
|
+
min-width: 60px;
|
|
25
|
+
text-align: right;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.root {
|
|
29
|
+
width: 100%;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.control {
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
width: 100%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.track {
|
|
39
|
+
position: relative;
|
|
40
|
+
height: $fui-slider-track-height;
|
|
41
|
+
width: 100%;
|
|
42
|
+
background-color: var(--fui-bg-tertiary, $fui-bg-tertiary);
|
|
43
|
+
border-radius: var(--fui-radius-full, $fui-radius-full);
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
|
|
46
|
+
&[data-disabled] {
|
|
47
|
+
opacity: 0.5;
|
|
48
|
+
cursor: not-allowed;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.indicator {
|
|
53
|
+
position: absolute;
|
|
54
|
+
height: 100%;
|
|
55
|
+
background-color: var(--fui-color-accent, $fui-color-accent);
|
|
56
|
+
border-radius: var(--fui-radius-full, $fui-radius-full);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.thumb {
|
|
60
|
+
width: $fui-slider-thumb-size;
|
|
61
|
+
height: $fui-slider-thumb-size;
|
|
62
|
+
background-color: var(--fui-bg-primary, $fui-bg-primary);
|
|
63
|
+
border: $fui-slider-thumb-border solid var(--fui-color-accent, $fui-color-accent);
|
|
64
|
+
border-radius: var(--fui-radius-full, $fui-radius-full);
|
|
65
|
+
cursor: grab;
|
|
66
|
+
transition:
|
|
67
|
+
transform var(--fui-transition-fast, $fui-transition-fast),
|
|
68
|
+
box-shadow var(--fui-transition-fast, $fui-transition-fast);
|
|
69
|
+
|
|
70
|
+
&:hover {
|
|
71
|
+
transform: scale(1.1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
&:active {
|
|
75
|
+
cursor: grabbing;
|
|
76
|
+
transform: scale(1.05);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
&:focus-visible {
|
|
80
|
+
@include focus-ring;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
&[data-disabled] {
|
|
84
|
+
cursor: not-allowed;
|
|
85
|
+
opacity: 0.5;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Field } from '@base-ui/react/field';
|
|
3
|
+
import { Slider as BaseSlider } from '@base-ui/react/slider';
|
|
4
|
+
import styles from './Slider.module.scss';
|
|
5
|
+
import '../../styles/globals.scss';
|
|
6
|
+
|
|
7
|
+
export interface SliderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'> {
|
|
8
|
+
label?: string;
|
|
9
|
+
value?: number;
|
|
10
|
+
defaultValue?: number;
|
|
11
|
+
onChange?: (value: number) => void;
|
|
12
|
+
min?: number;
|
|
13
|
+
max?: number;
|
|
14
|
+
step?: number;
|
|
15
|
+
showValue?: boolean;
|
|
16
|
+
valueSuffix?: string;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
name?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const Slider = React.forwardRef<HTMLDivElement, SliderProps>(
|
|
22
|
+
function Slider(
|
|
23
|
+
{
|
|
24
|
+
label,
|
|
25
|
+
value,
|
|
26
|
+
defaultValue,
|
|
27
|
+
onChange,
|
|
28
|
+
min = 0,
|
|
29
|
+
max = 100,
|
|
30
|
+
step = 1,
|
|
31
|
+
showValue = false,
|
|
32
|
+
valueSuffix = '',
|
|
33
|
+
disabled = false,
|
|
34
|
+
className,
|
|
35
|
+
name,
|
|
36
|
+
id,
|
|
37
|
+
...htmlProps
|
|
38
|
+
},
|
|
39
|
+
ref
|
|
40
|
+
) {
|
|
41
|
+
// For controlled component, use value; otherwise track internal state for display
|
|
42
|
+
const [internalValue, setInternalValue] = React.useState(defaultValue ?? min);
|
|
43
|
+
const displayValue = value !== undefined ? value : internalValue;
|
|
44
|
+
|
|
45
|
+
const handleChange = (newValue: number | number[]) => {
|
|
46
|
+
const val = Array.isArray(newValue) ? newValue[0] : newValue;
|
|
47
|
+
setInternalValue(val);
|
|
48
|
+
onChange?.(val);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const sliderValue = value !== undefined ? [value] : (defaultValue !== undefined ? [defaultValue] : undefined);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Field.Root {...htmlProps} disabled={disabled} className={[styles.wrapper, className].filter(Boolean).join(' ')}>
|
|
55
|
+
{(label || showValue) && (
|
|
56
|
+
<div className={styles.header}>
|
|
57
|
+
{label && <Field.Label className={styles.label}>{label}</Field.Label>}
|
|
58
|
+
{showValue && (
|
|
59
|
+
<span className={styles.value}>
|
|
60
|
+
{displayValue}{valueSuffix}
|
|
61
|
+
</span>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
<BaseSlider.Root
|
|
66
|
+
ref={ref}
|
|
67
|
+
value={sliderValue}
|
|
68
|
+
defaultValue={defaultValue !== undefined ? [defaultValue] : undefined}
|
|
69
|
+
onValueChange={handleChange}
|
|
70
|
+
min={min}
|
|
71
|
+
max={max}
|
|
72
|
+
step={step}
|
|
73
|
+
disabled={disabled}
|
|
74
|
+
name={name}
|
|
75
|
+
id={id}
|
|
76
|
+
className={styles.root}
|
|
77
|
+
>
|
|
78
|
+
<BaseSlider.Control className={styles.control}>
|
|
79
|
+
<BaseSlider.Track className={styles.track}>
|
|
80
|
+
<BaseSlider.Indicator className={styles.indicator} />
|
|
81
|
+
<BaseSlider.Thumb className={styles.thumb} />
|
|
82
|
+
</BaseSlider.Track>
|
|
83
|
+
</BaseSlider.Control>
|
|
84
|
+
</BaseSlider.Root>
|
|
85
|
+
</Field.Root>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
@use '../../tokens/variables' as *;
|
|
2
|
+
|
|
3
|
+
.stack {
|
|
4
|
+
display: flex;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Direction
|
|
8
|
+
.row {
|
|
9
|
+
flex-direction: row;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.column {
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Responsive direction
|
|
17
|
+
.directionResponsive {
|
|
18
|
+
flex-direction: var(--fui-stack-direction, column);
|
|
19
|
+
|
|
20
|
+
@media (min-width: 640px) {
|
|
21
|
+
flex-direction: var(--fui-stack-direction-sm, var(--fui-stack-direction, column));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@media (min-width: 768px) {
|
|
25
|
+
flex-direction: var(--fui-stack-direction-md, var(--fui-stack-direction-sm, var(--fui-stack-direction, column)));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@media (min-width: 1024px) {
|
|
29
|
+
flex-direction: var(--fui-stack-direction-lg, var(--fui-stack-direction-md, var(--fui-stack-direction-sm, var(--fui-stack-direction, column))));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@media (min-width: 1280px) {
|
|
33
|
+
flex-direction: var(--fui-stack-direction-xl, var(--fui-stack-direction-lg, var(--fui-stack-direction-md, var(--fui-stack-direction-sm, var(--fui-stack-direction, column)))));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Gap sizes
|
|
38
|
+
.gap-xs {
|
|
39
|
+
gap: var(--fui-space-1, $fui-space-1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.gap-sm {
|
|
43
|
+
gap: var(--fui-space-2, $fui-space-2);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.gap-md {
|
|
47
|
+
gap: var(--fui-space-3, $fui-space-3);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.gap-lg {
|
|
51
|
+
gap: var(--fui-space-4, $fui-space-4);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.gap-xl {
|
|
55
|
+
gap: var(--fui-space-6, $fui-space-6);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Responsive gap
|
|
59
|
+
.gapResponsive {
|
|
60
|
+
gap: var(--fui-stack-gap, 0);
|
|
61
|
+
|
|
62
|
+
@media (min-width: 640px) {
|
|
63
|
+
gap: var(--fui-stack-gap-sm, var(--fui-stack-gap, 0));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@media (min-width: 768px) {
|
|
67
|
+
gap: var(--fui-stack-gap-md, var(--fui-stack-gap-sm, var(--fui-stack-gap, 0)));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@media (min-width: 1024px) {
|
|
71
|
+
gap: var(--fui-stack-gap-lg, var(--fui-stack-gap-md, var(--fui-stack-gap-sm, var(--fui-stack-gap, 0))));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@media (min-width: 1280px) {
|
|
75
|
+
gap: var(--fui-stack-gap-xl, var(--fui-stack-gap-lg, var(--fui-stack-gap-md, var(--fui-stack-gap-sm, var(--fui-stack-gap, 0)))));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Align (cross-axis)
|
|
80
|
+
.align-start {
|
|
81
|
+
align-items: flex-start;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.align-center {
|
|
85
|
+
align-items: center;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.align-end {
|
|
89
|
+
align-items: flex-end;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.align-stretch {
|
|
93
|
+
align-items: stretch;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.align-baseline {
|
|
97
|
+
align-items: baseline;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Justify (main-axis)
|
|
101
|
+
.justify-start {
|
|
102
|
+
justify-content: flex-start;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.justify-center {
|
|
106
|
+
justify-content: center;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.justify-end {
|
|
110
|
+
justify-content: flex-end;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.justify-between {
|
|
114
|
+
justify-content: space-between;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Wrap
|
|
118
|
+
.wrap {
|
|
119
|
+
flex-wrap: wrap;
|
|
120
|
+
}
|