@slipstream-ui/core 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/dist/index.d.mts +672 -52
- package/dist/index.d.ts +672 -52
- package/dist/index.js +1860 -657
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1836 -655
- package/dist/index.mjs.map +1 -1
- package/dist/manifests/app-shell.manifest.json +156 -0
- package/dist/manifests/auth-layout.manifest.json +99 -0
- package/dist/manifests/autocomplete.manifest.json +53 -5
- package/dist/manifests/badge.manifest.json +12 -0
- package/dist/manifests/button.manifest.json +6 -2
- package/dist/manifests/card.manifest.json +53 -6
- package/dist/manifests/date-picker.manifest.json +87 -0
- package/dist/manifests/dialog.manifest.json +49 -4
- package/dist/manifests/drawer.manifest.json +18 -4
- package/dist/manifests/empty-state.manifest.json +108 -0
- package/dist/manifests/file-upload.manifest.json +103 -0
- package/dist/manifests/filter-bar.manifest.json +100 -0
- package/dist/manifests/menu.manifest.json +13 -1
- package/dist/manifests/metric-card.manifest.json +105 -0
- package/dist/manifests/page-header.manifest.json +94 -0
- package/dist/manifests/pagination.manifest.json +62 -34
- package/dist/manifests/password-input.manifest.json +75 -0
- package/dist/manifests/select-menu.manifest.json +142 -0
- package/dist/manifests/select.manifest.json +49 -12
- package/dist/manifests/stepper.manifest.json +95 -0
- package/dist/manifests/table-card.manifest.json +123 -0
- package/dist/manifests/time-picker.manifest.json +86 -0
- package/dist/manifests/timeline.manifest.json +69 -0
- package/dist/manifests/toast.manifest.json +101 -25
- package/package.json +1 -1
- package/src/components/app-shell/AppShell.test.tsx +100 -0
- package/src/components/app-shell/AppShell.tsx +162 -0
- package/src/components/app-shell/index.ts +1 -0
- package/src/components/auth-layout/AuthLayout.test.tsx +34 -0
- package/src/components/auth-layout/AuthLayout.tsx +47 -0
- package/src/components/auth-layout/index.ts +1 -0
- package/src/components/autocomplete/Autocomplete.test.tsx +53 -0
- package/src/components/autocomplete/Autocomplete.tsx +160 -41
- package/src/components/badge/Badge.test.tsx +14 -0
- package/src/components/badge/Badge.tsx +10 -4
- package/src/components/button/Button.test.tsx +29 -0
- package/src/components/button/Button.tsx +10 -0
- package/src/components/button-group/ButtonGroup.test.tsx +6 -8
- package/src/components/button-group/ButtonGroup.tsx +17 -9
- package/src/components/card/Card.test.tsx +14 -0
- package/src/components/card/Card.tsx +15 -8
- package/src/components/date-picker/DatePicker.test.tsx +30 -0
- package/src/components/date-picker/DatePicker.tsx +57 -0
- package/src/components/date-picker/index.ts +1 -0
- package/src/components/dialog/Dialog.test.tsx +64 -1
- package/src/components/dialog/Dialog.tsx +113 -23
- package/src/components/dialog/index.ts +1 -2
- package/src/components/drawer/Drawer.test.tsx +50 -0
- package/src/components/drawer/Drawer.tsx +44 -6
- package/src/components/empty-state/EmptyState.test.tsx +30 -0
- package/src/components/empty-state/EmptyState.tsx +48 -0
- package/src/components/empty-state/index.ts +1 -0
- package/src/components/file-upload/FileUpload.test.tsx +43 -0
- package/src/components/file-upload/FileUpload.tsx +143 -0
- package/src/components/file-upload/index.ts +1 -0
- package/src/components/filter-bar/FilterBar.test.tsx +32 -0
- package/src/components/filter-bar/FilterBar.tsx +71 -0
- package/src/components/filter-bar/index.ts +1 -0
- package/src/components/input-group/InputGroup.test.tsx +6 -3
- package/src/components/input-group/InputGroup.tsx +9 -6
- package/src/components/menu/Menu.test.tsx +21 -0
- package/src/components/menu/Menu.tsx +8 -3
- package/src/components/metric-card/MetricCard.test.tsx +28 -0
- package/src/components/metric-card/MetricCard.tsx +54 -0
- package/src/components/metric-card/index.ts +1 -0
- package/src/components/page-header/PageHeader.test.tsx +31 -0
- package/src/components/page-header/PageHeader.tsx +37 -0
- package/src/components/page-header/index.ts +1 -0
- package/src/components/pagination/Pagination.test.tsx +25 -0
- package/src/components/pagination/Pagination.tsx +53 -12
- package/src/components/password-input/PasswordInput.test.tsx +29 -0
- package/src/components/password-input/PasswordInput.tsx +41 -0
- package/src/components/password-input/index.ts +1 -0
- package/src/components/select/Select.test.tsx +40 -1
- package/src/components/select/Select.tsx +52 -14
- package/src/components/select/index.ts +1 -1
- package/src/components/select-menu/SelectMenu.test.tsx +105 -0
- package/src/components/select-menu/SelectMenu.tsx +368 -0
- package/src/components/select-menu/index.ts +1 -0
- package/src/components/stepper/Stepper.test.tsx +35 -0
- package/src/components/stepper/Stepper.tsx +91 -0
- package/src/components/stepper/index.ts +1 -0
- package/src/components/table-card/TableCard.test.tsx +59 -0
- package/src/components/table-card/TableCard.tsx +48 -0
- package/src/components/table-card/index.ts +1 -0
- package/src/components/time-picker/TimePicker.test.tsx +30 -0
- package/src/components/time-picker/TimePicker.tsx +56 -0
- package/src/components/time-picker/index.ts +1 -0
- package/src/components/timeline/Timeline.test.tsx +26 -0
- package/src/components/timeline/Timeline.tsx +53 -0
- package/src/components/timeline/index.ts +1 -0
- package/src/components/toast/Toast.test.tsx +5 -0
- package/src/components/toast/Toast.tsx +2 -16
- package/src/components/toast/Toaster.test.tsx +84 -0
- package/src/components/toast/Toaster.tsx +116 -0
- package/src/components/toast/index.ts +4 -0
- package/src/components/toast/toast-store.ts +117 -0
- package/src/index.ts +14 -0
- package/src/manifests/app-shell.manifest.json +156 -0
- package/src/manifests/auth-layout.manifest.json +99 -0
- package/src/manifests/autocomplete.manifest.json +53 -5
- package/src/manifests/badge.manifest.json +12 -0
- package/src/manifests/button.manifest.json +6 -2
- package/src/manifests/card.manifest.json +53 -6
- package/src/manifests/date-picker.manifest.json +87 -0
- package/src/manifests/dialog.manifest.json +49 -4
- package/src/manifests/drawer.manifest.json +18 -4
- package/src/manifests/empty-state.manifest.json +108 -0
- package/src/manifests/file-upload.manifest.json +103 -0
- package/src/manifests/filter-bar.manifest.json +100 -0
- package/src/manifests/menu.manifest.json +13 -1
- package/src/manifests/metric-card.manifest.json +105 -0
- package/src/manifests/page-header.manifest.json +94 -0
- package/src/manifests/pagination.manifest.json +62 -34
- package/src/manifests/password-input.manifest.json +75 -0
- package/src/manifests/select-menu.manifest.json +142 -0
- package/src/manifests/select.manifest.json +49 -12
- package/src/manifests/stepper.manifest.json +95 -0
- package/src/manifests/table-card.manifest.json +123 -0
- package/src/manifests/time-picker.manifest.json +86 -0
- package/src/manifests/timeline.manifest.json +69 -0
- package/src/manifests/toast.manifest.json +101 -25
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "app-shell",
|
|
3
|
+
"name": "AppShell",
|
|
4
|
+
"tier": "organism",
|
|
5
|
+
"description": "Application frame with a sectioned sidebar, sticky header and scrolling main; the sidebar becomes a left Drawer on small screens.",
|
|
6
|
+
"designIntent": "Every dashboard hand-rolls the same shell: rail, header, scroll owner, and a mobile nav. AppShell owns the structure and responsive switch while staying presentational — you pass the active route and a router link component, so it works with React Router, Next Link, or plain anchors.",
|
|
7
|
+
"props": [
|
|
8
|
+
{
|
|
9
|
+
"name": "sidebar",
|
|
10
|
+
"type": "ReactNode",
|
|
11
|
+
"required": false,
|
|
12
|
+
"description": "Desktop rail content; also rendered inside the mobile drawer (AppShell)."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "header",
|
|
16
|
+
"type": "ReactNode",
|
|
17
|
+
"required": false,
|
|
18
|
+
"description": "Sticky top bar content (AppShell)."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "hideMobileMenu",
|
|
22
|
+
"type": "boolean",
|
|
23
|
+
"required": false,
|
|
24
|
+
"defaultValue": "false",
|
|
25
|
+
"description": "Hide the built-in burger button when the header owns navigation (AppShell)."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "sidebarWidth",
|
|
29
|
+
"type": "string",
|
|
30
|
+
"required": false,
|
|
31
|
+
"defaultValue": "w-64",
|
|
32
|
+
"description": "Tailwind width class for the desktop rail (AppShell)."
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "sections",
|
|
36
|
+
"type": "{ title?: string; items: SidebarItem[] }[]",
|
|
37
|
+
"required": true,
|
|
38
|
+
"description": "Grouped navigation items (Sidebar)."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "heading",
|
|
42
|
+
"type": "ReactNode",
|
|
43
|
+
"required": false,
|
|
44
|
+
"description": "Brand/mark at the top of the rail (Sidebar)."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "footer",
|
|
48
|
+
"type": "ReactNode",
|
|
49
|
+
"required": false,
|
|
50
|
+
"description": "Bottom area of the rail (Sidebar)."
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "activeHref",
|
|
54
|
+
"type": "string",
|
|
55
|
+
"required": false,
|
|
56
|
+
"description": "Current route; matching items get aria-current=page (Sidebar)."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "linkComponent",
|
|
60
|
+
"type": "React.ElementType",
|
|
61
|
+
"required": false,
|
|
62
|
+
"defaultValue": "a",
|
|
63
|
+
"description": "Router link component (react-router Link, Next Link) (Sidebar)."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "label",
|
|
67
|
+
"type": "string",
|
|
68
|
+
"required": false,
|
|
69
|
+
"defaultValue": "Primary",
|
|
70
|
+
"description": "Accessible name of the nav landmark (Sidebar)."
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"examples": [
|
|
74
|
+
{
|
|
75
|
+
"title": "Dashboard shell",
|
|
76
|
+
"code": "import { AppShell, Sidebar } from '@slipstream-ui/core';\n\n<AppShell\n header={<h1 className=\"text-sm font-semibold\">Studio</h1>}\n sidebar={\n <Sidebar\n activeHref={pathname}\n linkComponent={RouterLink}\n heading={<strong>Acme</strong>}\n sections={[\n { title: 'Workspace', items: [{ href: '/dashboard', label: 'Dashboard' }, { href: '/classes', label: 'Classes', badge: 4 }] },\n ]}\n />\n }\n>\n <Page />\n</AppShell>",
|
|
77
|
+
"description": "Router-agnostic: pass your Link component and the current path."
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"title": "Header only",
|
|
81
|
+
"code": "<AppShell header={<TopBar />}>\n <Page />\n</AppShell>",
|
|
82
|
+
"description": "Skip the sidebar when the product is a single surface."
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
"antiPatterns": [
|
|
86
|
+
{
|
|
87
|
+
"title": "Don't nest scroll containers",
|
|
88
|
+
"explanation": "AppShell's main is the scroll owner. Adding h-screen/overflow-hidden wrappers inside breaks sticky headers and mobile drawer focus.",
|
|
89
|
+
"example": "// Avoid\n<AppShell><div className=\"h-screen overflow-hidden\">…</div></AppShell>\n\n// Prefer\n<AppShell><Page /></AppShell>"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"title": "Don't hardcode anchors for SPA routes",
|
|
93
|
+
"explanation": "Plain <a href> triggers full page loads in client-side routers. Pass your router's Link via linkComponent.",
|
|
94
|
+
"example": "// Avoid (full reload in React Router)\n<Sidebar sections={sections} />\n\n// Prefer\n<Sidebar sections={sections} linkComponent={Link} />"
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"accessibility": [
|
|
98
|
+
{
|
|
99
|
+
"feature": "Nav landmark",
|
|
100
|
+
"implementation": "Sidebar renders nav with an accessible name (label prop)",
|
|
101
|
+
"userRequirements": "Keep one primary nav per page; use other labels for secondary rails"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"feature": "Current route",
|
|
105
|
+
"implementation": "aria-current=page on the active item",
|
|
106
|
+
"userRequirements": "Pass activeHref (or item.active) so the state is announced"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"feature": "Mobile drawer",
|
|
110
|
+
"implementation": "The rail becomes a Drawer with focus trap, Escape, focus restore and a labelled dialog",
|
|
111
|
+
"userRequirements": "Don't duplicate the nav in two places at once"
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"contentGuidelines": [
|
|
115
|
+
{
|
|
116
|
+
"contentType": "Item labels",
|
|
117
|
+
"guidelines": [
|
|
118
|
+
"Use nouns for destinations",
|
|
119
|
+
"Keep to 1-2 words",
|
|
120
|
+
"Group by workflow, not by component type"
|
|
121
|
+
],
|
|
122
|
+
"goodExamples": [
|
|
123
|
+
"Dashboard",
|
|
124
|
+
"Classes",
|
|
125
|
+
"Settings"
|
|
126
|
+
],
|
|
127
|
+
"badExamples": [
|
|
128
|
+
"Click here",
|
|
129
|
+
"List page component"
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
"compositionRules": [
|
|
134
|
+
{
|
|
135
|
+
"component": "Drawer",
|
|
136
|
+
"relationship": "AppShell mounts the mobile navigation Drawer for you when a sidebar is provided",
|
|
137
|
+
"example": "<AppShell sidebar={<Sidebar sections={sections} />}>…</AppShell>"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"component": "Button",
|
|
141
|
+
"relationship": "Header actions live in the header slot",
|
|
142
|
+
"example": "<AppShell header={<Button>New</Button>}>…</AppShell>"
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
"relatedComponents": [
|
|
146
|
+
"Drawer",
|
|
147
|
+
"Button",
|
|
148
|
+
"Breadcrumb",
|
|
149
|
+
"Menu"
|
|
150
|
+
],
|
|
151
|
+
"package": "@slipstream-ui/core",
|
|
152
|
+
"importPath": "@slipstream-ui/core",
|
|
153
|
+
"version": "0.1.0",
|
|
154
|
+
"sourceDir": "app-shell",
|
|
155
|
+
"exportName": "AppShell"
|
|
156
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "auth-layout",
|
|
3
|
+
"name": "AuthLayout",
|
|
4
|
+
"tier": "organism",
|
|
5
|
+
"description": "Centered authentication surface: brand, title/description, form card and footer, with size presets.",
|
|
6
|
+
"designIntent": "Sign-in, sign-up, reset and verify screens should look identical. AuthLayout owns the centered card, brand slot and footer so auth pages only supply the form.",
|
|
7
|
+
"props": [
|
|
8
|
+
{
|
|
9
|
+
"name": "title",
|
|
10
|
+
"type": "string",
|
|
11
|
+
"required": true,
|
|
12
|
+
"description": "Card heading (h1)."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "description",
|
|
16
|
+
"type": "string",
|
|
17
|
+
"required": false,
|
|
18
|
+
"description": "Supporting line under the heading."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "brand",
|
|
22
|
+
"type": "ReactNode",
|
|
23
|
+
"required": false,
|
|
24
|
+
"description": "Mark rendered above the card."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "footer",
|
|
28
|
+
"type": "ReactNode",
|
|
29
|
+
"required": false,
|
|
30
|
+
"description": "Links/legal copy under the card."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "size",
|
|
34
|
+
"type": "'sm' | 'md' | 'lg'",
|
|
35
|
+
"required": false,
|
|
36
|
+
"defaultValue": "md",
|
|
37
|
+
"description": "Card width preset.",
|
|
38
|
+
"enum": [
|
|
39
|
+
"sm",
|
|
40
|
+
"md",
|
|
41
|
+
"lg"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"examples": [
|
|
46
|
+
{
|
|
47
|
+
"title": "Sign in",
|
|
48
|
+
"code": "<AuthLayout\n title=\"Sign in\"\n description=\"Welcome back.\"\n brand={<Logo />}\n footer={<a href=\"/signup\">Create account</a>}\n>\n <SignInForm />\n</AuthLayout>",
|
|
49
|
+
"description": "Form lives inside the card."
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"antiPatterns": [
|
|
53
|
+
{
|
|
54
|
+
"title": "Don't nest another full-height page",
|
|
55
|
+
"explanation": "AuthLayout already owns min-h-screen centering.",
|
|
56
|
+
"example": "// Avoid\n<div className=\"min-h-screen\"><AuthLayout … /></div>"
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"accessibility": [
|
|
60
|
+
{
|
|
61
|
+
"feature": "One h1",
|
|
62
|
+
"implementation": "Renders the page heading",
|
|
63
|
+
"userRequirements": "Don't add another h1 inside the form"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"contentGuidelines": [
|
|
67
|
+
{
|
|
68
|
+
"contentType": "Titles",
|
|
69
|
+
"guidelines": [
|
|
70
|
+
"Action-oriented",
|
|
71
|
+
"Sentence case"
|
|
72
|
+
],
|
|
73
|
+
"goodExamples": [
|
|
74
|
+
"Sign in",
|
|
75
|
+
"Create your account"
|
|
76
|
+
],
|
|
77
|
+
"badExamples": [
|
|
78
|
+
"Login page"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
"compositionRules": [
|
|
83
|
+
{
|
|
84
|
+
"component": "Form",
|
|
85
|
+
"relationship": "Place the Form inside children",
|
|
86
|
+
"example": "<AuthLayout title=\"Sign in\"><Form onSubmit={…}>…</Form></AuthLayout>"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"relatedComponents": [
|
|
90
|
+
"Form",
|
|
91
|
+
"PasswordInput",
|
|
92
|
+
"Button"
|
|
93
|
+
],
|
|
94
|
+
"package": "@slipstream-ui/core",
|
|
95
|
+
"importPath": "@slipstream-ui/core",
|
|
96
|
+
"version": "0.1.0",
|
|
97
|
+
"sourceDir": "auth-layout",
|
|
98
|
+
"exportName": "AuthLayout"
|
|
99
|
+
}
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
"id": "autocomplete",
|
|
3
3
|
"name": "Autocomplete",
|
|
4
4
|
"tier": "molecule",
|
|
5
|
-
"description": "
|
|
6
|
-
"designIntent": "
|
|
5
|
+
"description": "Free-text type-ahead input with a portalled listbox: static or async options, grouped headings, descriptions and custom rows.",
|
|
6
|
+
"designIntent": "Free text stays first-class: any value may be typed, while matching options accelerate entry. Async sources are debounced with stale-response guards; grouped headings and custom rows cover searchable-select patterns; Escape/arrow keys follow the combobox model.",
|
|
7
7
|
"props": [
|
|
8
8
|
{
|
|
9
9
|
"name": "suggestions",
|
|
10
10
|
"type": "string[]",
|
|
11
|
-
"required":
|
|
12
|
-
"description": "
|
|
11
|
+
"required": false,
|
|
12
|
+
"description": "Deprecated — use options.",
|
|
13
13
|
"examples": [
|
|
14
14
|
"['react', 'remix', 'astro']"
|
|
15
15
|
]
|
|
@@ -43,13 +43,61 @@
|
|
|
43
43
|
"name": "emptyText",
|
|
44
44
|
"type": "string",
|
|
45
45
|
"required": false,
|
|
46
|
-
"description": "Shown when no
|
|
46
|
+
"description": "Shown when no option matches.",
|
|
47
47
|
"examples": [
|
|
48
48
|
"'No matches — press Enter to use your text'"
|
|
49
49
|
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "options",
|
|
53
|
+
"type": "Array<string | AutocompleteOption>",
|
|
54
|
+
"required": false,
|
|
55
|
+
"description": "Static options; strings or { value, label?, group?, description? }."
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "loadOptions",
|
|
59
|
+
"type": "(query: string) => Promise<Array<string | AutocompleteOption>>",
|
|
60
|
+
"required": false,
|
|
61
|
+
"description": "Async option source; debounced and guarded against stale responses."
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "renderOption",
|
|
65
|
+
"type": "(option, state: { active: boolean }) => ReactNode",
|
|
66
|
+
"required": false,
|
|
67
|
+
"description": "Custom row renderer."
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "clearable",
|
|
71
|
+
"type": "boolean",
|
|
72
|
+
"required": false,
|
|
73
|
+
"defaultValue": "false",
|
|
74
|
+
"description": "Shows a clear button once the field has text."
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "loadingText",
|
|
78
|
+
"type": "string",
|
|
79
|
+
"required": false,
|
|
80
|
+
"description": "Shown while async options load."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "debounceMs",
|
|
84
|
+
"type": "number",
|
|
85
|
+
"required": false,
|
|
86
|
+
"defaultValue": "200",
|
|
87
|
+
"description": "Debounce for loadOptions in ms."
|
|
50
88
|
}
|
|
51
89
|
],
|
|
52
90
|
"examples": [
|
|
91
|
+
{
|
|
92
|
+
"title": "Async search",
|
|
93
|
+
"code": "<Autocomplete\n aria-label=\"Search people\"\n clearable\n loadOptions={async (query) => searchPeople(query)}\n/>",
|
|
94
|
+
"description": "Debounced async source with stale-response guard and clear."
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"title": "Grouped options with descriptions",
|
|
98
|
+
"code": "<Autocomplete\n aria-label=\"Search\"\n options={[\n { value: 'ada', label: 'Ada Lovelace', group: 'People', description: 'Mathematician' },\n { value: 'rust', label: 'Rust', group: 'Languages' },\n ]}\n/>",
|
|
99
|
+
"description": "Section headings plus a supporting line per row."
|
|
100
|
+
},
|
|
53
101
|
{
|
|
54
102
|
"title": "Controlled form field",
|
|
55
103
|
"code": "const [framework, setFramework] = useState('');\n\n<FormField id=\"framework\" label=\"Primary framework\" hint=\"Type or pick a suggestion\">\n <Autocomplete\n suggestions={['react', 'remix', 'next', 'vite']}\n value={framework}\n onValueChange={setFramework}\n name=\"framework\"\n autoComplete=\"off\"\n />\n</FormField>",
|
|
@@ -40,9 +40,21 @@
|
|
|
40
40
|
"type": "string",
|
|
41
41
|
"required": false,
|
|
42
42
|
"description": "Additional CSS classes to apply"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "asChild",
|
|
46
|
+
"type": "boolean",
|
|
47
|
+
"required": false,
|
|
48
|
+
"defaultValue": "false",
|
|
49
|
+
"description": "Render the child element (e.g. a router Link) with badge styling."
|
|
43
50
|
}
|
|
44
51
|
],
|
|
45
52
|
"examples": [
|
|
53
|
+
{
|
|
54
|
+
"title": "Tag linking to a filter",
|
|
55
|
+
"code": "<Badge asChild>\n <Link href=\"/tags/react\">React</Link>\n</Badge>",
|
|
56
|
+
"description": "Tag that navigates."
|
|
57
|
+
},
|
|
46
58
|
{
|
|
47
59
|
"title": "Default badge",
|
|
48
60
|
"code": "<Badge>New</Badge>",
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"props": [
|
|
8
8
|
{
|
|
9
9
|
"name": "variant",
|
|
10
|
-
"type": "'default' | 'destructive' | 'success' | 'warning' | 'info' | 'outline' | 'secondary' | 'ghost' | 'link'",
|
|
10
|
+
"type": "'default' | 'destructive' | 'success' | 'warning' | 'info' | 'outline' | 'outline-destructive' | 'outline-success' | 'outline-warning' | 'outline-info' | 'secondary' | 'ghost' | 'link'",
|
|
11
11
|
"defaultValue": "default",
|
|
12
12
|
"required": false,
|
|
13
13
|
"description": "Visual style variant of the button",
|
|
@@ -20,7 +20,11 @@
|
|
|
20
20
|
"link",
|
|
21
21
|
"success",
|
|
22
22
|
"warning",
|
|
23
|
-
"info"
|
|
23
|
+
"info",
|
|
24
|
+
"outline-destructive",
|
|
25
|
+
"outline-success",
|
|
26
|
+
"outline-warning",
|
|
27
|
+
"outline-info"
|
|
24
28
|
],
|
|
25
29
|
"examples": [
|
|
26
30
|
"default",
|
|
@@ -5,10 +5,32 @@
|
|
|
5
5
|
"description": "A content container with header, body, and footer sections for grouping related information",
|
|
6
6
|
"designIntent": "Card groups related content into a scannable container. Use it for settings panels, feature highlights, user profiles, and any content that benefits from visual grouping.",
|
|
7
7
|
"props": [
|
|
8
|
-
{
|
|
9
|
-
|
|
8
|
+
{
|
|
9
|
+
"name": "children",
|
|
10
|
+
"type": "ReactNode",
|
|
11
|
+
"required": true,
|
|
12
|
+
"description": "Card sub-components (CardHeader, CardContent, CardFooter)"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "className",
|
|
16
|
+
"type": "string",
|
|
17
|
+
"required": false,
|
|
18
|
+
"description": "Additional CSS classes to apply"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "asChild",
|
|
22
|
+
"type": "boolean",
|
|
23
|
+
"required": false,
|
|
24
|
+
"defaultValue": "false",
|
|
25
|
+
"description": "Render the child element (router Link, <a>) with card styling instead of a div."
|
|
26
|
+
}
|
|
10
27
|
],
|
|
11
28
|
"examples": [
|
|
29
|
+
{
|
|
30
|
+
"title": "Card as a link",
|
|
31
|
+
"code": "<Card asChild>\n <Link href=\"/writing/hello\">\n <CardHeader><CardTitle>Hello</CardTitle></CardHeader>\n </Link>\n</Card>",
|
|
32
|
+
"description": "Whole card becomes a semantic link without losing styling."
|
|
33
|
+
},
|
|
12
34
|
{
|
|
13
35
|
"title": "Basic card",
|
|
14
36
|
"code": "<Card>\n <CardHeader>\n <CardTitle>Profile</CardTitle>\n <CardDescription>Manage your account settings</CardDescription>\n </CardHeader>\n <CardContent>Form fields here</CardContent>\n <CardFooter>\n <Button>Save</Button>\n </CardFooter>\n</Card>",
|
|
@@ -28,15 +50,40 @@
|
|
|
28
50
|
}
|
|
29
51
|
],
|
|
30
52
|
"accessibility": [
|
|
31
|
-
{
|
|
53
|
+
{
|
|
54
|
+
"feature": "Heading hierarchy",
|
|
55
|
+
"implementation": "CardTitle renders as h3",
|
|
56
|
+
"userRequirements": "Maintain logical heading order in your page"
|
|
57
|
+
}
|
|
32
58
|
],
|
|
33
59
|
"contentGuidelines": [
|
|
34
|
-
{
|
|
60
|
+
{
|
|
61
|
+
"contentType": "Card titles",
|
|
62
|
+
"guidelines": [
|
|
63
|
+
"Keep titles short (2-5 words)",
|
|
64
|
+
"Use sentence case"
|
|
65
|
+
],
|
|
66
|
+
"goodExamples": [
|
|
67
|
+
"Profile settings",
|
|
68
|
+
"Billing overview"
|
|
69
|
+
],
|
|
70
|
+
"badExamples": [
|
|
71
|
+
"Card title",
|
|
72
|
+
"Section"
|
|
73
|
+
]
|
|
74
|
+
}
|
|
35
75
|
],
|
|
36
76
|
"compositionRules": [
|
|
37
|
-
{
|
|
77
|
+
{
|
|
78
|
+
"component": "Button",
|
|
79
|
+
"relationship": "Place primary actions in CardFooter",
|
|
80
|
+
"example": "<CardFooter><Button>Save</Button></CardFooter>"
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"relatedComponents": [
|
|
84
|
+
"Separator",
|
|
85
|
+
"Tabs"
|
|
38
86
|
],
|
|
39
|
-
"relatedComponents": ["Separator", "Tabs"],
|
|
40
87
|
"package": "@slipstream-ui/core",
|
|
41
88
|
"importPath": "@slipstream-ui/core",
|
|
42
89
|
"version": "0.1.0",
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "date-picker",
|
|
3
|
+
"name": "DatePicker",
|
|
4
|
+
"tier": "molecule",
|
|
5
|
+
"description": "Native date field with an ISO-string contract, min/max passthrough and an optional clear button.",
|
|
6
|
+
"designIntent": "A custom calendar is a large accessibility surface. The platform date picker already handles locale formatting, keyboard editing and mobile wheels, so DatePicker standardizes the ISO contract and a clear affordance instead of reimplementing a calendar.",
|
|
7
|
+
"props": [
|
|
8
|
+
{
|
|
9
|
+
"name": "value",
|
|
10
|
+
"type": "string",
|
|
11
|
+
"required": false,
|
|
12
|
+
"description": "ISO date (YYYY-MM-DD), controlled."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "defaultValue",
|
|
16
|
+
"type": "string",
|
|
17
|
+
"required": false,
|
|
18
|
+
"description": "ISO date, initial value."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "onValueChange",
|
|
22
|
+
"type": "(value: string) => void",
|
|
23
|
+
"required": false,
|
|
24
|
+
"description": "Fired with the ISO string; '' when cleared."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "clearable",
|
|
28
|
+
"type": "boolean",
|
|
29
|
+
"required": false,
|
|
30
|
+
"defaultValue": "false",
|
|
31
|
+
"description": "Shows a clear button once a value exists."
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"examples": [
|
|
35
|
+
{
|
|
36
|
+
"title": "ISO date field",
|
|
37
|
+
"code": "<DatePicker\n aria-label=\"Start date\"\n value={start}\n onValueChange={setStart}\n clearable\n/>",
|
|
38
|
+
"description": "Controlled ISO value with a clear button."
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"antiPatterns": [
|
|
42
|
+
{
|
|
43
|
+
"title": "Don't store locale-formatted strings",
|
|
44
|
+
"explanation": "The contract is YYYY-MM-DD; format for display at the edge.",
|
|
45
|
+
"example": "// Avoid: '03/11/2026' in state\n// Prefer: '2026-11-03'"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"accessibility": [
|
|
49
|
+
{
|
|
50
|
+
"feature": "Platform input",
|
|
51
|
+
"implementation": "Renders input[type=date] so OS pickers and keyboard editing apply",
|
|
52
|
+
"userRequirements": "Always provide a label (FormField or aria-label)"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"contentGuidelines": [
|
|
56
|
+
{
|
|
57
|
+
"contentType": "Labels",
|
|
58
|
+
"guidelines": [
|
|
59
|
+
"Name the date's meaning, not 'Date'"
|
|
60
|
+
],
|
|
61
|
+
"goodExamples": [
|
|
62
|
+
"Start date",
|
|
63
|
+
"Due on"
|
|
64
|
+
],
|
|
65
|
+
"badExamples": [
|
|
66
|
+
"Date"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"compositionRules": [
|
|
71
|
+
{
|
|
72
|
+
"component": "FormField",
|
|
73
|
+
"relationship": "Wrap for label/hint/error wiring",
|
|
74
|
+
"example": "<FormField id=\"start\" label=\"Start date\"><DatePicker /></FormField>"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"relatedComponents": [
|
|
78
|
+
"TimePicker",
|
|
79
|
+
"FormField",
|
|
80
|
+
"Input"
|
|
81
|
+
],
|
|
82
|
+
"package": "@slipstream-ui/core",
|
|
83
|
+
"importPath": "@slipstream-ui/core",
|
|
84
|
+
"version": "0.1.0",
|
|
85
|
+
"sourceDir": "date-picker",
|
|
86
|
+
"exportName": "DatePicker"
|
|
87
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "dialog",
|
|
3
3
|
"name": "Dialog",
|
|
4
4
|
"tier": "molecule",
|
|
5
|
-
"description": "Accessible modal dialog with focus
|
|
5
|
+
"description": "Accessible modal dialog with focus trap/restore, backdrop and Escape dismissal, size presets, optional header/footer slots, a scrollable body, and opt-in fade motion.",
|
|
6
6
|
"designIntent": "Modals interrupt the task flow on purpose: confirmations, small forms, and destructive-action guards. The Dialog implements the full modal contract — aria-modal labelling, initial focus, a Tab focus trap, Escape and backdrop dismissal, and focus restoration to the opener — so consumers never hand-roll focus management.",
|
|
7
7
|
"props": [
|
|
8
8
|
{
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
{
|
|
21
21
|
"name": "title",
|
|
22
22
|
"type": "string",
|
|
23
|
-
"required":
|
|
24
|
-
"description": "Accessible
|
|
23
|
+
"required": false,
|
|
24
|
+
"description": "Accessible name + default header title. Optional when composing a custom header with ariaLabel."
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"name": "description",
|
|
@@ -69,6 +69,49 @@
|
|
|
69
69
|
"true",
|
|
70
70
|
"false"
|
|
71
71
|
]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "size",
|
|
75
|
+
"type": "'sm' | 'md' | 'lg' | 'xl' | '2xl'",
|
|
76
|
+
"required": false,
|
|
77
|
+
"defaultValue": "md",
|
|
78
|
+
"description": "Panel max-width preset.",
|
|
79
|
+
"enum": [
|
|
80
|
+
"sm",
|
|
81
|
+
"md",
|
|
82
|
+
"lg",
|
|
83
|
+
"xl",
|
|
84
|
+
"2xl"
|
|
85
|
+
],
|
|
86
|
+
"examples": [
|
|
87
|
+
"lg",
|
|
88
|
+
"2xl"
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"name": "header",
|
|
93
|
+
"type": "ReactNode",
|
|
94
|
+
"required": false,
|
|
95
|
+
"description": "Replaces the default header row — compose with DialogHeader/DialogTitle/DialogDescription (ids wire automatically)."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "footer",
|
|
99
|
+
"type": "ReactNode",
|
|
100
|
+
"required": false,
|
|
101
|
+
"description": "Action row pinned under the body."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"name": "scrollable",
|
|
105
|
+
"type": "boolean",
|
|
106
|
+
"required": false,
|
|
107
|
+
"defaultValue": "false",
|
|
108
|
+
"description": "Caps the body height and scrolls inside the panel."
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "bodyClassName",
|
|
112
|
+
"type": "string",
|
|
113
|
+
"required": false,
|
|
114
|
+
"description": "Extra classes for the body wrapper."
|
|
72
115
|
}
|
|
73
116
|
],
|
|
74
117
|
"examples": [
|
|
@@ -107,7 +150,9 @@
|
|
|
107
150
|
],
|
|
108
151
|
"relatedComponents": [
|
|
109
152
|
"Button",
|
|
110
|
-
"Toast"
|
|
153
|
+
"Toast",
|
|
154
|
+
"Drawer",
|
|
155
|
+
"Confirmation"
|
|
111
156
|
],
|
|
112
157
|
"package": "@slipstream-ui/core",
|
|
113
158
|
"importPath": "@slipstream-ui/core",
|
|
@@ -41,12 +41,26 @@
|
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"name": "size",
|
|
44
|
-
"type": "'sm' | 'md' | 'lg'",
|
|
44
|
+
"type": "'sm' | 'md' | 'lg' | 'xl' | '2xl'",
|
|
45
45
|
"defaultValue": "md",
|
|
46
46
|
"required": false,
|
|
47
|
-
"description": "
|
|
48
|
-
"enum": ["sm", "md", "lg"],
|
|
49
|
-
"examples": ["lg"]
|
|
47
|
+
"description": "Preset extent along the side axis (width for left/right, height for top/bottom).",
|
|
48
|
+
"enum": ["sm", "md", "lg", "xl", "2xl"],
|
|
49
|
+
"examples": ["lg", "xl"]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "width",
|
|
53
|
+
"type": "number | string",
|
|
54
|
+
"required": false,
|
|
55
|
+
"description": "Custom extent for left/right drawers: pixels (480) or any CSS length (\"70vw\"). Overrides size.",
|
|
56
|
+
"examples": [480, "70vw"]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "height",
|
|
60
|
+
"type": "number | string",
|
|
61
|
+
"required": false,
|
|
62
|
+
"description": "Custom extent for top/bottom drawers: pixels (320) or any CSS length (\"60vh\"). Overrides size.",
|
|
63
|
+
"examples": [320, "60vh"]
|
|
50
64
|
},
|
|
51
65
|
{
|
|
52
66
|
"name": "closeOnBackdrop",
|