@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.
Files changed (128) hide show
  1. package/dist/index.d.mts +672 -52
  2. package/dist/index.d.ts +672 -52
  3. package/dist/index.js +1860 -657
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1836 -655
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/manifests/app-shell.manifest.json +156 -0
  8. package/dist/manifests/auth-layout.manifest.json +99 -0
  9. package/dist/manifests/autocomplete.manifest.json +53 -5
  10. package/dist/manifests/badge.manifest.json +12 -0
  11. package/dist/manifests/button.manifest.json +6 -2
  12. package/dist/manifests/card.manifest.json +53 -6
  13. package/dist/manifests/date-picker.manifest.json +87 -0
  14. package/dist/manifests/dialog.manifest.json +49 -4
  15. package/dist/manifests/drawer.manifest.json +18 -4
  16. package/dist/manifests/empty-state.manifest.json +108 -0
  17. package/dist/manifests/file-upload.manifest.json +103 -0
  18. package/dist/manifests/filter-bar.manifest.json +100 -0
  19. package/dist/manifests/menu.manifest.json +13 -1
  20. package/dist/manifests/metric-card.manifest.json +105 -0
  21. package/dist/manifests/page-header.manifest.json +94 -0
  22. package/dist/manifests/pagination.manifest.json +62 -34
  23. package/dist/manifests/password-input.manifest.json +75 -0
  24. package/dist/manifests/select-menu.manifest.json +142 -0
  25. package/dist/manifests/select.manifest.json +49 -12
  26. package/dist/manifests/stepper.manifest.json +95 -0
  27. package/dist/manifests/table-card.manifest.json +123 -0
  28. package/dist/manifests/time-picker.manifest.json +86 -0
  29. package/dist/manifests/timeline.manifest.json +69 -0
  30. package/dist/manifests/toast.manifest.json +101 -25
  31. package/package.json +1 -1
  32. package/src/components/app-shell/AppShell.test.tsx +100 -0
  33. package/src/components/app-shell/AppShell.tsx +162 -0
  34. package/src/components/app-shell/index.ts +1 -0
  35. package/src/components/auth-layout/AuthLayout.test.tsx +34 -0
  36. package/src/components/auth-layout/AuthLayout.tsx +47 -0
  37. package/src/components/auth-layout/index.ts +1 -0
  38. package/src/components/autocomplete/Autocomplete.test.tsx +53 -0
  39. package/src/components/autocomplete/Autocomplete.tsx +160 -41
  40. package/src/components/badge/Badge.test.tsx +14 -0
  41. package/src/components/badge/Badge.tsx +10 -4
  42. package/src/components/button/Button.test.tsx +29 -0
  43. package/src/components/button/Button.tsx +10 -0
  44. package/src/components/button-group/ButtonGroup.test.tsx +6 -8
  45. package/src/components/button-group/ButtonGroup.tsx +17 -9
  46. package/src/components/card/Card.test.tsx +14 -0
  47. package/src/components/card/Card.tsx +15 -8
  48. package/src/components/date-picker/DatePicker.test.tsx +30 -0
  49. package/src/components/date-picker/DatePicker.tsx +57 -0
  50. package/src/components/date-picker/index.ts +1 -0
  51. package/src/components/dialog/Dialog.test.tsx +64 -1
  52. package/src/components/dialog/Dialog.tsx +113 -23
  53. package/src/components/dialog/index.ts +1 -2
  54. package/src/components/drawer/Drawer.test.tsx +50 -0
  55. package/src/components/drawer/Drawer.tsx +44 -6
  56. package/src/components/empty-state/EmptyState.test.tsx +30 -0
  57. package/src/components/empty-state/EmptyState.tsx +48 -0
  58. package/src/components/empty-state/index.ts +1 -0
  59. package/src/components/file-upload/FileUpload.test.tsx +43 -0
  60. package/src/components/file-upload/FileUpload.tsx +143 -0
  61. package/src/components/file-upload/index.ts +1 -0
  62. package/src/components/filter-bar/FilterBar.test.tsx +32 -0
  63. package/src/components/filter-bar/FilterBar.tsx +71 -0
  64. package/src/components/filter-bar/index.ts +1 -0
  65. package/src/components/input-group/InputGroup.test.tsx +6 -3
  66. package/src/components/input-group/InputGroup.tsx +9 -6
  67. package/src/components/menu/Menu.test.tsx +21 -0
  68. package/src/components/menu/Menu.tsx +8 -3
  69. package/src/components/metric-card/MetricCard.test.tsx +28 -0
  70. package/src/components/metric-card/MetricCard.tsx +54 -0
  71. package/src/components/metric-card/index.ts +1 -0
  72. package/src/components/page-header/PageHeader.test.tsx +31 -0
  73. package/src/components/page-header/PageHeader.tsx +37 -0
  74. package/src/components/page-header/index.ts +1 -0
  75. package/src/components/pagination/Pagination.test.tsx +25 -0
  76. package/src/components/pagination/Pagination.tsx +53 -12
  77. package/src/components/password-input/PasswordInput.test.tsx +29 -0
  78. package/src/components/password-input/PasswordInput.tsx +41 -0
  79. package/src/components/password-input/index.ts +1 -0
  80. package/src/components/select/Select.test.tsx +40 -1
  81. package/src/components/select/Select.tsx +52 -14
  82. package/src/components/select/index.ts +1 -1
  83. package/src/components/select-menu/SelectMenu.test.tsx +105 -0
  84. package/src/components/select-menu/SelectMenu.tsx +368 -0
  85. package/src/components/select-menu/index.ts +1 -0
  86. package/src/components/stepper/Stepper.test.tsx +35 -0
  87. package/src/components/stepper/Stepper.tsx +91 -0
  88. package/src/components/stepper/index.ts +1 -0
  89. package/src/components/table-card/TableCard.test.tsx +59 -0
  90. package/src/components/table-card/TableCard.tsx +48 -0
  91. package/src/components/table-card/index.ts +1 -0
  92. package/src/components/time-picker/TimePicker.test.tsx +30 -0
  93. package/src/components/time-picker/TimePicker.tsx +56 -0
  94. package/src/components/time-picker/index.ts +1 -0
  95. package/src/components/timeline/Timeline.test.tsx +26 -0
  96. package/src/components/timeline/Timeline.tsx +53 -0
  97. package/src/components/timeline/index.ts +1 -0
  98. package/src/components/toast/Toast.test.tsx +5 -0
  99. package/src/components/toast/Toast.tsx +2 -16
  100. package/src/components/toast/Toaster.test.tsx +84 -0
  101. package/src/components/toast/Toaster.tsx +116 -0
  102. package/src/components/toast/index.ts +4 -0
  103. package/src/components/toast/toast-store.ts +117 -0
  104. package/src/index.ts +14 -0
  105. package/src/manifests/app-shell.manifest.json +156 -0
  106. package/src/manifests/auth-layout.manifest.json +99 -0
  107. package/src/manifests/autocomplete.manifest.json +53 -5
  108. package/src/manifests/badge.manifest.json +12 -0
  109. package/src/manifests/button.manifest.json +6 -2
  110. package/src/manifests/card.manifest.json +53 -6
  111. package/src/manifests/date-picker.manifest.json +87 -0
  112. package/src/manifests/dialog.manifest.json +49 -4
  113. package/src/manifests/drawer.manifest.json +18 -4
  114. package/src/manifests/empty-state.manifest.json +108 -0
  115. package/src/manifests/file-upload.manifest.json +103 -0
  116. package/src/manifests/filter-bar.manifest.json +100 -0
  117. package/src/manifests/menu.manifest.json +13 -1
  118. package/src/manifests/metric-card.manifest.json +105 -0
  119. package/src/manifests/page-header.manifest.json +94 -0
  120. package/src/manifests/pagination.manifest.json +62 -34
  121. package/src/manifests/password-input.manifest.json +75 -0
  122. package/src/manifests/select-menu.manifest.json +142 -0
  123. package/src/manifests/select.manifest.json +49 -12
  124. package/src/manifests/stepper.manifest.json +95 -0
  125. package/src/manifests/table-card.manifest.json +123 -0
  126. package/src/manifests/time-picker.manifest.json +86 -0
  127. package/src/manifests/timeline.manifest.json +69 -0
  128. package/src/manifests/toast.manifest.json +101 -25
@@ -0,0 +1,75 @@
1
+ {
2
+ "id": "password-input",
3
+ "name": "PasswordInput",
4
+ "tier": "molecule",
5
+ "description": "Password field with an accessible show/hide toggle (aria-pressed), built on Input.",
6
+ "designIntent": "Users mistype passwords; a reveal toggle reduces failed sign-ins. The toggle must be a real button with a pressed state and a proper label so it is not a mystery control.",
7
+ "props": [
8
+ {
9
+ "name": "showLabel",
10
+ "type": "string",
11
+ "required": false,
12
+ "defaultValue": "Show password",
13
+ "description": "Accessible label while hidden."
14
+ },
15
+ {
16
+ "name": "hideLabel",
17
+ "type": "string",
18
+ "required": false,
19
+ "defaultValue": "Hide password",
20
+ "description": "Accessible label while visible."
21
+ }
22
+ ],
23
+ "examples": [
24
+ {
25
+ "title": "Sign-in field",
26
+ "code": "<PasswordInput aria-label=\"Password\" autoComplete=\"current-password\" />",
27
+ "description": "Input props pass through."
28
+ }
29
+ ],
30
+ "antiPatterns": [
31
+ {
32
+ "title": "Don't reveal by default",
33
+ "explanation": "Start masked; reveal is user-initiated.",
34
+ "example": "// Avoid: type=\"text\" for a password\n<Input type=\"text\" aria-label=\"Password\" />\n\n// Prefer\n<PasswordInput aria-label=\"Password\" />"
35
+ }
36
+ ],
37
+ "accessibility": [
38
+ {
39
+ "feature": "Toggle semantics",
40
+ "implementation": "Button with aria-pressed and Show/Hide labels",
41
+ "userRequirements": "Keep both labels descriptive"
42
+ }
43
+ ],
44
+ "contentGuidelines": [
45
+ {
46
+ "contentType": "Labels",
47
+ "guidelines": [
48
+ "Verb-first toggle labels"
49
+ ],
50
+ "goodExamples": [
51
+ "Show password"
52
+ ],
53
+ "badExamples": [
54
+ "Eye"
55
+ ]
56
+ }
57
+ ],
58
+ "compositionRules": [
59
+ {
60
+ "component": "FormField",
61
+ "relationship": "Wrap for label/hint/error wiring",
62
+ "example": "<FormField id=\"pw\" label=\"Password\"><PasswordInput /></FormField>"
63
+ }
64
+ ],
65
+ "relatedComponents": [
66
+ "Input",
67
+ "FormField",
68
+ "InputGroup"
69
+ ],
70
+ "package": "@slipstream-ui/core",
71
+ "importPath": "@slipstream-ui/core",
72
+ "version": "0.1.0",
73
+ "sourceDir": "password-input",
74
+ "exportName": "PasswordInput"
75
+ }
@@ -0,0 +1,142 @@
1
+ {
2
+ "id": "select-menu",
3
+ "name": "SelectMenu",
4
+ "tier": "molecule",
5
+ "package": "@slipstream-ui/core",
6
+ "importPath": "@slipstream-ui/core",
7
+ "version": "0.1.0",
8
+ "sourceDir": "select-menu",
9
+ "exportName": "SelectMenu",
10
+ "description": "Custom listbox select for rich options: descriptions, icons, groups, multi-select, custom rows and hidden inputs for forms.",
11
+ "designIntent": "Native <select> cannot render rich option content. SelectMenu supplies a real listbox with the button/listbox keyboard model while keeping the same value/onValueChange contract as Select, so teams can move between the two without rewriting state.",
12
+ "props": [
13
+ {
14
+ "name": "items",
15
+ "type": "SelectMenuItem[]",
16
+ "required": true,
17
+ "description": "Options: { value, label, description?, icon?, disabled?, group? }."
18
+ },
19
+ {
20
+ "name": "value",
21
+ "type": "string | string[]",
22
+ "required": false,
23
+ "description": "Controlled value(s); string[] when multiple."
24
+ },
25
+ {
26
+ "name": "defaultValue",
27
+ "type": "string | string[]",
28
+ "required": false,
29
+ "description": "Initial value(s) when uncontrolled."
30
+ },
31
+ {
32
+ "name": "onValueChange",
33
+ "type": "(value: string | string[]) => void",
34
+ "required": false,
35
+ "description": "Fired with the new selection."
36
+ },
37
+ {
38
+ "name": "multiple",
39
+ "type": "boolean",
40
+ "required": false,
41
+ "defaultValue": "false",
42
+ "description": "Allow several selections; the listbox stays open."
43
+ },
44
+ {
45
+ "name": "placeholder",
46
+ "type": "string",
47
+ "required": false,
48
+ "defaultValue": "Select…",
49
+ "description": "Shown when nothing is selected."
50
+ },
51
+ {
52
+ "name": "name",
53
+ "type": "string",
54
+ "required": false,
55
+ "description": "Renders hidden form inputs for the selected values."
56
+ },
57
+ {
58
+ "name": "renderValue",
59
+ "type": "(selected: SelectMenuItem[]) => ReactNode",
60
+ "required": false,
61
+ "description": "Custom trigger content."
62
+ },
63
+ {
64
+ "name": "renderItem",
65
+ "type": "(item, state: { selected: boolean; active: boolean }) => ReactNode",
66
+ "required": false,
67
+ "description": "Custom option row."
68
+ },
69
+ {
70
+ "name": "emptyText",
71
+ "type": "string",
72
+ "required": false,
73
+ "defaultValue": "No options",
74
+ "description": "Shown when items is empty."
75
+ }
76
+ ],
77
+ "examples": [
78
+ {
79
+ "title": "Rich options",
80
+ "code": "<SelectMenu\n aria-label=\"Plan\"\n placeholder=\"Choose a plan\"\n items={[\n { value: 'free', label: 'Free', description: '1 project', group: 'Individual' },\n { value: 'team', label: 'Team', description: 'SSO + audit log', group: 'Business' },\n ]}\n value={plan}\n onValueChange={setPlan}\n/>",
81
+ "description": "Descriptions and grouped headings inside a real listbox."
82
+ },
83
+ {
84
+ "title": "Multi-select with form input",
85
+ "code": "<SelectMenu\n aria-label=\"Channels\"\n multiple\n name=\"channels\"\n items={channels}\n value={selected}\n onValueChange={setSelected}\n/>",
86
+ "description": "Hidden inputs make the selection submit with the form."
87
+ }
88
+ ],
89
+ "antiPatterns": [
90
+ {
91
+ "title": "Don't use it for free-text entry",
92
+ "explanation": "SelectMenu has no text input. Use Autocomplete when typing a value is expected.",
93
+ "example": "// Avoid: 200 options behind a menu\n<SelectMenu items={allCities} />\n\n// Prefer\n<Autocomplete loadOptions={searchCities} />"
94
+ },
95
+ {
96
+ "title": "Don't render more than ~50 options",
97
+ "explanation": "There is no filtering. Large sets need Autocomplete or a searchable flow.",
98
+ "example": "// Avoid\n<SelectMenu items={tenThousandRows} />"
99
+ }
100
+ ],
101
+ "accessibility": [
102
+ {
103
+ "feature": "Keyboard model",
104
+ "implementation": "Arrows move the active option, Home/End jump, Enter/Space select, Escape closes, typeahead jumps by first letters",
105
+ "userRequirements": "Keep option labels distinct so typeahead stays predictable"
106
+ }
107
+ ],
108
+ "contentGuidelines": [
109
+ {
110
+ "contentType": "Option labels",
111
+ "guidelines": [
112
+ "Noun phrases",
113
+ "Use description for the second line",
114
+ "Parallel phrasing across options"
115
+ ],
116
+ "goodExamples": [
117
+ "Pro — Unlimited projects"
118
+ ],
119
+ "badExamples": [
120
+ "Click to pick the pro plan option"
121
+ ]
122
+ }
123
+ ],
124
+ "compositionRules": [
125
+ {
126
+ "component": "FormField",
127
+ "relationship": "Wrap for label/hint/error wiring",
128
+ "example": "<FormField id=\"plan\" label=\"Plan\"><SelectMenu items={items} /></FormField>"
129
+ },
130
+ {
131
+ "component": "Select",
132
+ "relationship": "Prefer Select when options are plain text (native pickers, mobile UX)",
133
+ "example": "<Select options={plain} />"
134
+ }
135
+ ],
136
+ "relatedComponents": [
137
+ "Select",
138
+ "Autocomplete",
139
+ "Menu",
140
+ "FormField"
141
+ ]
142
+ }
@@ -2,15 +2,20 @@
2
2
  "id": "select",
3
3
  "name": "Select",
4
4
  "tier": "atom",
5
- "description": "Accessible select primitive built on the native select element with controlled and uncontrolled modes",
6
- "designIntent": "Most dropdown needs are satisfied by the platform select: it brings complete keyboard, screen-reader and mobile behavior for free and never strands focus. This primitive adds consistent styling and a typed options API without re-implementing a listbox.",
5
+ "description": "Accessible select built on the native <select> with flat options, optgroups, a placeholder row and native multi-select.",
6
+ "designIntent": "Native selects keep platform keyboard, mobile picker and screen-reader behavior for free. Groups cover coarse categorization, placeholder renders the 'choose one' affordance, and multiple switches the contract to string[] for tag-picker use cases without reimplementing a listbox.",
7
7
  "props": [
8
8
  {
9
9
  "name": "options",
10
10
  "type": "SelectOption[]",
11
- "required": true,
12
- "description": "Options to render; each is { value, label, disabled? }",
13
- "examples": [{ "value": "sm", "label": "Small" }]
11
+ "required": false,
12
+ "description": "Flat options ({ value, label, disabled? }). Ignored when groups is provided.",
13
+ "examples": [
14
+ {
15
+ "value": "sm",
16
+ "label": "Small"
17
+ }
18
+ ]
14
19
  },
15
20
  {
16
21
  "name": "label",
@@ -20,24 +25,53 @@
20
25
  },
21
26
  {
22
27
  "name": "value",
23
- "type": "string",
28
+ "type": "string | string[]",
24
29
  "required": false,
25
- "description": "Controlled selected value"
30
+ "description": "Controlled value (string, or string[] when multiple)."
26
31
  },
27
32
  {
28
33
  "name": "defaultValue",
29
- "type": "string",
34
+ "type": "string | string[]",
30
35
  "required": false,
31
- "description": "Uncontrolled initial value"
36
+ "description": "Initial value when uncontrolled."
32
37
  },
33
38
  {
34
39
  "name": "onChange",
35
- "type": "(value: string) => void",
40
+ "type": "(value: string | string[]) => void",
41
+ "required": false,
42
+ "description": "Fired with the new value; string[] when multiple."
43
+ },
44
+ {
45
+ "name": "groups",
46
+ "type": "SelectOptionGroup[]",
47
+ "required": false,
48
+ "description": "Optgroups: [{ label, options }] rendered with a heading per section."
49
+ },
50
+ {
51
+ "name": "placeholder",
52
+ "type": "string",
36
53
  "required": false,
37
- "description": "Called with the newly selected value"
54
+ "description": "Renders a disabled empty option as the 'choose one' affordance."
55
+ },
56
+ {
57
+ "name": "multiple",
58
+ "type": "boolean",
59
+ "required": false,
60
+ "defaultValue": "false",
61
+ "description": "Native multi-select; value/defaultValue/onChange switch to string[]."
38
62
  }
39
63
  ],
40
64
  "examples": [
65
+ {
66
+ "title": "Grouped options",
67
+ "code": "<Select\n label=\"Plan\"\n placeholder=\"Choose a plan\"\n groups={[\n { label: 'Individual', options: [{ value: 'free', label: 'Free' }] },\n { label: 'Business', options: [{ value: 'team', label: 'Team' }] },\n ]}\n/>",
68
+ "description": "Optgroups with a placeholder row."
69
+ },
70
+ {
71
+ "title": "Multi-select",
72
+ "code": "<Select\n label=\"Channels\"\n multiple\n value={channels}\n onChange={setChannels}\n options={[{ value: 'email', label: 'Email' }, { value: 'slack', label: 'Slack' }]}\n/>",
73
+ "description": "Native listbox with a string[] contract."
74
+ },
41
75
  {
42
76
  "title": "Controlled select",
43
77
  "code": "const [size, setSize] = useState('default');\n\n<Select\n label=\"Size\"\n value={size}\n onChange={setSize}\n options={[\n { value: 'default', label: 'Default' },\n { value: 'sm', label: 'Small' },\n { value: 'lg', label: 'Large' },\n ]}\n/>",
@@ -66,7 +100,10 @@
66
100
  "relationship": "Pair Select with Button in filter toolbars; Button submits, Select filters."
67
101
  }
68
102
  ],
69
- "relatedComponents": ["Button", "Input"],
103
+ "relatedComponents": [
104
+ "Button",
105
+ "Input"
106
+ ],
70
107
  "package": "@slipstream-ui/core",
71
108
  "importPath": "@slipstream-ui/core",
72
109
  "version": "0.0.1",
@@ -0,0 +1,95 @@
1
+ {
2
+ "id": "stepper",
3
+ "name": "Stepper",
4
+ "tier": "molecule",
5
+ "description": "Multi-step flow indicator: ordered list, aria-current step, text state for complete/current/upcoming.",
6
+ "designIntent": "Wizards need orientation: where am I, what is done, what is next. A stepper built on an ordered list keeps the sequence structural and announces state in text, not just color.",
7
+ "props": [
8
+ {
9
+ "name": "steps",
10
+ "type": "{ label: string; description?: string }[]",
11
+ "required": true,
12
+ "description": "Ordered step definitions."
13
+ },
14
+ {
15
+ "name": "current",
16
+ "type": "number",
17
+ "required": true,
18
+ "description": "Zero-based index of the active step."
19
+ },
20
+ {
21
+ "name": "orientation",
22
+ "type": "'horizontal' | 'vertical'",
23
+ "required": false,
24
+ "defaultValue": "horizontal",
25
+ "description": "Layout direction.",
26
+ "enum": [
27
+ "horizontal",
28
+ "vertical"
29
+ ]
30
+ },
31
+ {
32
+ "name": "onStepClick",
33
+ "type": "(index: number) => void",
34
+ "required": false,
35
+ "description": "Makes steps clickable (for revisit flows)."
36
+ }
37
+ ],
38
+ "examples": [
39
+ {
40
+ "title": "Onboarding wizard",
41
+ "code": "<Stepper\n current={step}\n steps={[{ label: 'Account' }, { label: 'Studio details' }, { label: 'Invite team' }, { label: 'Review' }]}\n/>",
42
+ "description": "Current step is announced; completed steps show a check."
43
+ }
44
+ ],
45
+ "antiPatterns": [
46
+ {
47
+ "title": "Don't use it for progress percentage",
48
+ "explanation": "Use Progress for measurable 0-100 progress; Stepper is discrete stages.",
49
+ "example": "// Avoid: 7 steps for an upload\n// Prefer: <Progress value={42} />"
50
+ }
51
+ ],
52
+ "accessibility": [
53
+ {
54
+ "feature": "Sequence + state",
55
+ "implementation": "Ordered list with aria-current=step and sr-only state text",
56
+ "userRequirements": "Keep step labels short and parallel"
57
+ }
58
+ ],
59
+ "contentGuidelines": [
60
+ {
61
+ "contentType": "Step labels",
62
+ "guidelines": [
63
+ "Verb or noun phrase",
64
+ "Parallel grammar",
65
+ "3-5 steps max"
66
+ ],
67
+ "goodExamples": [
68
+ "Account",
69
+ "Studio details",
70
+ "Review"
71
+ ],
72
+ "badExamples": [
73
+ "Step 1",
74
+ "Now do this"
75
+ ]
76
+ }
77
+ ],
78
+ "compositionRules": [
79
+ {
80
+ "component": "Form",
81
+ "relationship": "One Form (or Form step) per Stepper stage",
82
+ "example": "<Stepper current={1} steps={steps} />"
83
+ }
84
+ ],
85
+ "relatedComponents": [
86
+ "Progress",
87
+ "Tabs",
88
+ "Form"
89
+ ],
90
+ "package": "@slipstream-ui/core",
91
+ "importPath": "@slipstream-ui/core",
92
+ "version": "0.1.0",
93
+ "sourceDir": "stepper",
94
+ "exportName": "Stepper"
95
+ }
@@ -0,0 +1,123 @@
1
+ {
2
+ "id": "table-card",
3
+ "name": "TableCard",
4
+ "tier": "molecule",
5
+ "package": "@slipstream-ui/core",
6
+ "importPath": "@slipstream-ui/core",
7
+ "version": "0.1.0",
8
+ "sourceDir": "table-card",
9
+ "exportName": "TableCard",
10
+ "description": "Table shell with title/description, header actions, optional toolbar, edge-to-edge body and a footer row.",
11
+ "designIntent": "Every list page rebuilds the same chrome around Table/DataTable: heading, count, actions, filter row, pagination. TableCard standardizes that shell so pages only supply the table.",
12
+ "props": [
13
+ {
14
+ "name": "title",
15
+ "type": "string",
16
+ "required": false,
17
+ "description": "Card heading."
18
+ },
19
+ {
20
+ "name": "description",
21
+ "type": "string",
22
+ "required": false,
23
+ "description": "Supporting line under the heading."
24
+ },
25
+ {
26
+ "name": "actions",
27
+ "type": "ReactNode",
28
+ "required": false,
29
+ "description": "Right-aligned header actions."
30
+ },
31
+ {
32
+ "name": "toolbar",
33
+ "type": "ReactNode",
34
+ "required": false,
35
+ "description": "Row between header and table (filters, bulk actions)."
36
+ },
37
+ {
38
+ "name": "children",
39
+ "type": "ReactNode",
40
+ "required": true,
41
+ "description": "Table or DataTable content."
42
+ },
43
+ {
44
+ "name": "footer",
45
+ "type": "ReactNode",
46
+ "required": false,
47
+ "description": "Footer row (pagination, summary)."
48
+ },
49
+ {
50
+ "name": "flush",
51
+ "type": "boolean",
52
+ "required": false,
53
+ "defaultValue": "true",
54
+ "description": "Remove inner padding so the table reaches the card edges."
55
+ }
56
+ ],
57
+ "examples": [
58
+ {
59
+ "title": "List page table",
60
+ "code": "<TableCard\n title=\"Classes\"\n description=\"12 active\"\n actions={<Button>New class</Button>}\n toolbar={<FilterBar … />}\n footer={<Pagination … />}\n>\n <DataTable columns={columns} data={rows} />\n</TableCard>",
61
+ "description": "Header, toolbar, table and pagination in one surface."
62
+ }
63
+ ],
64
+ "antiPatterns": [
65
+ {
66
+ "title": "Don't nest another bordered card",
67
+ "explanation": "TableCard is the surface; wrapping it in Card double-borders the content.",
68
+ "example": "// Avoid\n<Card><TableCard>…</TableCard></Card>"
69
+ }
70
+ ],
71
+ "accessibility": [
72
+ {
73
+ "feature": "Heading semantics",
74
+ "implementation": "Title renders an h2 (page owns the h1 via PageHeader)",
75
+ "userRequirements": "Don't skip heading levels"
76
+ },
77
+ {
78
+ "feature": "Table labelling",
79
+ "implementation": "The table keeps its own caption/aria-label",
80
+ "userRequirements": "Always label the table inside"
81
+ }
82
+ ],
83
+ "contentGuidelines": [
84
+ {
85
+ "contentType": "Titles",
86
+ "guidelines": [
87
+ "Plural noun",
88
+ "Sentence case",
89
+ "Pair with a count in description"
90
+ ],
91
+ "goodExamples": [
92
+ "Classes · 12 active"
93
+ ],
94
+ "badExamples": [
95
+ "Data"
96
+ ]
97
+ }
98
+ ],
99
+ "compositionRules": [
100
+ {
101
+ "component": "DataTable",
102
+ "relationship": "Body content; use flush for edge-to-edge rows",
103
+ "example": "<TableCard flush><DataTable … /></TableCard>"
104
+ },
105
+ {
106
+ "component": "FilterBar",
107
+ "relationship": "Pass as toolbar for list filtering",
108
+ "example": "<TableCard toolbar={<FilterBar … />}>…</TableCard>"
109
+ },
110
+ {
111
+ "component": "Pagination",
112
+ "relationship": "Pass as footer",
113
+ "example": "<TableCard footer={<Pagination … />}>…</TableCard>"
114
+ }
115
+ ],
116
+ "relatedComponents": [
117
+ "DataTable",
118
+ "Table",
119
+ "FilterBar",
120
+ "Pagination",
121
+ "Card"
122
+ ]
123
+ }
@@ -0,0 +1,86 @@
1
+ {
2
+ "id": "time-picker",
3
+ "name": "TimePicker",
4
+ "tier": "molecule",
5
+ "description": "Native time field with an HH:MM contract and an optional clear button.",
6
+ "designIntent": "Like DatePicker: the platform time control is already accessible and locale-aware. TimePicker standardizes the HH:MM contract and clear affordance.",
7
+ "props": [
8
+ {
9
+ "name": "value",
10
+ "type": "string",
11
+ "required": false,
12
+ "description": "\"HH:MM\" (24h), controlled."
13
+ },
14
+ {
15
+ "name": "defaultValue",
16
+ "type": "string",
17
+ "required": false,
18
+ "description": "\"HH:MM\", initial value."
19
+ },
20
+ {
21
+ "name": "onValueChange",
22
+ "type": "(value: string) => void",
23
+ "required": false,
24
+ "description": "Fired with the time 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": "Time field",
37
+ "code": "<TimePicker aria-label=\"Starts at\" value={start} onValueChange={setStart} clearable />",
38
+ "description": "Controlled HH:MM value."
39
+ }
40
+ ],
41
+ "antiPatterns": [
42
+ {
43
+ "title": "Don't mix 12h display with 24h state",
44
+ "explanation": "State stays HH:MM; format for display with Intl.",
45
+ "example": "// Avoid: '9:30 PM' in state\n// Prefer: '21:30'"
46
+ }
47
+ ],
48
+ "accessibility": [
49
+ {
50
+ "feature": "Platform input",
51
+ "implementation": "Renders input[type=time]",
52
+ "userRequirements": "Provide a label describing the moment"
53
+ }
54
+ ],
55
+ "contentGuidelines": [
56
+ {
57
+ "contentType": "Labels",
58
+ "guidelines": [
59
+ "Describe the moment (Starts at / Ends by)"
60
+ ],
61
+ "goodExamples": [
62
+ "Starts at"
63
+ ],
64
+ "badExamples": [
65
+ "Time"
66
+ ]
67
+ }
68
+ ],
69
+ "compositionRules": [
70
+ {
71
+ "component": "DatePicker",
72
+ "relationship": "Pair for date+time scheduling",
73
+ "example": "<DatePicker aria-label=\"Date\" /><TimePicker aria-label=\"Time\" />"
74
+ }
75
+ ],
76
+ "relatedComponents": [
77
+ "DatePicker",
78
+ "FormField",
79
+ "Input"
80
+ ],
81
+ "package": "@slipstream-ui/core",
82
+ "importPath": "@slipstream-ui/core",
83
+ "version": "0.1.0",
84
+ "sourceDir": "time-picker",
85
+ "exportName": "TimePicker"
86
+ }