@duro-app/ui 0.20.0 → 0.21.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/dist/components/ActionBar/ActionBar.meta.d.ts +3 -0
- package/dist/components/ActionBar/ActionBar.meta.d.ts.map +1 -0
- package/dist/components/Alert/Alert.meta.d.ts +3 -0
- package/dist/components/Alert/Alert.meta.d.ts.map +1 -0
- package/dist/components/Badge/Badge.meta.d.ts +3 -0
- package/dist/components/Badge/Badge.meta.d.ts.map +1 -0
- package/dist/components/Callout/Callout.meta.d.ts +3 -0
- package/dist/components/Callout/Callout.meta.d.ts.map +1 -0
- package/dist/components/Card/Card.meta.d.ts +3 -0
- package/dist/components/Card/Card.meta.d.ts.map +1 -0
- package/dist/components/Dialog/Dialog.d.ts.map +1 -1
- package/dist/components/Dialog/Dialog.stories.d.ts.map +1 -1
- package/dist/components/Drawer/Drawer.d.ts.map +1 -1
- package/dist/components/Drawer/Drawer.stories.d.ts.map +1 -1
- package/dist/components/Drawer/useSwipeDismiss.d.ts.map +1 -1
- package/dist/components/EmptyState/EmptyState.meta.d.ts +3 -0
- package/dist/components/EmptyState/EmptyState.meta.d.ts.map +1 -0
- package/dist/components/Heading/Heading.meta.d.ts +3 -0
- package/dist/components/Heading/Heading.meta.d.ts.map +1 -0
- package/dist/components/Icon/Icon.meta.d.ts +3 -0
- package/dist/components/Icon/Icon.meta.d.ts.map +1 -0
- package/dist/components/InputGroup/InputGroup.meta.d.ts +3 -0
- package/dist/components/InputGroup/InputGroup.meta.d.ts.map +1 -0
- package/dist/components/Spinner/Spinner.meta.d.ts +3 -0
- package/dist/components/Spinner/Spinner.meta.d.ts.map +1 -0
- package/dist/components/StatusIcon/StatusIcon.meta.d.ts +3 -0
- package/dist/components/StatusIcon/StatusIcon.meta.d.ts.map +1 -0
- package/dist/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/components/Text/Text.meta.d.ts +3 -0
- package/dist/components/Text/Text.meta.d.ts.map +1 -0
- package/dist/components/ThemeProvider/ThemeProvider.meta.d.ts +3 -0
- package/dist/components/ThemeProvider/ThemeProvider.meta.d.ts.map +1 -0
- package/dist/components/Toggle/styles.css.d.ts +1 -1
- package/dist/components/Toggle/styles.css.d.ts.map +1 -1
- package/dist/index.js +10 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/ActionBar/ActionBar.meta.ts +20 -0
- package/src/components/Alert/Alert.meta.ts +25 -0
- package/src/components/Badge/Badge.meta.ts +20 -0
- package/src/components/Callout/Callout.meta.ts +22 -0
- package/src/components/Card/Card.meta.ts +25 -0
- package/src/components/Dialog/Dialog.stories.tsx +5 -7
- package/src/components/Dialog/Dialog.tsx +1 -5
- package/src/components/Drawer/Drawer.stories.tsx +24 -21
- package/src/components/Drawer/Drawer.tsx +2 -9
- package/src/components/Drawer/useSwipeDismiss.ts +13 -6
- package/src/components/EmptyState/EmptyState.meta.ts +22 -0
- package/src/components/Heading/Heading.meta.ts +24 -0
- package/src/components/Icon/Icon.meta.ts +23 -0
- package/src/components/InputGroup/InputGroup.meta.ts +28 -0
- package/src/components/Spinner/Spinner.meta.ts +19 -0
- package/src/components/StatusIcon/StatusIcon.meta.ts +24 -0
- package/src/components/Table/Table.stories.tsx +1 -4
- package/src/components/Text/Text.meta.ts +23 -0
- package/src/components/ThemeProvider/ThemeProvider.meta.ts +16 -0
- package/src/components/Toggle/styles.css.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duro-app/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@duro-app/tokens": "^0.
|
|
55
|
+
"@duro-app/tokens": "^0.21.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@babel/preset-typescript": "^7.28.0",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Floating toolbar that appears at the bottom of the viewport when items are selected. Shows selection count and bulk actions. Animates in/out. Use with Table checkboxes for bulk operations.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Bulk actions on table selections (delete, revoke, export)',
|
|
8
|
+
'Multi-select workflows where actions apply to all selected items',
|
|
9
|
+
],
|
|
10
|
+
whenNotToUse: [
|
|
11
|
+
'Single-item actions — use inline buttons or a Menu',
|
|
12
|
+
'Persistent toolbars — use Inline with buttons',
|
|
13
|
+
],
|
|
14
|
+
relatedTo: [
|
|
15
|
+
{component: 'Table', relationship: 'Common pattern: Table checkboxes + ActionBar for bulk ops'},
|
|
16
|
+
],
|
|
17
|
+
example: `<ActionBar selectedItemCount={selected.size} onClearSelection={() => setSelected(new Set())}>
|
|
18
|
+
<Button variant="danger" size="small" onClick={handleDelete}>Delete</Button>
|
|
19
|
+
</ActionBar>`,
|
|
20
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Inline status message with icon. Compact, single-line feedback for form validation results, API responses, or status updates. For block-level prominent messages, use Callout instead.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Form submission feedback (success, error)',
|
|
8
|
+
'API response status messages',
|
|
9
|
+
'Inline warnings or info notices',
|
|
10
|
+
],
|
|
11
|
+
whenNotToUse: [
|
|
12
|
+
'Block-level prominent messages — use Callout',
|
|
13
|
+
'Toast notifications — use a toast system',
|
|
14
|
+
'Form field errors — use Field.Error',
|
|
15
|
+
],
|
|
16
|
+
relatedTo: [
|
|
17
|
+
{component: 'Callout', relationship: 'Callout for larger block-level messages; Alert for compact inline'},
|
|
18
|
+
],
|
|
19
|
+
example: `<Stack gap="sm">
|
|
20
|
+
<Alert variant="success">Settings saved successfully.</Alert>
|
|
21
|
+
<Alert variant="error">Failed to connect. Please retry.</Alert>
|
|
22
|
+
<Alert variant="warning">Your session expires in 5 minutes.</Alert>
|
|
23
|
+
<Alert variant="info">New version available.</Alert>
|
|
24
|
+
</Stack>`,
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Small label or tag for status indicators, counts, or categories. Renders inline. Use for metadata annotations next to text or in table cells.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Status indicators (active, expired, pending)',
|
|
8
|
+
'Category tags or labels',
|
|
9
|
+
'Count indicators next to items',
|
|
10
|
+
],
|
|
11
|
+
whenNotToUse: [
|
|
12
|
+
'Interactive toggleable tags — use Toggle or ToggleGroup',
|
|
13
|
+
'Long descriptive messages — use Alert or Callout',
|
|
14
|
+
],
|
|
15
|
+
example: `<Inline gap="xs">
|
|
16
|
+
<Badge variant="success">Active</Badge>
|
|
17
|
+
<Badge variant="error">Expired</Badge>
|
|
18
|
+
<Badge variant="default">Draft</Badge>
|
|
19
|
+
</Inline>`,
|
|
20
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Block-level informational message with icon and colored background. More prominent than Alert. Use for important notices, onboarding tips, or contextual guidance that should stand out.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Important notices that need visual prominence',
|
|
8
|
+
'Onboarding tips or contextual guidance',
|
|
9
|
+
'Warning messages before destructive actions',
|
|
10
|
+
],
|
|
11
|
+
whenNotToUse: [
|
|
12
|
+
'Compact inline feedback — use Alert',
|
|
13
|
+
'Form field errors — use Field.Error',
|
|
14
|
+
'Toast notifications — use a toast system',
|
|
15
|
+
],
|
|
16
|
+
relatedTo: [
|
|
17
|
+
{component: 'Alert', relationship: 'Alert for compact inline; Callout for block-level prominent'},
|
|
18
|
+
],
|
|
19
|
+
example: `<Callout variant="warning">
|
|
20
|
+
Your subscription expires in 3 days. Renew now to avoid service interruption.
|
|
21
|
+
</Callout>`,
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Container with visual styling (elevation, border, or fill). Groups related content with consistent spacing and background. For structural containers with Header/Body/Footer slots, use Panel instead.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Grouping related content visually (settings sections, dashboard widgets)',
|
|
8
|
+
'Clickable content cards with hover state (interactive variant)',
|
|
9
|
+
'Content containers with elevation or borders',
|
|
10
|
+
],
|
|
11
|
+
whenNotToUse: [
|
|
12
|
+
'Structural containers with Header/Body/Footer — use Panel',
|
|
13
|
+
'Page-level layout — use PageShell',
|
|
14
|
+
'Simple spacing — use Stack or Inline',
|
|
15
|
+
],
|
|
16
|
+
relatedTo: [
|
|
17
|
+
{component: 'Panel', relationship: 'Panel for structural Header/Body/Footer; Card for visual container'},
|
|
18
|
+
],
|
|
19
|
+
example: `<Card variant="elevated" size="md">
|
|
20
|
+
<Stack gap="md">
|
|
21
|
+
<Heading level={3}>Account</Heading>
|
|
22
|
+
<Text>Manage your account settings.</Text>
|
|
23
|
+
</Stack>
|
|
24
|
+
</Card>`,
|
|
25
|
+
}
|
|
@@ -177,9 +177,7 @@ function FormDialogDemo() {
|
|
|
177
177
|
<Dialog.Title>Edit profile</Dialog.Title>
|
|
178
178
|
<Dialog.Close />
|
|
179
179
|
</Dialog.Header>
|
|
180
|
-
<Dialog.Description>
|
|
181
|
-
Update your display name and email address.
|
|
182
|
-
</Dialog.Description>
|
|
180
|
+
<Dialog.Description>Update your display name and email address.</Dialog.Description>
|
|
183
181
|
<Dialog.Body>
|
|
184
182
|
<Stack gap="md">
|
|
185
183
|
<Field.Root>
|
|
@@ -270,8 +268,8 @@ export const NonDismissable: Story = {
|
|
|
270
268
|
<Dialog.Body>
|
|
271
269
|
<Stack gap="md">
|
|
272
270
|
<Text>
|
|
273
|
-
You must accept the terms of service to continue. This dialog cannot be
|
|
274
|
-
|
|
271
|
+
You must accept the terms of service to continue. This dialog cannot be dismissed by
|
|
272
|
+
clicking outside or pressing Escape.
|
|
275
273
|
</Text>
|
|
276
274
|
<Text variant="bodySm" color="muted">
|
|
277
275
|
Try clicking the backdrop or pressing Escape — the dialog stays open.
|
|
@@ -309,8 +307,8 @@ function BackdropDismissDemo() {
|
|
|
309
307
|
</Dialog.Header>
|
|
310
308
|
<Dialog.Body>
|
|
311
309
|
<Text>
|
|
312
|
-
Click the dark backdrop behind this dialog to dismiss it.
|
|
313
|
-
|
|
310
|
+
Click the dark backdrop behind this dialog to dismiss it. You can also press Escape or
|
|
311
|
+
use the close button.
|
|
314
312
|
</Text>
|
|
315
313
|
</Dialog.Body>
|
|
316
314
|
</Dialog.Portal>
|
|
@@ -278,11 +278,7 @@ function Close({children, 'aria-label': ariaLabel = 'Close'}: CloseProps) {
|
|
|
278
278
|
}
|
|
279
279
|
|
|
280
280
|
return (
|
|
281
|
-
<html.button
|
|
282
|
-
onClick={requestClose}
|
|
283
|
-
aria-label={ariaLabel}
|
|
284
|
-
style={styles.closeButton}
|
|
285
|
-
>
|
|
281
|
+
<html.button onClick={requestClose} aria-label={ariaLabel} style={styles.closeButton}>
|
|
286
282
|
<svg width={16} height={16} viewBox="0 0 16 16" fill="none">
|
|
287
283
|
<path
|
|
288
284
|
d="M4 4l8 8M12 4l-8 8"
|
|
@@ -31,8 +31,8 @@ export const Default: Story = {
|
|
|
31
31
|
</Drawer.Header>
|
|
32
32
|
<Drawer.Body>
|
|
33
33
|
<Text>
|
|
34
|
-
This drawer slides in from the right edge. It's useful for showing details,
|
|
35
|
-
|
|
34
|
+
This drawer slides in from the right edge. It's useful for showing details, settings
|
|
35
|
+
panels, or supplementary content without leaving the current page.
|
|
36
36
|
</Text>
|
|
37
37
|
</Drawer.Body>
|
|
38
38
|
<Drawer.Footer>
|
|
@@ -93,9 +93,15 @@ export const BottomAnchor: Story = {
|
|
|
93
93
|
</Drawer.Header>
|
|
94
94
|
<Drawer.Body>
|
|
95
95
|
<Stack gap="sm">
|
|
96
|
-
<Button variant="secondary" fullWidth>
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
<Button variant="secondary" fullWidth>
|
|
97
|
+
Share
|
|
98
|
+
</Button>
|
|
99
|
+
<Button variant="secondary" fullWidth>
|
|
100
|
+
Duplicate
|
|
101
|
+
</Button>
|
|
102
|
+
<Button variant="danger" fullWidth>
|
|
103
|
+
Delete
|
|
104
|
+
</Button>
|
|
99
105
|
</Stack>
|
|
100
106
|
</Drawer.Body>
|
|
101
107
|
</Drawer.Portal>
|
|
@@ -125,7 +131,10 @@ function SizesDemo() {
|
|
|
125
131
|
<Drawer.Close />
|
|
126
132
|
</Drawer.Header>
|
|
127
133
|
<Drawer.Body>
|
|
128
|
-
<Text>
|
|
134
|
+
<Text>
|
|
135
|
+
This drawer uses the {s} size preset (
|
|
136
|
+
{s === 'sm' ? '360' : s === 'md' ? '480' : '640'}px max-width).
|
|
137
|
+
</Text>
|
|
129
138
|
</Drawer.Body>
|
|
130
139
|
<Drawer.Footer>
|
|
131
140
|
<Drawer.Close>
|
|
@@ -164,9 +173,7 @@ function FormDrawerDemo() {
|
|
|
164
173
|
<Drawer.Title>Edit profile</Drawer.Title>
|
|
165
174
|
<Drawer.Close />
|
|
166
175
|
</Drawer.Header>
|
|
167
|
-
<Drawer.Description>
|
|
168
|
-
Update your account information below.
|
|
169
|
-
</Drawer.Description>
|
|
176
|
+
<Drawer.Description>Update your account information below.</Drawer.Description>
|
|
170
177
|
<Drawer.Body>
|
|
171
178
|
<Stack gap="md">
|
|
172
179
|
<Field.Root>
|
|
@@ -269,8 +276,8 @@ export const NonDismissable: Story = {
|
|
|
269
276
|
<Drawer.Body>
|
|
270
277
|
<Stack gap="md">
|
|
271
278
|
<Text>
|
|
272
|
-
This drawer cannot be dismissed by clicking the backdrop or pressing Escape.
|
|
273
|
-
|
|
279
|
+
This drawer cannot be dismissed by clicking the backdrop or pressing Escape. You must
|
|
280
|
+
use the explicit close action below.
|
|
274
281
|
</Text>
|
|
275
282
|
<Text variant="bodySm" color="muted">
|
|
276
283
|
Try clicking outside or pressing Escape — nothing happens.
|
|
@@ -302,8 +309,8 @@ export const NoSwipeDismiss: Story = {
|
|
|
302
309
|
</Drawer.Header>
|
|
303
310
|
<Drawer.Body>
|
|
304
311
|
<Text>
|
|
305
|
-
This bottom drawer has swipe-to-dismiss disabled. On mobile, you cannot
|
|
306
|
-
|
|
312
|
+
This bottom drawer has swipe-to-dismiss disabled. On mobile, you cannot swipe down to
|
|
313
|
+
close it — use the close button or tap the backdrop instead.
|
|
307
314
|
</Text>
|
|
308
315
|
</Drawer.Body>
|
|
309
316
|
</Drawer.Portal>
|
|
@@ -326,12 +333,10 @@ export const SwipeDismiss: Story = {
|
|
|
326
333
|
</Drawer.Header>
|
|
327
334
|
<Drawer.Body>
|
|
328
335
|
<Stack gap="md">
|
|
329
|
-
<Text>
|
|
330
|
-
Drag this drawer downward to dismiss it. The backdrop fades as you drag.
|
|
331
|
-
</Text>
|
|
336
|
+
<Text>Drag this drawer downward to dismiss it. The backdrop fades as you drag.</Text>
|
|
332
337
|
<Text variant="bodySm" color="muted">
|
|
333
|
-
Swipe past 30% of the panel height or flick quickly to dismiss.
|
|
334
|
-
|
|
338
|
+
Swipe past 30% of the panel height or flick quickly to dismiss. Otherwise it snaps
|
|
339
|
+
back.
|
|
335
340
|
</Text>
|
|
336
341
|
</Stack>
|
|
337
342
|
</Drawer.Body>
|
|
@@ -354,9 +359,7 @@ export const SwipeDismissRight: Story = {
|
|
|
354
359
|
<Drawer.Close />
|
|
355
360
|
</Drawer.Header>
|
|
356
361
|
<Drawer.Body>
|
|
357
|
-
<Text>
|
|
358
|
-
Drag this panel to the right to dismiss it. Works with touch and mouse.
|
|
359
|
-
</Text>
|
|
362
|
+
<Text>Drag this panel to the right to dismiss it. Works with touch and mouse.</Text>
|
|
360
363
|
</Drawer.Body>
|
|
361
364
|
</Drawer.Portal>
|
|
362
365
|
</Drawer.Root>
|
|
@@ -252,10 +252,7 @@ function Portal({children, size = 'md'}: PortalProps) {
|
|
|
252
252
|
/>
|
|
253
253
|
|
|
254
254
|
{/* Viewport */}
|
|
255
|
-
<html.div
|
|
256
|
-
style={[styles.viewport, viewportAnchorMap[anchor]]}
|
|
257
|
-
data-drawer-viewport=""
|
|
258
|
-
>
|
|
255
|
+
<html.div style={[styles.viewport, viewportAnchorMap[anchor]]} data-drawer-viewport="">
|
|
259
256
|
<html.div
|
|
260
257
|
ref={panelRef}
|
|
261
258
|
role="dialog"
|
|
@@ -338,11 +335,7 @@ function Close({children, 'aria-label': ariaLabel = 'Close'}: CloseProps) {
|
|
|
338
335
|
}
|
|
339
336
|
|
|
340
337
|
return (
|
|
341
|
-
<html.button
|
|
342
|
-
onClick={requestClose}
|
|
343
|
-
aria-label={ariaLabel}
|
|
344
|
-
style={styles.closeButton}
|
|
345
|
-
>
|
|
338
|
+
<html.button onClick={requestClose} aria-label={ariaLabel} style={styles.closeButton}>
|
|
346
339
|
<svg width={16} height={16} viewBox="0 0 16 16" fill="none">
|
|
347
340
|
<path
|
|
348
341
|
d="M4 4l8 8M12 4l-8 8"
|
|
@@ -97,9 +97,7 @@ export function useSwipeDismiss({anchor, enabled, onDismiss, panelRef}: UseSwipe
|
|
|
97
97
|
|
|
98
98
|
panelEl.style.transform = ''
|
|
99
99
|
panelEl.style.boxShadow =
|
|
100
|
-
shadowSpread > 0
|
|
101
|
-
? `0 0 ${shadowSpread * 2}px ${shadowSpread}px rgba(0, 0, 0, 0.15)`
|
|
102
|
-
: ''
|
|
100
|
+
shadowSpread > 0 ? `0 0 ${shadowSpread * 2}px ${shadowSpread}px rgba(0, 0, 0, 0.15)` : ''
|
|
103
101
|
}
|
|
104
102
|
},
|
|
105
103
|
[anchor, isHorizontal],
|
|
@@ -255,8 +253,7 @@ export function useSwipeDismiss({anchor, enabled, onDismiss, panelRef}: UseSwipe
|
|
|
255
253
|
}
|
|
256
254
|
|
|
257
255
|
const fraction = movement / state.panelSize
|
|
258
|
-
const shouldDismiss =
|
|
259
|
-
fraction > DISMISS_THRESHOLD || velocity > VELOCITY_THRESHOLD
|
|
256
|
+
const shouldDismiss = fraction > DISMISS_THRESHOLD || velocity > VELOCITY_THRESHOLD
|
|
260
257
|
|
|
261
258
|
if (shouldDismiss) {
|
|
262
259
|
animateDismiss(panel!)
|
|
@@ -291,5 +288,15 @@ export function useSwipeDismiss({anchor, enabled, onDismiss, panelRef}: UseSwipe
|
|
|
291
288
|
panel.removeEventListener('pointercancel', handlePointerCancel)
|
|
292
289
|
panel.removeEventListener('touchmove', handleTouchMove)
|
|
293
290
|
}
|
|
294
|
-
}, [
|
|
291
|
+
}, [
|
|
292
|
+
enabled,
|
|
293
|
+
panelRef,
|
|
294
|
+
anchor,
|
|
295
|
+
isHorizontal,
|
|
296
|
+
getMovement,
|
|
297
|
+
applyTransform,
|
|
298
|
+
resetStyles,
|
|
299
|
+
animateSnapBack,
|
|
300
|
+
animateDismiss,
|
|
301
|
+
])
|
|
295
302
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Placeholder for empty content areas. Shows an icon, message, and optional action button. Use when a list, table, or section has no data to display.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Empty tables or lists with no results',
|
|
8
|
+
'Search results with no matches',
|
|
9
|
+
'First-time use before any data exists',
|
|
10
|
+
],
|
|
11
|
+
whenNotToUse: [
|
|
12
|
+
'Loading states — use Spinner',
|
|
13
|
+
'Error states — use Alert or Callout',
|
|
14
|
+
],
|
|
15
|
+
example: `<Card>
|
|
16
|
+
<EmptyState
|
|
17
|
+
icon={<Icon name="info-circle" size={48} />}
|
|
18
|
+
message="No results found"
|
|
19
|
+
action={<Button variant="secondary">Clear filters</Button>}
|
|
20
|
+
/>
|
|
21
|
+
</Card>`,
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Semantic heading element (h1-h6) with typography presets. Always use the correct level for document hierarchy. Visual size can differ from semantic level via the variant prop.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Page titles (level 1)',
|
|
8
|
+
'Section headers (level 2-3)',
|
|
9
|
+
'Card or panel titles (level 3-4)',
|
|
10
|
+
],
|
|
11
|
+
whenNotToUse: [
|
|
12
|
+
'Body text — use Text',
|
|
13
|
+
'Form labels — use Field.Label',
|
|
14
|
+
'Non-semantic bold text — use Text with weight',
|
|
15
|
+
],
|
|
16
|
+
relatedTo: [
|
|
17
|
+
{component: 'Text', relationship: 'Text for body/label; Heading for semantic headings'},
|
|
18
|
+
],
|
|
19
|
+
example: `<Stack gap="md">
|
|
20
|
+
<Heading level={1}>Page Title</Heading>
|
|
21
|
+
<Heading level={2} variant="headingMd">Section</Heading>
|
|
22
|
+
<Heading level={3}>Subsection</Heading>
|
|
23
|
+
</Stack>`,
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'SVG icon component. Renders stroke or filled icons from the design system icon set. Size scales with the size prop. Use inside buttons, badges, or standalone.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Button icons (inside Button children)',
|
|
8
|
+
'Status indicators alongside text',
|
|
9
|
+
'Navigation or action indicators',
|
|
10
|
+
],
|
|
11
|
+
whenNotToUse: [
|
|
12
|
+
'Decorative images — use img elements',
|
|
13
|
+
'Complex illustrations — use custom SVGs',
|
|
14
|
+
],
|
|
15
|
+
relatedTo: [
|
|
16
|
+
{component: 'StatusIcon', relationship: 'StatusIcon wraps Icon with a colored background circle'},
|
|
17
|
+
],
|
|
18
|
+
example: `<Inline gap="sm" align="center">
|
|
19
|
+
<Icon name="check-circle" size={20} />
|
|
20
|
+
<Icon name="alert-triangle" size={20} />
|
|
21
|
+
<Icon name="shield-filled" size={20} />
|
|
22
|
+
</Inline>`,
|
|
23
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Wraps an Input with prefix and/or suffix addons (icons, text, buttons). Addons are visually attached to the input edges.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Inputs with units (e.g. "$" prefix, "kg" suffix)',
|
|
8
|
+
'Search inputs with an icon prefix',
|
|
9
|
+
'URL inputs with protocol prefix',
|
|
10
|
+
],
|
|
11
|
+
whenNotToUse: [
|
|
12
|
+
'Standalone inputs without addons — use Input directly',
|
|
13
|
+
'Form field labels — use Field.Label',
|
|
14
|
+
],
|
|
15
|
+
anatomy: {
|
|
16
|
+
required: ['Root'],
|
|
17
|
+
optional: ['Addon'],
|
|
18
|
+
},
|
|
19
|
+
relatedTo: [
|
|
20
|
+
{component: 'Input', relationship: 'InputGroup enhances Input with addons'},
|
|
21
|
+
{component: 'Field', relationship: 'Field provides label/error; InputGroup provides visual addons'},
|
|
22
|
+
],
|
|
23
|
+
example: `<InputGroup.Root>
|
|
24
|
+
<InputGroup.Addon>$</InputGroup.Addon>
|
|
25
|
+
<Input type="number" placeholder="0.00" />
|
|
26
|
+
<InputGroup.Addon>USD</InputGroup.Addon>
|
|
27
|
+
</InputGroup.Root>`,
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Animated loading indicator. Use while content is being fetched or an action is processing. Always provide a label for accessibility.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Page or section loading states',
|
|
8
|
+
'Button loading states (inside a button)',
|
|
9
|
+
'Async operation feedback',
|
|
10
|
+
],
|
|
11
|
+
whenNotToUse: [
|
|
12
|
+
'Skeleton loading — use placeholder skeleton patterns',
|
|
13
|
+
'Progress with known completion — use a progress bar',
|
|
14
|
+
],
|
|
15
|
+
example: `<Stack gap="md" align="center">
|
|
16
|
+
<Spinner size="lg" label="Loading data..." />
|
|
17
|
+
<Button disabled><Spinner size="sm" /> Saving...</Button>
|
|
18
|
+
</Stack>`,
|
|
19
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Icon with a colored background circle. Combines an Icon with a status-colored container for visual emphasis. Use for status indicators in dashboards or lists.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Dashboard status indicators',
|
|
8
|
+
'List item status icons with colored backgrounds',
|
|
9
|
+
'Feature or capability indicators',
|
|
10
|
+
],
|
|
11
|
+
whenNotToUse: [
|
|
12
|
+
'Inline icons without background — use Icon',
|
|
13
|
+
'Status text labels — use Badge',
|
|
14
|
+
],
|
|
15
|
+
relatedTo: [
|
|
16
|
+
{component: 'Icon', relationship: 'StatusIcon wraps Icon with background; Icon is bare SVG'},
|
|
17
|
+
{component: 'Badge', relationship: 'Badge for text status; StatusIcon for icon status'},
|
|
18
|
+
],
|
|
19
|
+
example: `<Inline gap="md">
|
|
20
|
+
<StatusIcon name="check-circle" variant="success" size="md" />
|
|
21
|
+
<StatusIcon name="alert-triangle" variant="warning" size="md" />
|
|
22
|
+
<StatusIcon name="x-circle" variant="error" size="md" />
|
|
23
|
+
</Inline>`,
|
|
24
|
+
}
|
|
@@ -271,10 +271,7 @@ function CheckboxTableDemo() {
|
|
|
271
271
|
</Table.Body>
|
|
272
272
|
</Table.Root>
|
|
273
273
|
|
|
274
|
-
<ActionBar
|
|
275
|
-
selectedItemCount={selected.size}
|
|
276
|
-
onClearSelection={() => setSelected(new Set())}
|
|
277
|
-
>
|
|
274
|
+
<ActionBar selectedItemCount={selected.size} onClearSelection={() => setSelected(new Set())}>
|
|
278
275
|
<Button variant="danger" size="small">
|
|
279
276
|
Delete
|
|
280
277
|
</Button>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Body and label typography component. Renders a span or paragraph with design system typography presets. Use for all non-heading text content.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'Body text (paragraphs, descriptions)',
|
|
8
|
+
'UI labels and metadata',
|
|
9
|
+
'Captions, code snippets, overlines',
|
|
10
|
+
],
|
|
11
|
+
whenNotToUse: [
|
|
12
|
+
'Page or section titles — use Heading',
|
|
13
|
+
'Form field labels — use Field.Label (auto-binds to form)',
|
|
14
|
+
],
|
|
15
|
+
relatedTo: [
|
|
16
|
+
{component: 'Heading', relationship: 'Heading for semantic h1-h6; Text for body/label'},
|
|
17
|
+
],
|
|
18
|
+
example: `<Stack gap="xs">
|
|
19
|
+
<Text variant="label">Email</Text>
|
|
20
|
+
<Text variant="bodySm" color="muted">We will never share your email.</Text>
|
|
21
|
+
<Text variant="code">user@example.com</Text>
|
|
22
|
+
</Stack>`,
|
|
23
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type {ComponentMeta} from '../component-meta'
|
|
2
|
+
|
|
3
|
+
export const meta: ComponentMeta = {
|
|
4
|
+
description:
|
|
5
|
+
'Theme context provider. Must wrap the entire app at the root level. Sets dark, light, or high-contrast theme for all child components via CSS custom properties.',
|
|
6
|
+
whenToUse: [
|
|
7
|
+
'App root — required for all Duro components to render correctly',
|
|
8
|
+
'Theme switching (dark/light mode toggle)',
|
|
9
|
+
],
|
|
10
|
+
whenNotToUse: [
|
|
11
|
+
'Never omit — all Duro components require ThemeProvider',
|
|
12
|
+
],
|
|
13
|
+
example: `<ThemeProvider theme="dark">
|
|
14
|
+
<App />
|
|
15
|
+
</ThemeProvider>`,
|
|
16
|
+
}
|
|
@@ -57,7 +57,8 @@ export const styles = css.create({
|
|
|
57
57
|
borderRadius: 0,
|
|
58
58
|
borderRightWidth: 1,
|
|
59
59
|
borderRightStyle: 'solid',
|
|
60
|
-
|
|
60
|
+
// Override any borderColor set by pressed state — dividers always use border token
|
|
61
|
+
borderColor: colors.border,
|
|
61
62
|
},
|
|
62
63
|
disabled: {
|
|
63
64
|
opacity: 0.5,
|