@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,478 +0,0 @@
|
|
|
1
|
-
import process from 'node:process'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Runtime prop validation utilities for component development
|
|
5
|
-
*
|
|
6
|
-
* Provides development-only warnings for invalid prop values with clear,
|
|
7
|
-
* actionable error messages.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { validateProps, PropTypes } from '@stacksjs/components'
|
|
12
|
-
*
|
|
13
|
-
* // Define prop schema
|
|
14
|
-
* const schema = {
|
|
15
|
-
* size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
16
|
-
* count: PropTypes.number.required,
|
|
17
|
-
* onClick: PropTypes.func,
|
|
18
|
-
* user: PropTypes.shape({
|
|
19
|
-
* name: PropTypes.string.required,
|
|
20
|
-
* age: PropTypes.number
|
|
21
|
-
* })
|
|
22
|
-
* }
|
|
23
|
-
*
|
|
24
|
-
* // Validate props
|
|
25
|
-
* validateProps('Button', props, schema)
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Environment check - only validate in development
|
|
31
|
-
*/
|
|
32
|
-
const isDevelopment = process.env.NODE_ENV !== 'production'
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Prop validator function type
|
|
36
|
-
*/
|
|
37
|
-
export interface PropValidator<_T = any> {
|
|
38
|
-
/** Validator function */
|
|
39
|
-
validate: (value: any, propName: string, componentName: string) => boolean
|
|
40
|
-
/** Error message generator */
|
|
41
|
-
message: (value: any, propName: string, componentName: string) => string
|
|
42
|
-
/** Whether this prop is required */
|
|
43
|
-
isRequired?: boolean
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Base validator class
|
|
48
|
-
*/
|
|
49
|
-
class Validator<T = any> implements PropValidator<T> {
|
|
50
|
-
constructor(
|
|
51
|
-
public validate: (value: any, propName: string, componentName: string) => boolean,
|
|
52
|
-
public message: (value: any, propName: string, componentName: string) => string,
|
|
53
|
-
public isRequired = false,
|
|
54
|
-
) {}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Mark this validator as required
|
|
58
|
-
*/
|
|
59
|
-
get required(): Validator<T> {
|
|
60
|
-
return new Validator(this.validate, this.message, true)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* PropTypes - collection of common validators
|
|
66
|
-
*/
|
|
67
|
-
export const PropTypes: {
|
|
68
|
-
string: Validator<string>
|
|
69
|
-
number: Validator<number>
|
|
70
|
-
boolean: Validator<boolean>
|
|
71
|
-
func: Validator<(...args: any[]) => any>
|
|
72
|
-
object: Validator<object>
|
|
73
|
-
array: Validator<any[]>
|
|
74
|
-
any: Validator<any>
|
|
75
|
-
email: Validator<string>
|
|
76
|
-
url: Validator<string>
|
|
77
|
-
oneOf: <T>(validValues: T[]) => Validator<T>
|
|
78
|
-
oneOfType: (validators: Validator[]) => Validator
|
|
79
|
-
arrayOf: (validator: Validator) => Validator
|
|
80
|
-
shape: (shape: Record<string, Validator>) => Validator
|
|
81
|
-
instanceOf: <T>(expectedClass: new (...args: any[]) => T) => Validator<T>
|
|
82
|
-
custom: (validatorFn: (value: any, propName: string, componentName: string) => boolean, errorMessage: string | ((value: any, propName: string, componentName: string) => string)) => Validator
|
|
83
|
-
min: (min: number) => Validator<number>
|
|
84
|
-
max: (max: number) => Validator<number>
|
|
85
|
-
range: (min: number, max: number) => Validator<number>
|
|
86
|
-
pattern: (pattern: RegExp) => Validator<string>
|
|
87
|
-
} = {
|
|
88
|
-
/**
|
|
89
|
-
* String type validator
|
|
90
|
-
*/
|
|
91
|
-
string: new Validator<string>(
|
|
92
|
-
value => typeof value === 'string',
|
|
93
|
-
(value, propName, componentName) =>
|
|
94
|
-
`Invalid prop \`${propName}\` of type \`${typeof value}\` supplied to \`${componentName}\`, expected \`string\`.`,
|
|
95
|
-
),
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Number type validator
|
|
99
|
-
*/
|
|
100
|
-
number: new Validator<number>(
|
|
101
|
-
value => typeof value === 'number' && !Number.isNaN(value),
|
|
102
|
-
(value, propName, componentName) =>
|
|
103
|
-
`Invalid prop \`${propName}\` of type \`${typeof value}\` supplied to \`${componentName}\`, expected \`number\`.`,
|
|
104
|
-
),
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Boolean type validator
|
|
108
|
-
*/
|
|
109
|
-
boolean: new Validator<boolean>(
|
|
110
|
-
value => typeof value === 'boolean',
|
|
111
|
-
(value, propName, componentName) =>
|
|
112
|
-
`Invalid prop \`${propName}\` of type \`${typeof value}\` supplied to \`${componentName}\`, expected \`boolean\`.`,
|
|
113
|
-
),
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Function type validator
|
|
117
|
-
*/
|
|
118
|
-
func: new Validator<(...args: any[]) => any>(
|
|
119
|
-
value => typeof value === 'function',
|
|
120
|
-
(value, propName, componentName) =>
|
|
121
|
-
`Invalid prop \`${propName}\` of type \`${typeof value}\` supplied to \`${componentName}\`, expected \`function\`.`,
|
|
122
|
-
),
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Object type validator
|
|
126
|
-
*/
|
|
127
|
-
object: new Validator<object>(
|
|
128
|
-
value => typeof value === 'object' && value !== null && !Array.isArray(value),
|
|
129
|
-
(value, propName, componentName) =>
|
|
130
|
-
`Invalid prop \`${propName}\` of type \`${typeof value}\` supplied to \`${componentName}\`, expected \`object\`.`,
|
|
131
|
-
),
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Array type validator
|
|
135
|
-
*/
|
|
136
|
-
array: new Validator<any[]>(
|
|
137
|
-
value => Array.isArray(value),
|
|
138
|
-
(value, propName, componentName) =>
|
|
139
|
-
`Invalid prop \`${propName}\` of type \`${typeof value}\` supplied to \`${componentName}\`, expected \`array\`.`,
|
|
140
|
-
),
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Any type validator (always passes)
|
|
144
|
-
*/
|
|
145
|
-
any: new Validator<any>(
|
|
146
|
-
() => true,
|
|
147
|
-
() => '',
|
|
148
|
-
),
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* OneOf validator - value must be one of specified options
|
|
152
|
-
*
|
|
153
|
-
* @param validValues - Array of valid values
|
|
154
|
-
*/
|
|
155
|
-
oneOf<T>(validValues: T[]): Validator<T> {
|
|
156
|
-
return new Validator(
|
|
157
|
-
value => validValues.includes(value),
|
|
158
|
-
(value, propName, componentName) =>
|
|
159
|
-
`Invalid prop \`${propName}\` of value \`${value}\` supplied to \`${componentName}\`, expected one of [${validValues.map(v => JSON.stringify(v)).join(', ')}].`,
|
|
160
|
-
)
|
|
161
|
-
},
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* OneOfType validator - value must match one of specified types
|
|
165
|
-
*
|
|
166
|
-
* @param validators - Array of validators
|
|
167
|
-
*/
|
|
168
|
-
oneOfType(validators: Validator[]): Validator {
|
|
169
|
-
return new Validator(
|
|
170
|
-
(value, propName, componentName) =>
|
|
171
|
-
validators.some(v => v.validate(value, propName, componentName)),
|
|
172
|
-
(_value, propName, componentName) =>
|
|
173
|
-
`Invalid prop \`${propName}\` supplied to \`${componentName}\`, expected one of the specified types.`,
|
|
174
|
-
)
|
|
175
|
-
},
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* ArrayOf validator - all array elements must match type
|
|
179
|
-
*
|
|
180
|
-
* @param validator - Validator for array elements
|
|
181
|
-
*/
|
|
182
|
-
arrayOf(validator: Validator): Validator {
|
|
183
|
-
return new Validator(
|
|
184
|
-
(value, propName, componentName) => {
|
|
185
|
-
if (!Array.isArray(value))
|
|
186
|
-
return false
|
|
187
|
-
return value.every((item, index) => validator.validate(item, `${propName}[${index}]`, componentName))
|
|
188
|
-
},
|
|
189
|
-
(_value, propName, componentName) =>
|
|
190
|
-
`Invalid prop \`${propName}\` supplied to \`${componentName}\`, expected array of specified type.`,
|
|
191
|
-
)
|
|
192
|
-
},
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Shape validator - object must match specified shape
|
|
196
|
-
*
|
|
197
|
-
* @param shape - Object shape definition
|
|
198
|
-
*/
|
|
199
|
-
shape(shape: Record<string, Validator>): Validator {
|
|
200
|
-
return new Validator(
|
|
201
|
-
(value, propName, componentName) => {
|
|
202
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
203
|
-
return false
|
|
204
|
-
|
|
205
|
-
// Check all required fields
|
|
206
|
-
for (const [key, validator] of Object.entries(shape)) {
|
|
207
|
-
if (validator.isRequired && !(key in value))
|
|
208
|
-
return false
|
|
209
|
-
|
|
210
|
-
if (key in value && !validator.validate(value[key], `${propName}.${key}`, componentName))
|
|
211
|
-
return false
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return true
|
|
215
|
-
},
|
|
216
|
-
(_value, propName, componentName) =>
|
|
217
|
-
`Invalid prop \`${propName}\` supplied to \`${componentName}\`, expected object matching specified shape.`,
|
|
218
|
-
)
|
|
219
|
-
},
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* InstanceOf validator - value must be instance of class
|
|
223
|
-
*
|
|
224
|
-
* @param expectedClass - Expected class constructor
|
|
225
|
-
*/
|
|
226
|
-
instanceOf<T>(expectedClass: new (...args: any[]) => T): Validator<T> {
|
|
227
|
-
return new Validator(
|
|
228
|
-
value => value instanceof expectedClass,
|
|
229
|
-
(_value, propName, componentName) =>
|
|
230
|
-
`Invalid prop \`${propName}\` supplied to \`${componentName}\`, expected instance of \`${expectedClass.name}\`.`,
|
|
231
|
-
)
|
|
232
|
-
},
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Custom validator
|
|
236
|
-
*
|
|
237
|
-
* @param validatorFn - Custom validation function
|
|
238
|
-
* @param errorMessage - Error message or message generator
|
|
239
|
-
*/
|
|
240
|
-
custom(
|
|
241
|
-
validatorFn: (value: any, propName: string, componentName: string) => boolean,
|
|
242
|
-
errorMessage: string | ((_value: any, _propName: string, _componentName: string) => string),
|
|
243
|
-
): Validator {
|
|
244
|
-
const messageGenerator = typeof errorMessage === 'string'
|
|
245
|
-
? () => errorMessage
|
|
246
|
-
: errorMessage
|
|
247
|
-
|
|
248
|
-
return new Validator(validatorFn, messageGenerator)
|
|
249
|
-
},
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Min validator for numbers
|
|
253
|
-
*
|
|
254
|
-
* @param min - Minimum value (inclusive)
|
|
255
|
-
*/
|
|
256
|
-
min(min: number): Validator<number> {
|
|
257
|
-
return new Validator(
|
|
258
|
-
value => typeof value === 'number' && value >= min,
|
|
259
|
-
(value, propName, componentName) =>
|
|
260
|
-
`Invalid prop \`${propName}\` of value \`${value}\` supplied to \`${componentName}\`, expected value >= ${min}.`,
|
|
261
|
-
)
|
|
262
|
-
},
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Max validator for numbers
|
|
266
|
-
*
|
|
267
|
-
* @param max - Maximum value (inclusive)
|
|
268
|
-
*/
|
|
269
|
-
max(max: number): Validator<number> {
|
|
270
|
-
return new Validator(
|
|
271
|
-
value => typeof value === 'number' && value <= max,
|
|
272
|
-
(value, propName, componentName) =>
|
|
273
|
-
`Invalid prop \`${propName}\` of value \`${value}\` supplied to \`${componentName}\`, expected value <= ${max}.`,
|
|
274
|
-
)
|
|
275
|
-
},
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Range validator for numbers
|
|
279
|
-
*
|
|
280
|
-
* @param min - Minimum value (inclusive)
|
|
281
|
-
* @param max - Maximum value (inclusive)
|
|
282
|
-
*/
|
|
283
|
-
range(min: number, max: number): Validator<number> {
|
|
284
|
-
return new Validator(
|
|
285
|
-
value => typeof value === 'number' && value >= min && value <= max,
|
|
286
|
-
(value, propName, componentName) =>
|
|
287
|
-
`Invalid prop \`${propName}\` of value \`${value}\` supplied to \`${componentName}\`, expected value between ${min} and ${max}.`,
|
|
288
|
-
)
|
|
289
|
-
},
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Pattern validator for strings
|
|
293
|
-
*
|
|
294
|
-
* @param pattern - Regular expression pattern
|
|
295
|
-
*/
|
|
296
|
-
pattern(pattern: RegExp): Validator<string> {
|
|
297
|
-
return new Validator(
|
|
298
|
-
value => typeof value === 'string' && pattern.test(value),
|
|
299
|
-
(value, propName, componentName) =>
|
|
300
|
-
`Invalid prop \`${propName}\` of value \`${value}\` supplied to \`${componentName}\`, expected value matching pattern ${pattern}.`,
|
|
301
|
-
)
|
|
302
|
-
},
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Email validator
|
|
306
|
-
*/
|
|
307
|
-
email: new Validator<string>(
|
|
308
|
-
value => typeof value === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
|
|
309
|
-
(value, propName, componentName) =>
|
|
310
|
-
`Invalid prop \`${propName}\` of value \`${value}\` supplied to \`${componentName}\`, expected valid email address.`,
|
|
311
|
-
),
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* URL validator
|
|
315
|
-
*/
|
|
316
|
-
url: new Validator<string>(
|
|
317
|
-
(value) => {
|
|
318
|
-
if (typeof value !== 'string')
|
|
319
|
-
return false
|
|
320
|
-
try {
|
|
321
|
-
const _url = new URL(value)
|
|
322
|
-
return true
|
|
323
|
-
}
|
|
324
|
-
catch {
|
|
325
|
-
return false
|
|
326
|
-
}
|
|
327
|
-
},
|
|
328
|
-
(value, propName, componentName) =>
|
|
329
|
-
`Invalid prop \`${propName}\` of value \`${value}\` supplied to \`${componentName}\`, expected valid URL.`,
|
|
330
|
-
),
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Prop schema definition
|
|
335
|
-
*/
|
|
336
|
-
export type PropSchema = Record<string, Validator>
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Validation result
|
|
340
|
-
*/
|
|
341
|
-
export interface ValidationResult {
|
|
342
|
-
/** Whether validation passed */
|
|
343
|
-
valid: boolean
|
|
344
|
-
/** Array of error messages */
|
|
345
|
-
errors: string[]
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Validate props against schema
|
|
350
|
-
*
|
|
351
|
-
* @param componentName - Name of component being validated
|
|
352
|
-
* @param props - Props object to validate
|
|
353
|
-
* @param schema - Prop schema definition
|
|
354
|
-
* @param options - Validation options
|
|
355
|
-
* @param options.throwOnError - Whether to throw on validation failure
|
|
356
|
-
* @param options.logWarnings - Whether to log warnings to console
|
|
357
|
-
* @returns Validation result
|
|
358
|
-
*/
|
|
359
|
-
export function validateProps(
|
|
360
|
-
componentName: string,
|
|
361
|
-
props: Record<string, any>,
|
|
362
|
-
schema: PropSchema,
|
|
363
|
-
options: {
|
|
364
|
-
/** Whether to throw on validation failure (default: false) */
|
|
365
|
-
throwOnError?: boolean
|
|
366
|
-
/** Whether to log warnings to console (default: true in dev) */
|
|
367
|
-
logWarnings?: boolean
|
|
368
|
-
} = {},
|
|
369
|
-
): ValidationResult {
|
|
370
|
-
const { throwOnError = false, logWarnings = isDevelopment } = options
|
|
371
|
-
const errors: string[] = []
|
|
372
|
-
|
|
373
|
-
// Skip validation in production
|
|
374
|
-
if (!isDevelopment) {
|
|
375
|
-
return { valid: true, errors: [] }
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Check each prop in schema
|
|
379
|
-
for (const [propName, validator] of Object.entries(schema)) {
|
|
380
|
-
const propValue = props[propName]
|
|
381
|
-
const isUndefined = propValue === undefined
|
|
382
|
-
|
|
383
|
-
// Check required props
|
|
384
|
-
if (validator.isRequired && isUndefined) {
|
|
385
|
-
const error = `Required prop \`${propName}\` was not specified in \`${componentName}\`.`
|
|
386
|
-
errors.push(error)
|
|
387
|
-
continue
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Skip validation if prop is undefined and not required
|
|
391
|
-
if (isUndefined)
|
|
392
|
-
continue
|
|
393
|
-
|
|
394
|
-
// Run validator
|
|
395
|
-
if (!validator.validate(propValue, propName, componentName)) {
|
|
396
|
-
const error = validator.message(propValue, propName, componentName)
|
|
397
|
-
errors.push(error)
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Handle errors
|
|
402
|
-
if (errors.length > 0) {
|
|
403
|
-
if (logWarnings) {
|
|
404
|
-
console.group(`⚠️ Prop validation failed for \`${componentName}\``)
|
|
405
|
-
errors.forEach(error => console.warn(error))
|
|
406
|
-
console.groupEnd()
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (throwOnError) {
|
|
410
|
-
throw new Error(`Prop validation failed for \`${componentName}\`:\n${errors.join('\n')}`)
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
return {
|
|
415
|
-
valid: errors.length === 0,
|
|
416
|
-
errors,
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Create a prop validator function for a component
|
|
422
|
-
*
|
|
423
|
-
* @param componentName - Name of component
|
|
424
|
-
* @param schema - Prop schema definition
|
|
425
|
-
* @returns Validator function
|
|
426
|
-
*
|
|
427
|
-
* @example
|
|
428
|
-
* ```ts
|
|
429
|
-
* const validateButtonProps = createPropValidator('Button', {
|
|
430
|
-
* size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
431
|
-
* disabled: PropTypes.boolean,
|
|
432
|
-
* onClick: PropTypes.func.required
|
|
433
|
-
* })
|
|
434
|
-
*
|
|
435
|
-
* // In component
|
|
436
|
-
* validateButtonProps(props)
|
|
437
|
-
* ```
|
|
438
|
-
*/
|
|
439
|
-
export function createPropValidator(
|
|
440
|
-
componentName: string,
|
|
441
|
-
schema: PropSchema,
|
|
442
|
-
options?: { throwOnError?: boolean, logWarnings?: boolean },
|
|
443
|
-
): (props: Record<string, any>) => ValidationResult {
|
|
444
|
-
return (props: Record<string, any>) => validateProps(componentName, props, schema, options)
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Assert that a value matches a validator
|
|
449
|
-
*
|
|
450
|
-
* @param value - Value to validate
|
|
451
|
-
* @param validator - Validator to use
|
|
452
|
-
* @param name - Name for error messages
|
|
453
|
-
* @throws Error if validation fails
|
|
454
|
-
*/
|
|
455
|
-
export function assertProp<T>(value: any, validator: Validator<T>, name = 'value'): asserts value is T {
|
|
456
|
-
if (!isDevelopment)
|
|
457
|
-
return
|
|
458
|
-
|
|
459
|
-
if (!validator.validate(value, name, 'assertion')) {
|
|
460
|
-
throw new Error(validator.message(value, name, 'assertion'))
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* Warn if prop doesn't match validator (non-throwing)
|
|
466
|
-
*
|
|
467
|
-
* @param value - Value to validate
|
|
468
|
-
* @param validator - Validator to use
|
|
469
|
-
* @param name - Name for warning messages
|
|
470
|
-
*/
|
|
471
|
-
export function warnProp(value: any, validator: Validator, name = 'value'): void {
|
|
472
|
-
if (!isDevelopment)
|
|
473
|
-
return
|
|
474
|
-
|
|
475
|
-
if (!validator.validate(value, name, 'warning')) {
|
|
476
|
-
console.warn(validator.message(value, name, 'warning'))
|
|
477
|
-
}
|
|
478
|
-
}
|