@kushagradhawan/kookie-ui 0.1.41 → 0.1.43
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/README.md +257 -60
- package/components.css +398 -91
- package/dist/cjs/components/schemas/base-button.schema.d.ts +319 -0
- package/dist/cjs/components/schemas/base-button.schema.d.ts.map +1 -0
- package/dist/cjs/components/schemas/base-button.schema.js +2 -0
- package/dist/cjs/components/schemas/base-button.schema.js.map +7 -0
- package/dist/cjs/components/schemas/button.schema.d.ts +686 -0
- package/dist/cjs/components/schemas/button.schema.d.ts.map +1 -0
- package/dist/cjs/components/schemas/button.schema.js +2 -0
- package/dist/cjs/components/schemas/button.schema.js.map +7 -0
- package/dist/cjs/components/schemas/icon-button.schema.d.ts +329 -0
- package/dist/cjs/components/schemas/icon-button.schema.d.ts.map +1 -0
- package/dist/cjs/components/schemas/icon-button.schema.js +2 -0
- package/dist/cjs/components/schemas/icon-button.schema.js.map +7 -0
- package/dist/cjs/components/schemas/index.d.ts +52 -0
- package/dist/cjs/components/schemas/index.d.ts.map +1 -0
- package/dist/cjs/components/schemas/index.js +2 -0
- package/dist/cjs/components/schemas/index.js.map +7 -0
- package/dist/cjs/components/schemas/toggle-button.schema.d.ts +1172 -0
- package/dist/cjs/components/schemas/toggle-button.schema.d.ts.map +1 -0
- package/dist/cjs/components/schemas/toggle-button.schema.js +2 -0
- package/dist/cjs/components/schemas/toggle-button.schema.js.map +7 -0
- package/dist/cjs/components/schemas/toggle-icon-button.schema.d.ts +563 -0
- package/dist/cjs/components/schemas/toggle-icon-button.schema.d.ts.map +1 -0
- package/dist/cjs/components/schemas/toggle-icon-button.schema.js +2 -0
- package/dist/cjs/components/schemas/toggle-icon-button.schema.js.map +7 -0
- package/dist/cjs/components/sheet.d.ts +1 -1
- package/dist/cjs/components/sheet.d.ts.map +1 -1
- package/dist/cjs/components/sheet.js +1 -1
- package/dist/cjs/components/sheet.js.map +3 -3
- package/dist/cjs/components/shell.d.ts +125 -164
- package/dist/cjs/components/shell.d.ts.map +1 -1
- package/dist/cjs/components/shell.js +1 -1
- package/dist/cjs/components/shell.js.map +3 -3
- package/dist/cjs/components/sidebar.d.ts +1 -7
- package/dist/cjs/components/sidebar.d.ts.map +1 -1
- package/dist/cjs/components/sidebar.js +1 -1
- package/dist/cjs/components/sidebar.js.map +3 -3
- package/dist/cjs/components/theme.d.ts +3 -0
- package/dist/cjs/components/theme.d.ts.map +1 -1
- package/dist/cjs/components/theme.js +1 -1
- package/dist/cjs/components/theme.js.map +3 -3
- package/dist/cjs/components/theme.props.d.ts +10 -0
- package/dist/cjs/components/theme.props.d.ts.map +1 -1
- package/dist/cjs/components/theme.props.js +1 -1
- package/dist/cjs/components/theme.props.js.map +3 -3
- package/dist/cjs/helpers/font-config.d.ts +96 -0
- package/dist/cjs/helpers/font-config.d.ts.map +1 -0
- package/dist/cjs/helpers/font-config.js +3 -0
- package/dist/cjs/helpers/font-config.js.map +7 -0
- package/dist/cjs/helpers/index.d.ts +1 -0
- package/dist/cjs/helpers/index.d.ts.map +1 -1
- package/dist/cjs/helpers/index.js +1 -1
- package/dist/cjs/helpers/index.js.map +2 -2
- package/dist/esm/components/schemas/base-button.schema.d.ts +319 -0
- package/dist/esm/components/schemas/base-button.schema.d.ts.map +1 -0
- package/dist/esm/components/schemas/base-button.schema.js +2 -0
- package/dist/esm/components/schemas/base-button.schema.js.map +7 -0
- package/dist/esm/components/schemas/button.schema.d.ts +686 -0
- package/dist/esm/components/schemas/button.schema.d.ts.map +1 -0
- package/dist/esm/components/schemas/button.schema.js +2 -0
- package/dist/esm/components/schemas/button.schema.js.map +7 -0
- package/dist/esm/components/schemas/icon-button.schema.d.ts +329 -0
- package/dist/esm/components/schemas/icon-button.schema.d.ts.map +1 -0
- package/dist/esm/components/schemas/icon-button.schema.js +2 -0
- package/dist/esm/components/schemas/icon-button.schema.js.map +7 -0
- package/dist/esm/components/schemas/index.d.ts +52 -0
- package/dist/esm/components/schemas/index.d.ts.map +1 -0
- package/dist/esm/components/schemas/index.js +2 -0
- package/dist/esm/components/schemas/index.js.map +7 -0
- package/dist/esm/components/schemas/toggle-button.schema.d.ts +1172 -0
- package/dist/esm/components/schemas/toggle-button.schema.d.ts.map +1 -0
- package/dist/esm/components/schemas/toggle-button.schema.js +2 -0
- package/dist/esm/components/schemas/toggle-button.schema.js.map +7 -0
- package/dist/esm/components/schemas/toggle-icon-button.schema.d.ts +563 -0
- package/dist/esm/components/schemas/toggle-icon-button.schema.d.ts.map +1 -0
- package/dist/esm/components/schemas/toggle-icon-button.schema.js +2 -0
- package/dist/esm/components/schemas/toggle-icon-button.schema.js.map +7 -0
- package/dist/esm/components/sheet.d.ts +1 -1
- package/dist/esm/components/sheet.d.ts.map +1 -1
- package/dist/esm/components/sheet.js +1 -1
- package/dist/esm/components/sheet.js.map +3 -3
- package/dist/esm/components/shell.d.ts +125 -164
- package/dist/esm/components/shell.d.ts.map +1 -1
- package/dist/esm/components/shell.js +1 -1
- package/dist/esm/components/shell.js.map +3 -3
- package/dist/esm/components/sidebar.d.ts +1 -7
- package/dist/esm/components/sidebar.d.ts.map +1 -1
- package/dist/esm/components/sidebar.js +1 -1
- package/dist/esm/components/sidebar.js.map +3 -3
- package/dist/esm/components/theme.d.ts +3 -0
- package/dist/esm/components/theme.d.ts.map +1 -1
- package/dist/esm/components/theme.js +1 -1
- package/dist/esm/components/theme.js.map +3 -3
- package/dist/esm/components/theme.props.d.ts +10 -0
- package/dist/esm/components/theme.props.d.ts.map +1 -1
- package/dist/esm/components/theme.props.js +1 -1
- package/dist/esm/components/theme.props.js.map +3 -3
- package/dist/esm/helpers/font-config.d.ts +96 -0
- package/dist/esm/helpers/font-config.d.ts.map +1 -0
- package/dist/esm/helpers/font-config.js +3 -0
- package/dist/esm/helpers/font-config.js.map +7 -0
- package/dist/esm/helpers/index.d.ts +1 -0
- package/dist/esm/helpers/index.d.ts.map +1 -1
- package/dist/esm/helpers/index.js +1 -1
- package/dist/esm/helpers/index.js.map +2 -2
- package/package.json +23 -3
- package/schemas/base-button.d.ts +2 -0
- package/schemas/base-button.json +284 -0
- package/schemas/button.d.ts +2 -0
- package/schemas/button.json +535 -0
- package/schemas/icon-button.d.ts +2 -0
- package/schemas/icon-button.json +318 -0
- package/schemas/index.d.ts +2 -0
- package/schemas/index.json +2016 -0
- package/schemas/schemas.d.ts +29 -0
- package/schemas/toggle-button.d.ts +2 -0
- package/schemas/toggle-button.json +543 -0
- package/schemas/toggle-icon-button.d.ts +2 -0
- package/schemas/toggle-icon-button.json +326 -0
- package/schemas-json.d.ts +12 -0
- package/src/components/_internal/base-sidebar-menu.css +3 -8
- package/src/components/_internal/base-sidebar.css +1 -2
- package/src/components/schemas/base-button.schema.ts +339 -0
- package/src/components/schemas/button.schema.ts +198 -0
- package/src/components/schemas/icon-button.schema.ts +142 -0
- package/src/components/schemas/index.ts +68 -0
- package/src/components/schemas/toggle-button.schema.ts +122 -0
- package/src/components/schemas/toggle-icon-button.schema.ts +195 -0
- package/src/components/sheet.css +39 -19
- package/src/components/sheet.tsx +62 -3
- package/src/components/shell.css +510 -89
- package/src/components/shell.tsx +2055 -928
- package/src/components/sidebar.css +126 -65
- package/src/components/sidebar.tsx +5 -24
- package/src/components/theme.props.tsx +8 -0
- package/src/components/theme.tsx +16 -0
- package/src/helpers/font-config.ts +167 -0
- package/src/helpers/index.ts +1 -0
- package/src/styles/fonts.css +16 -13
- package/src/styles/tokens/typography.css +27 -4
- package/styles.css +410 -91
- package/tokens/base.css +12 -0
- package/tokens.css +12 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* BaseButton Zod schema - Single source of truth for all button component props
|
|
5
|
+
*
|
|
6
|
+
* This schema defines the core button functionality used by Button, IconButton,
|
|
7
|
+
* ToggleButton, and ToggleIconButton components. It ensures consistency across
|
|
8
|
+
* the design system and provides machine-readable validation.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* // Basic validation
|
|
13
|
+
* const props = BaseButtonSchema.parse({ size: '2', variant: 'solid' });
|
|
14
|
+
*
|
|
15
|
+
* // With responsive props
|
|
16
|
+
* const responsiveProps = BaseButtonSchema.parse({
|
|
17
|
+
* size: { initial: '1', sm: '2', md: '3' },
|
|
18
|
+
* variant: 'solid'
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export const BaseButtonSchema = z
|
|
23
|
+
.object({
|
|
24
|
+
/**
|
|
25
|
+
* Polymorphic rendering - render as different HTML elements
|
|
26
|
+
* @default 'button'
|
|
27
|
+
*/
|
|
28
|
+
as: z.string().optional().describe('HTML element type to render as'),
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Use asChild to merge props with child element
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
asChild: z
|
|
35
|
+
.boolean()
|
|
36
|
+
.optional()
|
|
37
|
+
.describe('Merge props with child element instead of rendering wrapper'),
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Button size for responsive design and interface density
|
|
41
|
+
* Supports responsive objects: { initial: '1', sm: '2', md: '3', lg: '4' }
|
|
42
|
+
* @default '2'
|
|
43
|
+
*/
|
|
44
|
+
size: z
|
|
45
|
+
.union([
|
|
46
|
+
z.enum(['1', '2', '3', '4']),
|
|
47
|
+
z.object({
|
|
48
|
+
initial: z.enum(['1', '2', '3', '4']).optional(),
|
|
49
|
+
sm: z.enum(['1', '2', '3', '4']).optional(),
|
|
50
|
+
md: z.enum(['1', '2', '3', '4']).optional(),
|
|
51
|
+
lg: z.enum(['1', '2', '3', '4']).optional(),
|
|
52
|
+
xl: z.enum(['1', '2', '3', '4']).optional(),
|
|
53
|
+
}),
|
|
54
|
+
])
|
|
55
|
+
.default('2')
|
|
56
|
+
.describe('Button size for responsive design and interface density'),
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Visual variant that determines the button's appearance and context
|
|
60
|
+
* @default 'solid'
|
|
61
|
+
*/
|
|
62
|
+
variant: z
|
|
63
|
+
.enum(['classic', 'solid', 'soft', 'surface', 'outline', 'ghost', 'override'])
|
|
64
|
+
.default('solid')
|
|
65
|
+
.describe("Visual variant that determines the button's appearance and context"),
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Accent color for the button
|
|
69
|
+
*/
|
|
70
|
+
color: z
|
|
71
|
+
.enum([
|
|
72
|
+
'tomato',
|
|
73
|
+
'red',
|
|
74
|
+
'ruby',
|
|
75
|
+
'crimson',
|
|
76
|
+
'pink',
|
|
77
|
+
'plum',
|
|
78
|
+
'purple',
|
|
79
|
+
'violet',
|
|
80
|
+
'iris',
|
|
81
|
+
'indigo',
|
|
82
|
+
'blue',
|
|
83
|
+
'cyan',
|
|
84
|
+
'teal',
|
|
85
|
+
'jade',
|
|
86
|
+
'green',
|
|
87
|
+
'grass',
|
|
88
|
+
'brown',
|
|
89
|
+
'orange',
|
|
90
|
+
'sky',
|
|
91
|
+
'mint',
|
|
92
|
+
'lime',
|
|
93
|
+
'yellow',
|
|
94
|
+
'amber',
|
|
95
|
+
'gold',
|
|
96
|
+
'bronze',
|
|
97
|
+
'gray',
|
|
98
|
+
])
|
|
99
|
+
.optional()
|
|
100
|
+
.describe('Accent color for the button'),
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* High contrast mode for better accessibility
|
|
104
|
+
* @default false
|
|
105
|
+
*/
|
|
106
|
+
highContrast: z
|
|
107
|
+
.boolean()
|
|
108
|
+
.optional()
|
|
109
|
+
.default(false)
|
|
110
|
+
.describe('High contrast mode for better accessibility'),
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Border radius for the button
|
|
114
|
+
*/
|
|
115
|
+
radius: z
|
|
116
|
+
.enum(['none', 'small', 'medium', 'large', 'full'])
|
|
117
|
+
.optional()
|
|
118
|
+
.describe('Border radius for the button'),
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Loading state that shows a spinner and disables interaction
|
|
122
|
+
* Automatically sets disabled=true and provides accessibility announcements
|
|
123
|
+
* @default false
|
|
124
|
+
*/
|
|
125
|
+
loading: z
|
|
126
|
+
.boolean()
|
|
127
|
+
.optional()
|
|
128
|
+
.default(false)
|
|
129
|
+
.describe('Loading state that shows a spinner and disables interaction'),
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Full width mode that expands the button to fill its container
|
|
133
|
+
* Useful for mobile layouts and form submissions
|
|
134
|
+
* @default false
|
|
135
|
+
*/
|
|
136
|
+
fullWidth: z
|
|
137
|
+
.boolean()
|
|
138
|
+
.optional()
|
|
139
|
+
.default(false)
|
|
140
|
+
.describe('Full width mode that expands the button to fill its container'),
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Material type for visual rendering and depth effects
|
|
144
|
+
* Controls how the button renders its visual elements
|
|
145
|
+
*/
|
|
146
|
+
material: z
|
|
147
|
+
.enum(['solid', 'translucent'])
|
|
148
|
+
.optional()
|
|
149
|
+
.describe('Material type for visual rendering and depth effects'),
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Panel background type (deprecated)
|
|
153
|
+
* @deprecated Use `material` prop instead. This prop will be removed in a future version.
|
|
154
|
+
*/
|
|
155
|
+
panelBackground: z
|
|
156
|
+
.enum(['solid', 'translucent'])
|
|
157
|
+
.optional()
|
|
158
|
+
.describe('Panel background type (deprecated - use material instead)'),
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Flush mode that removes visual padding for seamless text integration
|
|
162
|
+
* Only effective with ghost variant
|
|
163
|
+
* @default false
|
|
164
|
+
*/
|
|
165
|
+
flush: z
|
|
166
|
+
.boolean()
|
|
167
|
+
.optional()
|
|
168
|
+
.default(false)
|
|
169
|
+
.describe('Flush mode that removes visual padding for seamless text integration'),
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Disabled state
|
|
173
|
+
* @default false
|
|
174
|
+
*/
|
|
175
|
+
disabled: z.boolean().optional().default(false).describe('Disabled state'),
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Type attribute for form buttons
|
|
179
|
+
* @default 'button'
|
|
180
|
+
*/
|
|
181
|
+
type: z
|
|
182
|
+
.enum(['button', 'submit', 'reset'])
|
|
183
|
+
.optional()
|
|
184
|
+
.describe('Type attribute for form buttons'),
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Additional CSS class name
|
|
188
|
+
*/
|
|
189
|
+
className: z.string().optional().describe('Additional CSS class name'),
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Inline styles
|
|
193
|
+
*/
|
|
194
|
+
style: z
|
|
195
|
+
.record(z.string(), z.union([z.string(), z.number()]))
|
|
196
|
+
.optional()
|
|
197
|
+
.describe('Inline styles'),
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Click handler
|
|
201
|
+
*/
|
|
202
|
+
onClick: z.function().optional().describe('Click handler'),
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Focus handler
|
|
206
|
+
*/
|
|
207
|
+
onFocus: z.function().optional().describe('Focus handler'),
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Blur handler
|
|
211
|
+
*/
|
|
212
|
+
onBlur: z.function().optional().describe('Blur handler'),
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Mouse enter handler
|
|
216
|
+
*/
|
|
217
|
+
onMouseEnter: z.function().optional().describe('Mouse enter handler'),
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Mouse leave handler
|
|
221
|
+
*/
|
|
222
|
+
onMouseLeave: z.function().optional().describe('Mouse leave handler'),
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Key down handler
|
|
226
|
+
*/
|
|
227
|
+
onKeyDown: z.function().optional().describe('Key down handler'),
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Key up handler
|
|
231
|
+
*/
|
|
232
|
+
onKeyUp: z.function().optional().describe('Key up handler'),
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Form submission handler
|
|
236
|
+
*/
|
|
237
|
+
onSubmit: z.function().optional().describe('Form submission handler'),
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Tab index for keyboard navigation
|
|
241
|
+
*/
|
|
242
|
+
tabIndex: z.number().optional().describe('Tab index for keyboard navigation'),
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* ARIA label for accessibility
|
|
246
|
+
*/
|
|
247
|
+
'aria-label': z.string().optional().describe('ARIA label for accessibility'),
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* ARIA labelled by reference
|
|
251
|
+
*/
|
|
252
|
+
'aria-labelledby': z.string().optional().describe('ARIA labelled by reference'),
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* ARIA described by reference
|
|
256
|
+
*/
|
|
257
|
+
'aria-describedby': z.string().optional().describe('ARIA described by reference'),
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* ARIA expanded state
|
|
261
|
+
*/
|
|
262
|
+
'aria-expanded': z.boolean().optional().describe('ARIA expanded state'),
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* ARIA pressed state
|
|
266
|
+
*/
|
|
267
|
+
'aria-pressed': z.boolean().optional().describe('ARIA pressed state'),
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* ARIA current state
|
|
271
|
+
*/
|
|
272
|
+
'aria-current': z
|
|
273
|
+
.union([z.boolean(), z.enum(['page', 'step', 'location', 'date', 'time'])])
|
|
274
|
+
.optional()
|
|
275
|
+
.describe('ARIA current state'),
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* ARIA controls reference
|
|
279
|
+
*/
|
|
280
|
+
'aria-controls': z.string().optional().describe('ARIA controls reference'),
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* ARIA owns reference
|
|
284
|
+
*/
|
|
285
|
+
'aria-owns': z.string().optional().describe('ARIA owns reference'),
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Data attributes
|
|
289
|
+
*/
|
|
290
|
+
'data-*': z.record(z.string(), z.string()).optional().describe('Data attributes'),
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* ID attribute
|
|
294
|
+
*/
|
|
295
|
+
id: z.string().optional().describe('ID attribute'),
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Title attribute for tooltip
|
|
299
|
+
*/
|
|
300
|
+
title: z.string().optional().describe('Title attribute for tooltip'),
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Role attribute
|
|
304
|
+
*/
|
|
305
|
+
role: z.string().optional().describe('Role attribute'),
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Children elements
|
|
309
|
+
*/
|
|
310
|
+
children: z.any().optional().describe('Children elements'),
|
|
311
|
+
})
|
|
312
|
+
.strict();
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Type derived from BaseButton Zod schema
|
|
316
|
+
* This ensures type safety and consistency with the schema
|
|
317
|
+
*/
|
|
318
|
+
export type BaseButtonProps = z.infer<typeof BaseButtonSchema>;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Development-only helper to validate and normalize props
|
|
322
|
+
* This function should only be used in development mode
|
|
323
|
+
*
|
|
324
|
+
* @param props - Props to validate and normalize
|
|
325
|
+
* @returns Validated and normalized props
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```tsx
|
|
329
|
+
* // In development, this will validate props and show helpful errors
|
|
330
|
+
* const validatedProps = parseBaseButtonProps({ size: 'invalid', variant: 'solid' });
|
|
331
|
+
* // Throws: "Invalid enum value. Expected '1' | '2' | '3' | '4', received 'invalid'"
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
export function parseBaseButtonProps(props: unknown): BaseButtonProps {
|
|
335
|
+
if (process.env.NODE_ENV === 'development') {
|
|
336
|
+
return BaseButtonSchema.parse(props);
|
|
337
|
+
}
|
|
338
|
+
return props as BaseButtonProps;
|
|
339
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseButtonSchema } from './base-button.schema.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Button Zod schema - Single source of truth for Button component props
|
|
6
|
+
*
|
|
7
|
+
* The Button component is the primary interactive element in the Kookie User Interface.
|
|
8
|
+
* It provides six visual variants, four sizes, comprehensive color options, and built-in
|
|
9
|
+
* tooltip support. The component automatically handles icon sizing, supports responsive
|
|
10
|
+
* layouts, and provides accessibility compliance out of the box.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* // Basic button validation
|
|
15
|
+
* const props = ButtonSchema.parse({ variant: 'solid', size: '2' });
|
|
16
|
+
*
|
|
17
|
+
* // Button with tooltip
|
|
18
|
+
* const tooltipProps = ButtonSchema.parse({
|
|
19
|
+
* tooltip: 'Save your progress',
|
|
20
|
+
* tooltipSide: 'top'
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Polymorphic button as link
|
|
24
|
+
* const linkProps = ButtonSchema.parse({
|
|
25
|
+
* as: 'a',
|
|
26
|
+
* href: '/dashboard',
|
|
27
|
+
* children: 'Go to Dashboard'
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export const ButtonSchema = BaseButtonSchema.extend({
|
|
32
|
+
/**
|
|
33
|
+
* Content to display in the tooltip on hover/focus
|
|
34
|
+
*/
|
|
35
|
+
tooltip: z.string().optional().describe('Content to display in the tooltip on hover/focus'),
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Side of the button where the tooltip should appear
|
|
39
|
+
* @default 'top'
|
|
40
|
+
*/
|
|
41
|
+
tooltipSide: z
|
|
42
|
+
.enum(['top', 'right', 'bottom', 'left'])
|
|
43
|
+
.optional()
|
|
44
|
+
.default('top')
|
|
45
|
+
.describe('Side of the button where the tooltip should appear'),
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Alignment of the tooltip relative to the button
|
|
49
|
+
* @default 'center'
|
|
50
|
+
*/
|
|
51
|
+
tooltipAlign: z
|
|
52
|
+
.enum(['start', 'center', 'end'])
|
|
53
|
+
.optional()
|
|
54
|
+
.default('center')
|
|
55
|
+
.describe('Alignment of the tooltip relative to the button'),
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Delay before showing the tooltip (in milliseconds)
|
|
59
|
+
*/
|
|
60
|
+
tooltipDelayDuration: z
|
|
61
|
+
.number()
|
|
62
|
+
.optional()
|
|
63
|
+
.describe('Delay before showing the tooltip (in milliseconds)'),
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Whether to disable hoverable content behavior
|
|
67
|
+
* @default false
|
|
68
|
+
*/
|
|
69
|
+
tooltipDisableHoverableContent: z
|
|
70
|
+
.boolean()
|
|
71
|
+
.optional()
|
|
72
|
+
.default(false)
|
|
73
|
+
.describe('Whether to disable hoverable content behavior'),
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Override styles for different interaction states
|
|
77
|
+
*/
|
|
78
|
+
overrideStyles: z
|
|
79
|
+
.object({
|
|
80
|
+
/** Default/idle state styles */
|
|
81
|
+
normal: z
|
|
82
|
+
.object({
|
|
83
|
+
color: z.string().optional(),
|
|
84
|
+
background: z.string().optional(),
|
|
85
|
+
backgroundColor: z.string().optional(),
|
|
86
|
+
boxShadow: z.string().optional(),
|
|
87
|
+
filter: z.string().optional(),
|
|
88
|
+
outline: z.string().optional(),
|
|
89
|
+
outlineOffset: z.string().optional(),
|
|
90
|
+
opacity: z.union([z.string(), z.number()]).optional(),
|
|
91
|
+
})
|
|
92
|
+
.optional(),
|
|
93
|
+
/** Hover state styles */
|
|
94
|
+
hover: z
|
|
95
|
+
.object({
|
|
96
|
+
color: z.string().optional(),
|
|
97
|
+
background: z.string().optional(),
|
|
98
|
+
backgroundColor: z.string().optional(),
|
|
99
|
+
boxShadow: z.string().optional(),
|
|
100
|
+
filter: z.string().optional(),
|
|
101
|
+
outline: z.string().optional(),
|
|
102
|
+
outlineOffset: z.string().optional(),
|
|
103
|
+
opacity: z.union([z.string(), z.number()]).optional(),
|
|
104
|
+
})
|
|
105
|
+
.optional(),
|
|
106
|
+
/** Active (mouse down) state styles */
|
|
107
|
+
active: z
|
|
108
|
+
.object({
|
|
109
|
+
color: z.string().optional(),
|
|
110
|
+
background: z.string().optional(),
|
|
111
|
+
backgroundColor: z.string().optional(),
|
|
112
|
+
boxShadow: z.string().optional(),
|
|
113
|
+
filter: z.string().optional(),
|
|
114
|
+
outline: z.string().optional(),
|
|
115
|
+
outlineOffset: z.string().optional(),
|
|
116
|
+
opacity: z.union([z.string(), z.number()]).optional(),
|
|
117
|
+
})
|
|
118
|
+
.optional(),
|
|
119
|
+
/** Toggle pressed state styles (data-state="on") */
|
|
120
|
+
pressed: z
|
|
121
|
+
.object({
|
|
122
|
+
color: z.string().optional(),
|
|
123
|
+
background: z.string().optional(),
|
|
124
|
+
backgroundColor: z.string().optional(),
|
|
125
|
+
boxShadow: z.string().optional(),
|
|
126
|
+
filter: z.string().optional(),
|
|
127
|
+
outline: z.string().optional(),
|
|
128
|
+
outlineOffset: z.string().optional(),
|
|
129
|
+
opacity: z.union([z.string(), z.number()]).optional(),
|
|
130
|
+
})
|
|
131
|
+
.optional(),
|
|
132
|
+
/** Open state styles (e.g., when used as a trigger) */
|
|
133
|
+
open: z
|
|
134
|
+
.object({
|
|
135
|
+
color: z.string().optional(),
|
|
136
|
+
background: z.string().optional(),
|
|
137
|
+
backgroundColor: z.string().optional(),
|
|
138
|
+
boxShadow: z.string().optional(),
|
|
139
|
+
filter: z.string().optional(),
|
|
140
|
+
outline: z.string().optional(),
|
|
141
|
+
outlineOffset: z.string().optional(),
|
|
142
|
+
opacity: z.union([z.string(), z.number()]).optional(),
|
|
143
|
+
})
|
|
144
|
+
.optional(),
|
|
145
|
+
/** Disabled state styles */
|
|
146
|
+
disabled: z
|
|
147
|
+
.object({
|
|
148
|
+
color: z.string().optional(),
|
|
149
|
+
background: z.string().optional(),
|
|
150
|
+
backgroundColor: z.string().optional(),
|
|
151
|
+
boxShadow: z.string().optional(),
|
|
152
|
+
filter: z.string().optional(),
|
|
153
|
+
outline: z.string().optional(),
|
|
154
|
+
outlineOffset: z.string().optional(),
|
|
155
|
+
opacity: z.union([z.string(), z.number()]).optional(),
|
|
156
|
+
})
|
|
157
|
+
.optional(),
|
|
158
|
+
/** Focus-visible outline styles */
|
|
159
|
+
focus: z
|
|
160
|
+
.object({
|
|
161
|
+
outline: z.string().optional(),
|
|
162
|
+
outlineOffset: z.string().optional(),
|
|
163
|
+
})
|
|
164
|
+
.optional(),
|
|
165
|
+
})
|
|
166
|
+
.optional()
|
|
167
|
+
.describe('Override styles for different interaction states'),
|
|
168
|
+
}).strict();
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Type derived from Button Zod schema
|
|
172
|
+
* This ensures type safety and consistency with the schema
|
|
173
|
+
*/
|
|
174
|
+
export type ButtonProps = z.infer<typeof ButtonSchema>;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Development-only helper to validate and normalize Button props
|
|
178
|
+
* This function should only be used in development mode
|
|
179
|
+
*
|
|
180
|
+
* @param props - Props to validate and normalize
|
|
181
|
+
* @returns Validated and normalized props
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```tsx
|
|
185
|
+
* // In development, this will validate props and show helpful errors
|
|
186
|
+
* const validatedProps = parseButtonProps({
|
|
187
|
+
* variant: 'invalid',
|
|
188
|
+
* tooltipSide: 'invalid'
|
|
189
|
+
* });
|
|
190
|
+
* // Throws validation errors for invalid enum values
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
export function parseButtonProps(props: unknown): ButtonProps {
|
|
194
|
+
if (process.env.NODE_ENV === 'development') {
|
|
195
|
+
return ButtonSchema.parse(props);
|
|
196
|
+
}
|
|
197
|
+
return props as ButtonProps;
|
|
198
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseButtonSchema } from './base-button.schema.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* IconButton Zod schema - Single source of truth for IconButton component props
|
|
6
|
+
*
|
|
7
|
+
* IconButton is a specialized button component designed for icon-only interactions.
|
|
8
|
+
* It requires proper accessibility attributes to meet WCAG guidelines and provides
|
|
9
|
+
* comprehensive tooltip support for better user experience.
|
|
10
|
+
*
|
|
11
|
+
* Key features:
|
|
12
|
+
* - Required accessibility attributes (aria-label, aria-labelledby, or children)
|
|
13
|
+
* - Tooltip support for better UX
|
|
14
|
+
* - Inherits all BaseButton functionality
|
|
15
|
+
* - Optimized for icon-only interactions
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* // Basic icon button with aria-label
|
|
20
|
+
* const props = IconButtonSchema.parse({
|
|
21
|
+
* 'aria-label': 'Close dialog',
|
|
22
|
+
* variant: 'ghost'
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // Icon button with tooltip
|
|
26
|
+
* const tooltipProps = IconButtonSchema.parse({
|
|
27
|
+
* 'aria-label': 'Settings',
|
|
28
|
+
* tooltip: 'Open settings menu',
|
|
29
|
+
* tooltipSide: 'right'
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // Icon button with children (fallback accessibility)
|
|
33
|
+
* const childrenProps = IconButtonSchema.parse({
|
|
34
|
+
* children: <SettingsIcon />,
|
|
35
|
+
* variant: 'soft'
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export const IconButtonSchema = BaseButtonSchema.extend({
|
|
40
|
+
/**
|
|
41
|
+
* Content to display in the tooltip on hover/focus
|
|
42
|
+
*/
|
|
43
|
+
tooltip: z.string().optional().describe('Content to display in the tooltip on hover/focus'),
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Side of the button where the tooltip should appear
|
|
47
|
+
* @default 'top'
|
|
48
|
+
*/
|
|
49
|
+
tooltipSide: z
|
|
50
|
+
.enum(['top', 'right', 'bottom', 'left'])
|
|
51
|
+
.optional()
|
|
52
|
+
.default('top')
|
|
53
|
+
.describe('Side of the button where the tooltip should appear'),
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Alignment of the tooltip relative to the button
|
|
57
|
+
* @default 'center'
|
|
58
|
+
*/
|
|
59
|
+
tooltipAlign: z
|
|
60
|
+
.enum(['start', 'center', 'end'])
|
|
61
|
+
.optional()
|
|
62
|
+
.default('center')
|
|
63
|
+
.describe('Alignment of the tooltip relative to the button'),
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Delay before showing the tooltip (in milliseconds)
|
|
67
|
+
*/
|
|
68
|
+
tooltipDelayDuration: z
|
|
69
|
+
.number()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe('Delay before showing the tooltip (in milliseconds)'),
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Whether to disable hoverable content behavior
|
|
75
|
+
* @default false
|
|
76
|
+
*/
|
|
77
|
+
tooltipDisableHoverableContent: z
|
|
78
|
+
.boolean()
|
|
79
|
+
.optional()
|
|
80
|
+
.default(false)
|
|
81
|
+
.describe('Whether to disable hoverable content behavior'),
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* ARIA label for accessibility (required if no aria-labelledby or children)
|
|
85
|
+
* Icon buttons must have an accessible name to meet WCAG guidelines
|
|
86
|
+
*/
|
|
87
|
+
'aria-label': z.string().optional().describe('ARIA label for accessibility'),
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* ARIA labelled by reference (required if no aria-label or children)
|
|
91
|
+
* Icon buttons must have an accessible name to meet WCAG guidelines
|
|
92
|
+
*/
|
|
93
|
+
'aria-labelledby': z.string().optional().describe('ARIA labelled by reference'),
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Children elements (required if no aria-label or aria-labelledby)
|
|
97
|
+
* Icon buttons must have an accessible name to meet WCAG guidelines
|
|
98
|
+
*/
|
|
99
|
+
children: z
|
|
100
|
+
.any()
|
|
101
|
+
.optional()
|
|
102
|
+
.describe('Children elements (required for accessibility if no aria-label)'),
|
|
103
|
+
}).refine((data) => data['aria-label'] || data['aria-labelledby'] || data.children, {
|
|
104
|
+
message:
|
|
105
|
+
"IconButton must have either 'aria-label', 'aria-labelledby', or 'children' for accessibility",
|
|
106
|
+
path: ['aria-label', 'aria-labelledby', 'children'],
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Type derived from IconButton Zod schema
|
|
111
|
+
* This ensures type safety and consistency with the schema
|
|
112
|
+
*/
|
|
113
|
+
export type IconButtonProps = z.infer<typeof IconButtonSchema>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Development-only helper to validate and normalize IconButton props
|
|
117
|
+
* This function should only be used in development mode
|
|
118
|
+
*
|
|
119
|
+
* @param props - Props to validate and normalize
|
|
120
|
+
* @returns Validated and normalized props
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```tsx
|
|
124
|
+
* // In development, this will validate props and show helpful errors
|
|
125
|
+
* const validatedProps = parseIconButtonProps({
|
|
126
|
+
* variant: 'ghost'
|
|
127
|
+
* // Missing accessibility - will throw error
|
|
128
|
+
* });
|
|
129
|
+
* // Throws: "IconButton must have either 'aria-label', 'aria-labelledby', or 'children' for accessibility"
|
|
130
|
+
*
|
|
131
|
+
* const validProps = parseIconButtonProps({
|
|
132
|
+
* 'aria-label': 'Close',
|
|
133
|
+
* variant: 'ghost'
|
|
134
|
+
* });
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
export function parseIconButtonProps(props: unknown): IconButtonProps {
|
|
138
|
+
if (process.env.NODE_ENV === 'development') {
|
|
139
|
+
return IconButtonSchema.parse(props);
|
|
140
|
+
}
|
|
141
|
+
return props as IconButtonProps;
|
|
142
|
+
}
|