@stacksjs/components 0.2.92 → 0.2.94
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/{Sidebar-yb115a2w.stx → Sidebar-4hn7vw4n.stx} +18 -0
- package/dist/index.js +5 -5
- package/package.json +5 -6
- package/src/components/CodeBlock.stx +0 -54
- package/src/components/Footer.stx +0 -66
- package/src/components/Hero.stx +0 -25
- package/src/components/Installation.stx +0 -37
- package/src/components/index.ts +0 -4
- package/src/composables/index.ts +0 -4
- package/src/composables/useCopyCode.ts +0 -73
- package/src/composables/useDarkMode.ts +0 -100
- package/src/composables/useSEO.ts +0 -230
- package/src/composables/utilities/index.ts +0 -7
- package/src/composables/utilities/useClickOutside.ts +0 -72
- package/src/composables/utilities/useFocusTrap.ts +0 -159
- package/src/composables/utilities/useKeyboard.ts +0 -124
- package/src/composables/utilities/useLocalStorage.ts +0 -132
- package/src/composables/utilities/useMediaQuery.ts +0 -118
- package/src/index.ts +0 -99
- package/src/stx.d.ts +0 -4
- package/src/ui/accordion/Accordion.stx +0 -146
- package/src/ui/accordion/AccordionItem.stx +0 -37
- package/src/ui/accordion/index.ts +0 -47
- package/src/ui/audio/Audio.stx +0 -50
- package/src/ui/audio/README.md +0 -41
- package/src/ui/audio/index.ts +0 -13
- package/src/ui/auth/Login.stx +0 -161
- package/src/ui/auth/README.md +0 -142
- package/src/ui/auth/Signup.stx +0 -159
- package/src/ui/auth/TwoFactorChallenge.stx +0 -111
- package/src/ui/auth/index.ts +0 -32
- package/src/ui/avatar/Avatar.stx +0 -77
- package/src/ui/avatar/index.ts +0 -12
- package/src/ui/badge/Badge.stx +0 -62
- package/src/ui/badge/index.ts +0 -10
- package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
- package/src/ui/breadcrumb/index.ts +0 -15
- package/src/ui/button/Button.stx +0 -74
- package/src/ui/button/README.md +0 -80
- package/src/ui/button/index.ts +0 -85
- package/src/ui/calendar/Calendar.stx +0 -189
- package/src/ui/calendar/README.md +0 -254
- package/src/ui/calendar/index.ts +0 -14
- package/src/ui/card/Card.stx +0 -68
- package/src/ui/card/index.ts +0 -13
- package/src/ui/checkbox/Checkbox.stx +0 -86
- package/src/ui/checkbox/index.ts +0 -16
- package/src/ui/combobox/Combobox.stx +0 -96
- package/src/ui/combobox/ComboboxButton.stx +0 -25
- package/src/ui/combobox/ComboboxInput.stx +0 -32
- package/src/ui/combobox/ComboboxOption.stx +0 -49
- package/src/ui/combobox/ComboboxOptions.stx +0 -16
- package/src/ui/combobox/README.md +0 -150
- package/src/ui/combobox/index.ts +0 -40
- package/src/ui/command-palette/CommandPalette.stx +0 -81
- package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
- package/src/ui/command-palette/README.md +0 -42
- package/src/ui/command-palette/index.ts +0 -15
- package/src/ui/dialog/Dialog.stx +0 -41
- package/src/ui/dialog/DialogBackdrop.stx +0 -9
- package/src/ui/dialog/DialogDescription.stx +0 -11
- package/src/ui/dialog/DialogPanel.stx +0 -16
- package/src/ui/dialog/DialogTitle.stx +0 -11
- package/src/ui/dialog/README.md +0 -121
- package/src/ui/dialog/index.ts +0 -32
- package/src/ui/drawer/Drawer.stx +0 -120
- package/src/ui/drawer/README.md +0 -90
- package/src/ui/drawer/index.ts +0 -9
- package/src/ui/dropdown/Dropdown.stx +0 -82
- package/src/ui/dropdown/DropdownButton.stx +0 -29
- package/src/ui/dropdown/DropdownItem.stx +0 -35
- package/src/ui/dropdown/DropdownItems.stx +0 -18
- package/src/ui/dropdown/README.md +0 -114
- package/src/ui/dropdown/index.ts +0 -29
- package/src/ui/form/Form.stx +0 -95
- package/src/ui/form/index.ts +0 -67
- package/src/ui/heatmap/Heatmap.stx +0 -294
- package/src/ui/image/Image.stx +0 -165
- package/src/ui/image/README.md +0 -164
- package/src/ui/image/index.ts +0 -22
- package/src/ui/input/EmailInput.stx +0 -30
- package/src/ui/input/NumberInput.stx +0 -126
- package/src/ui/input/PasswordInput.stx +0 -143
- package/src/ui/input/SearchInput.stx +0 -44
- package/src/ui/input/TextInput.stx +0 -132
- package/src/ui/input/index.ts +0 -180
- package/src/ui/listbox/Listbox.stx +0 -101
- package/src/ui/listbox/ListboxButton.stx +0 -28
- package/src/ui/listbox/ListboxLabel.stx +0 -11
- package/src/ui/listbox/ListboxOption.stx +0 -49
- package/src/ui/listbox/ListboxOptions.stx +0 -16
- package/src/ui/listbox/README.md +0 -135
- package/src/ui/listbox/index.ts +0 -39
- package/src/ui/navigator/Navigator.stx +0 -113
- package/src/ui/navigator/README.md +0 -352
- package/src/ui/navigator/index.ts +0 -20
- package/src/ui/notification/Notification.stx +0 -117
- package/src/ui/notification/README.md +0 -114
- package/src/ui/notification/index.ts +0 -12
- package/src/ui/pagination/Pagination.stx +0 -150
- package/src/ui/pagination/index.ts +0 -11
- package/src/ui/payment/Checkout.stx +0 -31
- package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
- package/src/ui/payment/PaymentMethods.stx +0 -104
- package/src/ui/payment/README.md +0 -345
- package/src/ui/payment/SubscriptionCheckout.stx +0 -323
- package/src/ui/payment/index.ts +0 -59
- package/src/ui/popover/Popover.stx +0 -60
- package/src/ui/popover/PopoverButton.stx +0 -29
- package/src/ui/popover/PopoverPanel.stx +0 -23
- package/src/ui/popover/README.md +0 -91
- package/src/ui/popover/index.ts +0 -22
- package/src/ui/portal/Portal.stx +0 -57
- package/src/ui/portal/index.ts +0 -6
- package/src/ui/progress/Progress.stx +0 -131
- package/src/ui/progress/index.ts +0 -12
- package/src/ui/radio/Radio.stx +0 -77
- package/src/ui/radio/index.ts +0 -15
- package/src/ui/radio-group/README.md +0 -139
- package/src/ui/radio-group/RadioGroup.stx +0 -60
- package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
- package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
- package/src/ui/radio-group/RadioGroupOption.stx +0 -58
- package/src/ui/radio-group/index.ts +0 -30
- package/src/ui/select/README.md +0 -180
- package/src/ui/select/Select.stx +0 -82
- package/src/ui/select/index.ts +0 -21
- package/src/ui/sidebar/Sidebar.stx +0 -458
- package/src/ui/sidebar/SidebarFooter.stx +0 -71
- package/src/ui/sidebar/SidebarHeader.stx +0 -103
- package/src/ui/sidebar/SidebarItem.stx +0 -86
- package/src/ui/sidebar/SidebarSection.stx +0 -83
- package/src/ui/sidebar/index.ts +0 -153
- package/src/ui/sidebar/themes.ts +0 -240
- package/src/ui/skeleton/Skeleton.stx +0 -48
- package/src/ui/skeleton/index.ts +0 -11
- package/src/ui/spinner/Spinner.stx +0 -109
- package/src/ui/spinner/index.ts +0 -9
- package/src/ui/stepper/README.md +0 -36
- package/src/ui/stepper/Stepper.stx +0 -17
- package/src/ui/stepper/StepperStep.stx +0 -57
- package/src/ui/stepper/index.ts +0 -19
- package/src/ui/storage/README.md +0 -193
- package/src/ui/storage/index.ts +0 -347
- package/src/ui/switch/README.md +0 -114
- package/src/ui/switch/Switch.stx +0 -72
- package/src/ui/switch/index.ts +0 -10
- package/src/ui/table/README.md +0 -39
- package/src/ui/table/Table.stx +0 -14
- package/src/ui/table/TableBody.stx +0 -10
- package/src/ui/table/TableCell.stx +0 -10
- package/src/ui/table/TableHead.stx +0 -10
- package/src/ui/table/TableHeader.stx +0 -20
- package/src/ui/table/TableRow.stx +0 -11
- package/src/ui/table/index.ts +0 -35
- package/src/ui/tabs/TabPanel.stx +0 -25
- package/src/ui/tabs/Tabs.stx +0 -202
- package/src/ui/tabs/index.ts +0 -50
- package/src/ui/teleport/Teleport.stx +0 -65
- package/src/ui/teleport/index.ts +0 -7
- package/src/ui/textarea/Textarea.stx +0 -108
- package/src/ui/textarea/index.ts +0 -20
- package/src/ui/tooltip/Tooltip.stx +0 -71
- package/src/ui/tooltip/index.ts +0 -10
- package/src/ui/transition/README.md +0 -30
- package/src/ui/transition/Transition.stx +0 -102
- package/src/ui/transition/index.ts +0 -25
- package/src/ui/video/README.md +0 -47
- package/src/ui/video/Video.stx +0 -45
- package/src/ui/video/index.ts +0 -16
- package/src/ui/virtual-list/VirtualList.stx +0 -67
- package/src/ui/virtual-list/index.ts +0 -10
- package/src/ui/virtual-table/VirtualTable.stx +0 -103
- package/src/ui/virtual-table/index.ts +0 -21
- package/src/utils/PROP_VALIDATION.md +0 -458
- package/src/utils/THEME_SYSTEM.md +0 -702
- package/src/utils/accessibility.ts +0 -841
- package/src/utils/animation/easing.ts +0 -368
- package/src/utils/animation/helpers.ts +0 -223
- package/src/utils/animation/index.ts +0 -5
- package/src/utils/animation/keyframes.ts +0 -230
- package/src/utils/error-handling.ts +0 -475
- package/src/utils/highlighter.ts +0 -93
- package/src/utils/i18n.ts +0 -634
- package/src/utils/keep-alive.ts +0 -819
- package/src/utils/lazy-loading.ts +0 -530
- package/src/utils/prop-validation.ts +0 -478
- package/src/utils/theme.ts +0 -626
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
# Prop Validation System
|
|
2
|
-
|
|
3
|
-
A comprehensive runtime prop validation system for component development with clear, actionable error messages.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Development-only warnings** - Zero runtime cost in production
|
|
8
|
-
- **TypeScript integration** - Works alongside TypeScript for compile-time checks
|
|
9
|
-
- **Clear error messages** - Actionable warnings with specific values and expected types
|
|
10
|
-
- **Extensive validators** - 20+ built-in validators for common use cases
|
|
11
|
-
- **Composable schemas** - Build complex validation schemas from simple validators
|
|
12
|
-
- **Custom validators** - Easy to create custom validation logic
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
15
|
-
|
|
16
|
-
The prop validation utilities are included in `@stacksjs/components`:
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
import { validateProps, PropTypes } from '@stacksjs/components'
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Basic Usage
|
|
23
|
-
|
|
24
|
-
### Define a Schema
|
|
25
|
-
|
|
26
|
-
```ts
|
|
27
|
-
import { PropTypes } from '@stacksjs/components'
|
|
28
|
-
|
|
29
|
-
const buttonPropSchema = {
|
|
30
|
-
variant: PropTypes.oneOf(['primary', 'secondary', 'outline']),
|
|
31
|
-
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
32
|
-
disabled: PropTypes.boolean,
|
|
33
|
-
onClick: PropTypes.func.required, // Mark as required
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Validate Props
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
import { validateProps } from '@stacksjs/components'
|
|
41
|
-
|
|
42
|
-
function Button(props) {
|
|
43
|
-
// Validate props (only warns in development)
|
|
44
|
-
validateProps('Button', props, buttonPropSchema)
|
|
45
|
-
|
|
46
|
-
// ... rest of component
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Using createPropValidator
|
|
51
|
-
|
|
52
|
-
For reusable validators:
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
import { createPropValidator, PropTypes } from '@stacksjs/components'
|
|
56
|
-
|
|
57
|
-
const validateButtonProps = createPropValidator('Button', {
|
|
58
|
-
variant: PropTypes.oneOf(['primary', 'secondary', 'outline']),
|
|
59
|
-
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
60
|
-
disabled: PropTypes.boolean,
|
|
61
|
-
onClick: PropTypes.func.required,
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
// In component
|
|
65
|
-
function Button(props) {
|
|
66
|
-
validateButtonProps(props)
|
|
67
|
-
// ... rest of component
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Available Validators
|
|
72
|
-
|
|
73
|
-
### Primitive Types
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
PropTypes.string // String type
|
|
77
|
-
PropTypes.number // Number type (excludes NaN)
|
|
78
|
-
PropTypes.boolean // Boolean type
|
|
79
|
-
PropTypes.func // Function type
|
|
80
|
-
PropTypes.object // Object type (excludes arrays and null)
|
|
81
|
-
PropTypes.array // Array type
|
|
82
|
-
PropTypes.any // Any type (always passes)
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Enum Validators
|
|
86
|
-
|
|
87
|
-
```ts
|
|
88
|
-
// Value must be one of the specified options
|
|
89
|
-
PropTypes.oneOf(['sm', 'md', 'lg'])
|
|
90
|
-
PropTypes.oneOf([1, 2, 3])
|
|
91
|
-
|
|
92
|
-
// Value must match one of the specified types
|
|
93
|
-
PropTypes.oneOfType([
|
|
94
|
-
PropTypes.string,
|
|
95
|
-
PropTypes.number
|
|
96
|
-
])
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Array Validators
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
// Array of specific type
|
|
103
|
-
PropTypes.arrayOf(PropTypes.string)
|
|
104
|
-
PropTypes.arrayOf(PropTypes.number)
|
|
105
|
-
|
|
106
|
-
// Array of objects with specific shape
|
|
107
|
-
PropTypes.arrayOf(
|
|
108
|
-
PropTypes.shape({
|
|
109
|
-
id: PropTypes.number.required,
|
|
110
|
-
name: PropTypes.string.required
|
|
111
|
-
})
|
|
112
|
-
)
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Object Validators
|
|
116
|
-
|
|
117
|
-
```ts
|
|
118
|
-
// Object with specific shape
|
|
119
|
-
PropTypes.shape({
|
|
120
|
-
name: PropTypes.string.required,
|
|
121
|
-
age: PropTypes.number,
|
|
122
|
-
email: PropTypes.email,
|
|
123
|
-
address: PropTypes.shape({
|
|
124
|
-
street: PropTypes.string,
|
|
125
|
-
city: PropTypes.string.required
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
// Instance of a class
|
|
130
|
-
PropTypes.instanceOf(Date)
|
|
131
|
-
PropTypes.instanceOf(HTMLElement)
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### Number Validators
|
|
135
|
-
|
|
136
|
-
```ts
|
|
137
|
-
// Minimum value
|
|
138
|
-
PropTypes.min(0)
|
|
139
|
-
|
|
140
|
-
// Maximum value
|
|
141
|
-
PropTypes.max(100)
|
|
142
|
-
|
|
143
|
-
// Range
|
|
144
|
-
PropTypes.range(0, 100)
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### String Validators
|
|
148
|
-
|
|
149
|
-
```ts
|
|
150
|
-
// Pattern matching
|
|
151
|
-
PropTypes.pattern(/^[A-Z]{3}$/)
|
|
152
|
-
|
|
153
|
-
// Email validation
|
|
154
|
-
PropTypes.email
|
|
155
|
-
|
|
156
|
-
// URL validation
|
|
157
|
-
PropTypes.url
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Custom Validators
|
|
161
|
-
|
|
162
|
-
```ts
|
|
163
|
-
// Custom validation function
|
|
164
|
-
PropTypes.custom(
|
|
165
|
-
(value) => value > 0 && value < 100,
|
|
166
|
-
'Value must be between 0 and 100'
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
// Custom with dynamic error message
|
|
170
|
-
PropTypes.custom(
|
|
171
|
-
(value, propName, componentName) => typeof value === 'string' && value.length > 5,
|
|
172
|
-
(value, propName, componentName) =>
|
|
173
|
-
`Prop \`${propName}\` in \`${componentName}\` must be a string longer than 5 characters`
|
|
174
|
-
)
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
## Required Props
|
|
178
|
-
|
|
179
|
-
Mark any validator as required:
|
|
180
|
-
|
|
181
|
-
```ts
|
|
182
|
-
const schema = {
|
|
183
|
-
name: PropTypes.string.required,
|
|
184
|
-
age: PropTypes.number.required,
|
|
185
|
-
email: PropTypes.email.required,
|
|
186
|
-
onClick: PropTypes.func.required
|
|
187
|
-
}
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
## Complex Examples
|
|
191
|
-
|
|
192
|
-
### Form Input Validation
|
|
193
|
-
|
|
194
|
-
```ts
|
|
195
|
-
const inputPropSchema = {
|
|
196
|
-
value: PropTypes.string,
|
|
197
|
-
onChange: PropTypes.func.required,
|
|
198
|
-
placeholder: PropTypes.string,
|
|
199
|
-
disabled: PropTypes.boolean,
|
|
200
|
-
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
201
|
-
error: PropTypes.boolean,
|
|
202
|
-
label: PropTypes.string,
|
|
203
|
-
helperText: PropTypes.string,
|
|
204
|
-
required: PropTypes.boolean,
|
|
205
|
-
maxLength: PropTypes.range(1, 1000),
|
|
206
|
-
pattern: PropTypes.instanceOf(RegExp)
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### User Object Validation
|
|
211
|
-
|
|
212
|
-
```ts
|
|
213
|
-
const userPropSchema = {
|
|
214
|
-
user: PropTypes.shape({
|
|
215
|
-
id: PropTypes.number.required,
|
|
216
|
-
name: PropTypes.string.required,
|
|
217
|
-
email: PropTypes.email.required,
|
|
218
|
-
age: PropTypes.range(0, 150),
|
|
219
|
-
address: PropTypes.shape({
|
|
220
|
-
street: PropTypes.string,
|
|
221
|
-
city: PropTypes.string.required,
|
|
222
|
-
zipCode: PropTypes.pattern(/^\d{5}$/)
|
|
223
|
-
}),
|
|
224
|
-
roles: PropTypes.arrayOf(
|
|
225
|
-
PropTypes.oneOf(['admin', 'user', 'guest'])
|
|
226
|
-
)
|
|
227
|
-
}).required
|
|
228
|
-
}
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### Complex Component Props
|
|
232
|
-
|
|
233
|
-
```ts
|
|
234
|
-
const dataPropSchema = {
|
|
235
|
-
items: PropTypes.arrayOf(
|
|
236
|
-
PropTypes.shape({
|
|
237
|
-
id: PropTypes.oneOfType([
|
|
238
|
-
PropTypes.string,
|
|
239
|
-
PropTypes.number
|
|
240
|
-
]).required,
|
|
241
|
-
label: PropTypes.string.required,
|
|
242
|
-
value: PropTypes.any,
|
|
243
|
-
disabled: PropTypes.boolean,
|
|
244
|
-
metadata: PropTypes.object
|
|
245
|
-
})
|
|
246
|
-
).required,
|
|
247
|
-
selectedId: PropTypes.oneOfType([
|
|
248
|
-
PropTypes.string,
|
|
249
|
-
PropTypes.number
|
|
250
|
-
]),
|
|
251
|
-
onSelect: PropTypes.func.required,
|
|
252
|
-
multiSelect: PropTypes.boolean,
|
|
253
|
-
maxSelections: PropTypes.custom(
|
|
254
|
-
(value) => typeof value === 'number' && value > 0,
|
|
255
|
-
'maxSelections must be a positive number'
|
|
256
|
-
)
|
|
257
|
-
}
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
## Validation Options
|
|
261
|
-
|
|
262
|
-
```ts
|
|
263
|
-
validateProps('Component', props, schema, {
|
|
264
|
-
// Throw error instead of warning (default: false)
|
|
265
|
-
throwOnError: true,
|
|
266
|
-
|
|
267
|
-
// Disable console warnings (default: true in dev)
|
|
268
|
-
logWarnings: false
|
|
269
|
-
})
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
## Helper Functions
|
|
273
|
-
|
|
274
|
-
### assertProp
|
|
275
|
-
|
|
276
|
-
Assert a value matches a validator (throws on failure):
|
|
277
|
-
|
|
278
|
-
```ts
|
|
279
|
-
import { assertProp, PropTypes } from '@stacksjs/components'
|
|
280
|
-
|
|
281
|
-
function processUser(user: unknown) {
|
|
282
|
-
// Throws if user doesn't match shape
|
|
283
|
-
assertProp(user, PropTypes.shape({
|
|
284
|
-
id: PropTypes.number.required,
|
|
285
|
-
name: PropTypes.string.required
|
|
286
|
-
}), 'user')
|
|
287
|
-
|
|
288
|
-
// TypeScript now knows user has the correct shape
|
|
289
|
-
console.log(user.id, user.name)
|
|
290
|
-
}
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### warnProp
|
|
294
|
-
|
|
295
|
-
Warn if prop doesn't match validator (non-throwing):
|
|
296
|
-
|
|
297
|
-
```ts
|
|
298
|
-
import { warnProp, PropTypes } from '@stacksjs/components'
|
|
299
|
-
|
|
300
|
-
function logValue(value: unknown) {
|
|
301
|
-
// Only warns in development, doesn't throw
|
|
302
|
-
warnProp(value, PropTypes.string, 'value')
|
|
303
|
-
|
|
304
|
-
console.log(value)
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
## Error Messages
|
|
309
|
-
|
|
310
|
-
The validation system provides clear, actionable error messages:
|
|
311
|
-
|
|
312
|
-
```
|
|
313
|
-
⚠️ Prop validation failed for `Button`
|
|
314
|
-
Invalid prop `variant` of value `large` supplied to `Button`, expected one of ["primary", "secondary", "outline", "ghost", "danger"].
|
|
315
|
-
Required prop `onClick` was not specified in `Button`.
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
## Production Behavior
|
|
319
|
-
|
|
320
|
-
- All validation is **completely skipped in production** builds
|
|
321
|
-
- Zero runtime overhead
|
|
322
|
-
- No console warnings
|
|
323
|
-
- `validateProps()`, `assertProp()`, and `warnProp()` become no-ops
|
|
324
|
-
|
|
325
|
-
## TypeScript Integration
|
|
326
|
-
|
|
327
|
-
Prop validation works alongside TypeScript:
|
|
328
|
-
|
|
329
|
-
```ts
|
|
330
|
-
interface ButtonProps {
|
|
331
|
-
variant?: 'primary' | 'secondary' | 'outline'
|
|
332
|
-
size?: 'sm' | 'md' | 'lg'
|
|
333
|
-
disabled?: boolean
|
|
334
|
-
onClick: (event: Event) => void
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const buttonPropSchema = {
|
|
338
|
-
variant: PropTypes.oneOf(['primary', 'secondary', 'outline']),
|
|
339
|
-
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
340
|
-
disabled: PropTypes.boolean,
|
|
341
|
-
onClick: PropTypes.func.required
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
function Button(props: ButtonProps) {
|
|
345
|
-
// TypeScript provides compile-time checking
|
|
346
|
-
// PropTypes provides runtime checking in development
|
|
347
|
-
validateProps('Button', props, buttonPropSchema)
|
|
348
|
-
|
|
349
|
-
// ...
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
## Best Practices
|
|
354
|
-
|
|
355
|
-
### 1. Define schemas separately
|
|
356
|
-
|
|
357
|
-
```ts
|
|
358
|
-
// schemas/button.ts
|
|
359
|
-
export const buttonPropSchema = {
|
|
360
|
-
variant: PropTypes.oneOf(['primary', 'secondary']),
|
|
361
|
-
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
362
|
-
onClick: PropTypes.func.required
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// components/Button.ts
|
|
366
|
-
import { buttonPropSchema } from '../schemas/button'
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### 2. Reuse base schemas
|
|
370
|
-
|
|
371
|
-
```ts
|
|
372
|
-
const baseInputSchema = {
|
|
373
|
-
value: PropTypes.string,
|
|
374
|
-
onChange: PropTypes.func,
|
|
375
|
-
disabled: PropTypes.boolean,
|
|
376
|
-
size: PropTypes.oneOf(['sm', 'md', 'lg'])
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
const textInputSchema = {
|
|
380
|
-
...baseInputSchema,
|
|
381
|
-
maxLength: PropTypes.number,
|
|
382
|
-
placeholder: PropTypes.string
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
const numberInputSchema = {
|
|
386
|
-
...baseInputSchema,
|
|
387
|
-
min: PropTypes.number,
|
|
388
|
-
max: PropTypes.number,
|
|
389
|
-
step: PropTypes.number
|
|
390
|
-
}
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
### 3. Create component-specific validators
|
|
394
|
-
|
|
395
|
-
```ts
|
|
396
|
-
// ui/button/index.ts
|
|
397
|
-
import { createPropValidator, PropTypes } from '@stacksjs/components'
|
|
398
|
-
|
|
399
|
-
export const validateButtonProps = createPropValidator('Button', {
|
|
400
|
-
variant: PropTypes.oneOf(['primary', 'secondary', 'outline']),
|
|
401
|
-
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
402
|
-
onClick: PropTypes.func.required
|
|
403
|
-
})
|
|
404
|
-
|
|
405
|
-
// Export for use in other files
|
|
406
|
-
export { validateButtonProps }
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### 4. Use custom validators for complex logic
|
|
410
|
-
|
|
411
|
-
```ts
|
|
412
|
-
const isPowerOfTwo = PropTypes.custom(
|
|
413
|
-
(value) => typeof value === 'number' && (value & (value - 1)) === 0,
|
|
414
|
-
'Value must be a power of 2'
|
|
415
|
-
)
|
|
416
|
-
|
|
417
|
-
const isEven = PropTypes.custom(
|
|
418
|
-
(value) => typeof value === 'number' && value % 2 === 0,
|
|
419
|
-
'Value must be an even number'
|
|
420
|
-
)
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
## Migration from React PropTypes
|
|
424
|
-
|
|
425
|
-
If you're familiar with React's PropTypes, this system follows a similar API:
|
|
426
|
-
|
|
427
|
-
```ts
|
|
428
|
-
// React PropTypes
|
|
429
|
-
Component.propTypes = {
|
|
430
|
-
name: PropTypes.string.isRequired,
|
|
431
|
-
age: PropTypes.number,
|
|
432
|
-
onClick: PropTypes.func.isRequired
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// @stacksjs/components
|
|
436
|
-
const componentPropSchema = {
|
|
437
|
-
name: PropTypes.string.required,
|
|
438
|
-
age: PropTypes.number,
|
|
439
|
-
onClick: PropTypes.func.required
|
|
440
|
-
}
|
|
441
|
-
validateProps('Component', props, componentPropSchema)
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
Key differences:
|
|
445
|
-
- Use `.required` instead of `.isRequired`
|
|
446
|
-
- Validation is a separate function call, not a static property
|
|
447
|
-
- Validation only runs in development mode
|
|
448
|
-
|
|
449
|
-
## Examples in the Library
|
|
450
|
-
|
|
451
|
-
Several components in the library already use prop validation:
|
|
452
|
-
|
|
453
|
-
- **Button** - `src/ui/button/index.ts`
|
|
454
|
-
- **TextInput** - `src/ui/input/index.ts`
|
|
455
|
-
- **NumberInput** - `src/ui/input/index.ts`
|
|
456
|
-
- **Form** - `src/ui/form/index.ts`
|
|
457
|
-
|
|
458
|
-
Check these files for real-world examples of prop validation in action.
|