@stacksjs/components 0.2.93 → 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.
Files changed (187) hide show
  1. package/dist/index.js +4 -4
  2. package/package.json +5 -6
  3. package/src/components/CodeBlock.stx +0 -54
  4. package/src/components/Footer.stx +0 -66
  5. package/src/components/Hero.stx +0 -25
  6. package/src/components/Installation.stx +0 -37
  7. package/src/components/index.ts +0 -4
  8. package/src/composables/index.ts +0 -4
  9. package/src/composables/useCopyCode.ts +0 -73
  10. package/src/composables/useDarkMode.ts +0 -100
  11. package/src/composables/useSEO.ts +0 -230
  12. package/src/composables/utilities/index.ts +0 -7
  13. package/src/composables/utilities/useClickOutside.ts +0 -72
  14. package/src/composables/utilities/useFocusTrap.ts +0 -159
  15. package/src/composables/utilities/useKeyboard.ts +0 -124
  16. package/src/composables/utilities/useLocalStorage.ts +0 -132
  17. package/src/composables/utilities/useMediaQuery.ts +0 -118
  18. package/src/index.ts +0 -99
  19. package/src/stx.d.ts +0 -4
  20. package/src/ui/accordion/Accordion.stx +0 -146
  21. package/src/ui/accordion/AccordionItem.stx +0 -37
  22. package/src/ui/accordion/index.ts +0 -47
  23. package/src/ui/audio/Audio.stx +0 -50
  24. package/src/ui/audio/README.md +0 -41
  25. package/src/ui/audio/index.ts +0 -13
  26. package/src/ui/auth/Login.stx +0 -161
  27. package/src/ui/auth/README.md +0 -142
  28. package/src/ui/auth/Signup.stx +0 -159
  29. package/src/ui/auth/TwoFactorChallenge.stx +0 -111
  30. package/src/ui/auth/index.ts +0 -32
  31. package/src/ui/avatar/Avatar.stx +0 -77
  32. package/src/ui/avatar/index.ts +0 -12
  33. package/src/ui/badge/Badge.stx +0 -62
  34. package/src/ui/badge/index.ts +0 -10
  35. package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
  36. package/src/ui/breadcrumb/index.ts +0 -15
  37. package/src/ui/button/Button.stx +0 -74
  38. package/src/ui/button/README.md +0 -80
  39. package/src/ui/button/index.ts +0 -85
  40. package/src/ui/calendar/Calendar.stx +0 -189
  41. package/src/ui/calendar/README.md +0 -254
  42. package/src/ui/calendar/index.ts +0 -14
  43. package/src/ui/card/Card.stx +0 -68
  44. package/src/ui/card/index.ts +0 -13
  45. package/src/ui/checkbox/Checkbox.stx +0 -86
  46. package/src/ui/checkbox/index.ts +0 -16
  47. package/src/ui/combobox/Combobox.stx +0 -96
  48. package/src/ui/combobox/ComboboxButton.stx +0 -25
  49. package/src/ui/combobox/ComboboxInput.stx +0 -32
  50. package/src/ui/combobox/ComboboxOption.stx +0 -49
  51. package/src/ui/combobox/ComboboxOptions.stx +0 -16
  52. package/src/ui/combobox/README.md +0 -150
  53. package/src/ui/combobox/index.ts +0 -40
  54. package/src/ui/command-palette/CommandPalette.stx +0 -81
  55. package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
  56. package/src/ui/command-palette/README.md +0 -42
  57. package/src/ui/command-palette/index.ts +0 -15
  58. package/src/ui/dialog/Dialog.stx +0 -41
  59. package/src/ui/dialog/DialogBackdrop.stx +0 -9
  60. package/src/ui/dialog/DialogDescription.stx +0 -11
  61. package/src/ui/dialog/DialogPanel.stx +0 -16
  62. package/src/ui/dialog/DialogTitle.stx +0 -11
  63. package/src/ui/dialog/README.md +0 -121
  64. package/src/ui/dialog/index.ts +0 -32
  65. package/src/ui/drawer/Drawer.stx +0 -120
  66. package/src/ui/drawer/README.md +0 -90
  67. package/src/ui/drawer/index.ts +0 -9
  68. package/src/ui/dropdown/Dropdown.stx +0 -82
  69. package/src/ui/dropdown/DropdownButton.stx +0 -29
  70. package/src/ui/dropdown/DropdownItem.stx +0 -35
  71. package/src/ui/dropdown/DropdownItems.stx +0 -18
  72. package/src/ui/dropdown/README.md +0 -114
  73. package/src/ui/dropdown/index.ts +0 -29
  74. package/src/ui/form/Form.stx +0 -95
  75. package/src/ui/form/index.ts +0 -67
  76. package/src/ui/heatmap/Heatmap.stx +0 -294
  77. package/src/ui/image/Image.stx +0 -165
  78. package/src/ui/image/README.md +0 -164
  79. package/src/ui/image/index.ts +0 -22
  80. package/src/ui/input/EmailInput.stx +0 -30
  81. package/src/ui/input/NumberInput.stx +0 -126
  82. package/src/ui/input/PasswordInput.stx +0 -143
  83. package/src/ui/input/SearchInput.stx +0 -44
  84. package/src/ui/input/TextInput.stx +0 -132
  85. package/src/ui/input/index.ts +0 -180
  86. package/src/ui/listbox/Listbox.stx +0 -101
  87. package/src/ui/listbox/ListboxButton.stx +0 -28
  88. package/src/ui/listbox/ListboxLabel.stx +0 -11
  89. package/src/ui/listbox/ListboxOption.stx +0 -49
  90. package/src/ui/listbox/ListboxOptions.stx +0 -16
  91. package/src/ui/listbox/README.md +0 -135
  92. package/src/ui/listbox/index.ts +0 -39
  93. package/src/ui/navigator/Navigator.stx +0 -113
  94. package/src/ui/navigator/README.md +0 -352
  95. package/src/ui/navigator/index.ts +0 -20
  96. package/src/ui/notification/Notification.stx +0 -117
  97. package/src/ui/notification/README.md +0 -114
  98. package/src/ui/notification/index.ts +0 -12
  99. package/src/ui/pagination/Pagination.stx +0 -150
  100. package/src/ui/pagination/index.ts +0 -11
  101. package/src/ui/payment/Checkout.stx +0 -31
  102. package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
  103. package/src/ui/payment/PaymentMethods.stx +0 -104
  104. package/src/ui/payment/README.md +0 -345
  105. package/src/ui/payment/SubscriptionCheckout.stx +0 -323
  106. package/src/ui/payment/index.ts +0 -59
  107. package/src/ui/popover/Popover.stx +0 -60
  108. package/src/ui/popover/PopoverButton.stx +0 -29
  109. package/src/ui/popover/PopoverPanel.stx +0 -23
  110. package/src/ui/popover/README.md +0 -91
  111. package/src/ui/popover/index.ts +0 -22
  112. package/src/ui/portal/Portal.stx +0 -57
  113. package/src/ui/portal/index.ts +0 -6
  114. package/src/ui/progress/Progress.stx +0 -131
  115. package/src/ui/progress/index.ts +0 -12
  116. package/src/ui/radio/Radio.stx +0 -77
  117. package/src/ui/radio/index.ts +0 -15
  118. package/src/ui/radio-group/README.md +0 -139
  119. package/src/ui/radio-group/RadioGroup.stx +0 -60
  120. package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
  121. package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
  122. package/src/ui/radio-group/RadioGroupOption.stx +0 -58
  123. package/src/ui/radio-group/index.ts +0 -30
  124. package/src/ui/select/README.md +0 -180
  125. package/src/ui/select/Select.stx +0 -82
  126. package/src/ui/select/index.ts +0 -21
  127. package/src/ui/sidebar/Sidebar.stx +0 -476
  128. package/src/ui/sidebar/SidebarFooter.stx +0 -71
  129. package/src/ui/sidebar/SidebarHeader.stx +0 -103
  130. package/src/ui/sidebar/SidebarItem.stx +0 -86
  131. package/src/ui/sidebar/SidebarSection.stx +0 -83
  132. package/src/ui/sidebar/index.ts +0 -153
  133. package/src/ui/sidebar/themes.ts +0 -240
  134. package/src/ui/skeleton/Skeleton.stx +0 -48
  135. package/src/ui/skeleton/index.ts +0 -11
  136. package/src/ui/spinner/Spinner.stx +0 -109
  137. package/src/ui/spinner/index.ts +0 -9
  138. package/src/ui/stepper/README.md +0 -36
  139. package/src/ui/stepper/Stepper.stx +0 -17
  140. package/src/ui/stepper/StepperStep.stx +0 -57
  141. package/src/ui/stepper/index.ts +0 -19
  142. package/src/ui/storage/README.md +0 -193
  143. package/src/ui/storage/index.ts +0 -347
  144. package/src/ui/switch/README.md +0 -114
  145. package/src/ui/switch/Switch.stx +0 -72
  146. package/src/ui/switch/index.ts +0 -10
  147. package/src/ui/table/README.md +0 -39
  148. package/src/ui/table/Table.stx +0 -14
  149. package/src/ui/table/TableBody.stx +0 -10
  150. package/src/ui/table/TableCell.stx +0 -10
  151. package/src/ui/table/TableHead.stx +0 -10
  152. package/src/ui/table/TableHeader.stx +0 -20
  153. package/src/ui/table/TableRow.stx +0 -11
  154. package/src/ui/table/index.ts +0 -35
  155. package/src/ui/tabs/TabPanel.stx +0 -25
  156. package/src/ui/tabs/Tabs.stx +0 -202
  157. package/src/ui/tabs/index.ts +0 -50
  158. package/src/ui/teleport/Teleport.stx +0 -65
  159. package/src/ui/teleport/index.ts +0 -7
  160. package/src/ui/textarea/Textarea.stx +0 -108
  161. package/src/ui/textarea/index.ts +0 -20
  162. package/src/ui/tooltip/Tooltip.stx +0 -71
  163. package/src/ui/tooltip/index.ts +0 -10
  164. package/src/ui/transition/README.md +0 -30
  165. package/src/ui/transition/Transition.stx +0 -102
  166. package/src/ui/transition/index.ts +0 -25
  167. package/src/ui/video/README.md +0 -47
  168. package/src/ui/video/Video.stx +0 -45
  169. package/src/ui/video/index.ts +0 -16
  170. package/src/ui/virtual-list/VirtualList.stx +0 -67
  171. package/src/ui/virtual-list/index.ts +0 -10
  172. package/src/ui/virtual-table/VirtualTable.stx +0 -103
  173. package/src/ui/virtual-table/index.ts +0 -21
  174. package/src/utils/PROP_VALIDATION.md +0 -458
  175. package/src/utils/THEME_SYSTEM.md +0 -702
  176. package/src/utils/accessibility.ts +0 -841
  177. package/src/utils/animation/easing.ts +0 -368
  178. package/src/utils/animation/helpers.ts +0 -223
  179. package/src/utils/animation/index.ts +0 -5
  180. package/src/utils/animation/keyframes.ts +0 -230
  181. package/src/utils/error-handling.ts +0 -475
  182. package/src/utils/highlighter.ts +0 -93
  183. package/src/utils/i18n.ts +0 -634
  184. package/src/utils/keep-alive.ts +0 -819
  185. package/src/utils/lazy-loading.ts +0 -530
  186. package/src/utils/prop-validation.ts +0 -478
  187. 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
- }