@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.
Files changed (188) hide show
  1. package/dist/{Sidebar-yb115a2w.stx → Sidebar-4hn7vw4n.stx} +18 -0
  2. package/dist/index.js +5 -5
  3. package/package.json +5 -6
  4. package/src/components/CodeBlock.stx +0 -54
  5. package/src/components/Footer.stx +0 -66
  6. package/src/components/Hero.stx +0 -25
  7. package/src/components/Installation.stx +0 -37
  8. package/src/components/index.ts +0 -4
  9. package/src/composables/index.ts +0 -4
  10. package/src/composables/useCopyCode.ts +0 -73
  11. package/src/composables/useDarkMode.ts +0 -100
  12. package/src/composables/useSEO.ts +0 -230
  13. package/src/composables/utilities/index.ts +0 -7
  14. package/src/composables/utilities/useClickOutside.ts +0 -72
  15. package/src/composables/utilities/useFocusTrap.ts +0 -159
  16. package/src/composables/utilities/useKeyboard.ts +0 -124
  17. package/src/composables/utilities/useLocalStorage.ts +0 -132
  18. package/src/composables/utilities/useMediaQuery.ts +0 -118
  19. package/src/index.ts +0 -99
  20. package/src/stx.d.ts +0 -4
  21. package/src/ui/accordion/Accordion.stx +0 -146
  22. package/src/ui/accordion/AccordionItem.stx +0 -37
  23. package/src/ui/accordion/index.ts +0 -47
  24. package/src/ui/audio/Audio.stx +0 -50
  25. package/src/ui/audio/README.md +0 -41
  26. package/src/ui/audio/index.ts +0 -13
  27. package/src/ui/auth/Login.stx +0 -161
  28. package/src/ui/auth/README.md +0 -142
  29. package/src/ui/auth/Signup.stx +0 -159
  30. package/src/ui/auth/TwoFactorChallenge.stx +0 -111
  31. package/src/ui/auth/index.ts +0 -32
  32. package/src/ui/avatar/Avatar.stx +0 -77
  33. package/src/ui/avatar/index.ts +0 -12
  34. package/src/ui/badge/Badge.stx +0 -62
  35. package/src/ui/badge/index.ts +0 -10
  36. package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
  37. package/src/ui/breadcrumb/index.ts +0 -15
  38. package/src/ui/button/Button.stx +0 -74
  39. package/src/ui/button/README.md +0 -80
  40. package/src/ui/button/index.ts +0 -85
  41. package/src/ui/calendar/Calendar.stx +0 -189
  42. package/src/ui/calendar/README.md +0 -254
  43. package/src/ui/calendar/index.ts +0 -14
  44. package/src/ui/card/Card.stx +0 -68
  45. package/src/ui/card/index.ts +0 -13
  46. package/src/ui/checkbox/Checkbox.stx +0 -86
  47. package/src/ui/checkbox/index.ts +0 -16
  48. package/src/ui/combobox/Combobox.stx +0 -96
  49. package/src/ui/combobox/ComboboxButton.stx +0 -25
  50. package/src/ui/combobox/ComboboxInput.stx +0 -32
  51. package/src/ui/combobox/ComboboxOption.stx +0 -49
  52. package/src/ui/combobox/ComboboxOptions.stx +0 -16
  53. package/src/ui/combobox/README.md +0 -150
  54. package/src/ui/combobox/index.ts +0 -40
  55. package/src/ui/command-palette/CommandPalette.stx +0 -81
  56. package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
  57. package/src/ui/command-palette/README.md +0 -42
  58. package/src/ui/command-palette/index.ts +0 -15
  59. package/src/ui/dialog/Dialog.stx +0 -41
  60. package/src/ui/dialog/DialogBackdrop.stx +0 -9
  61. package/src/ui/dialog/DialogDescription.stx +0 -11
  62. package/src/ui/dialog/DialogPanel.stx +0 -16
  63. package/src/ui/dialog/DialogTitle.stx +0 -11
  64. package/src/ui/dialog/README.md +0 -121
  65. package/src/ui/dialog/index.ts +0 -32
  66. package/src/ui/drawer/Drawer.stx +0 -120
  67. package/src/ui/drawer/README.md +0 -90
  68. package/src/ui/drawer/index.ts +0 -9
  69. package/src/ui/dropdown/Dropdown.stx +0 -82
  70. package/src/ui/dropdown/DropdownButton.stx +0 -29
  71. package/src/ui/dropdown/DropdownItem.stx +0 -35
  72. package/src/ui/dropdown/DropdownItems.stx +0 -18
  73. package/src/ui/dropdown/README.md +0 -114
  74. package/src/ui/dropdown/index.ts +0 -29
  75. package/src/ui/form/Form.stx +0 -95
  76. package/src/ui/form/index.ts +0 -67
  77. package/src/ui/heatmap/Heatmap.stx +0 -294
  78. package/src/ui/image/Image.stx +0 -165
  79. package/src/ui/image/README.md +0 -164
  80. package/src/ui/image/index.ts +0 -22
  81. package/src/ui/input/EmailInput.stx +0 -30
  82. package/src/ui/input/NumberInput.stx +0 -126
  83. package/src/ui/input/PasswordInput.stx +0 -143
  84. package/src/ui/input/SearchInput.stx +0 -44
  85. package/src/ui/input/TextInput.stx +0 -132
  86. package/src/ui/input/index.ts +0 -180
  87. package/src/ui/listbox/Listbox.stx +0 -101
  88. package/src/ui/listbox/ListboxButton.stx +0 -28
  89. package/src/ui/listbox/ListboxLabel.stx +0 -11
  90. package/src/ui/listbox/ListboxOption.stx +0 -49
  91. package/src/ui/listbox/ListboxOptions.stx +0 -16
  92. package/src/ui/listbox/README.md +0 -135
  93. package/src/ui/listbox/index.ts +0 -39
  94. package/src/ui/navigator/Navigator.stx +0 -113
  95. package/src/ui/navigator/README.md +0 -352
  96. package/src/ui/navigator/index.ts +0 -20
  97. package/src/ui/notification/Notification.stx +0 -117
  98. package/src/ui/notification/README.md +0 -114
  99. package/src/ui/notification/index.ts +0 -12
  100. package/src/ui/pagination/Pagination.stx +0 -150
  101. package/src/ui/pagination/index.ts +0 -11
  102. package/src/ui/payment/Checkout.stx +0 -31
  103. package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
  104. package/src/ui/payment/PaymentMethods.stx +0 -104
  105. package/src/ui/payment/README.md +0 -345
  106. package/src/ui/payment/SubscriptionCheckout.stx +0 -323
  107. package/src/ui/payment/index.ts +0 -59
  108. package/src/ui/popover/Popover.stx +0 -60
  109. package/src/ui/popover/PopoverButton.stx +0 -29
  110. package/src/ui/popover/PopoverPanel.stx +0 -23
  111. package/src/ui/popover/README.md +0 -91
  112. package/src/ui/popover/index.ts +0 -22
  113. package/src/ui/portal/Portal.stx +0 -57
  114. package/src/ui/portal/index.ts +0 -6
  115. package/src/ui/progress/Progress.stx +0 -131
  116. package/src/ui/progress/index.ts +0 -12
  117. package/src/ui/radio/Radio.stx +0 -77
  118. package/src/ui/radio/index.ts +0 -15
  119. package/src/ui/radio-group/README.md +0 -139
  120. package/src/ui/radio-group/RadioGroup.stx +0 -60
  121. package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
  122. package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
  123. package/src/ui/radio-group/RadioGroupOption.stx +0 -58
  124. package/src/ui/radio-group/index.ts +0 -30
  125. package/src/ui/select/README.md +0 -180
  126. package/src/ui/select/Select.stx +0 -82
  127. package/src/ui/select/index.ts +0 -21
  128. package/src/ui/sidebar/Sidebar.stx +0 -458
  129. package/src/ui/sidebar/SidebarFooter.stx +0 -71
  130. package/src/ui/sidebar/SidebarHeader.stx +0 -103
  131. package/src/ui/sidebar/SidebarItem.stx +0 -86
  132. package/src/ui/sidebar/SidebarSection.stx +0 -83
  133. package/src/ui/sidebar/index.ts +0 -153
  134. package/src/ui/sidebar/themes.ts +0 -240
  135. package/src/ui/skeleton/Skeleton.stx +0 -48
  136. package/src/ui/skeleton/index.ts +0 -11
  137. package/src/ui/spinner/Spinner.stx +0 -109
  138. package/src/ui/spinner/index.ts +0 -9
  139. package/src/ui/stepper/README.md +0 -36
  140. package/src/ui/stepper/Stepper.stx +0 -17
  141. package/src/ui/stepper/StepperStep.stx +0 -57
  142. package/src/ui/stepper/index.ts +0 -19
  143. package/src/ui/storage/README.md +0 -193
  144. package/src/ui/storage/index.ts +0 -347
  145. package/src/ui/switch/README.md +0 -114
  146. package/src/ui/switch/Switch.stx +0 -72
  147. package/src/ui/switch/index.ts +0 -10
  148. package/src/ui/table/README.md +0 -39
  149. package/src/ui/table/Table.stx +0 -14
  150. package/src/ui/table/TableBody.stx +0 -10
  151. package/src/ui/table/TableCell.stx +0 -10
  152. package/src/ui/table/TableHead.stx +0 -10
  153. package/src/ui/table/TableHeader.stx +0 -20
  154. package/src/ui/table/TableRow.stx +0 -11
  155. package/src/ui/table/index.ts +0 -35
  156. package/src/ui/tabs/TabPanel.stx +0 -25
  157. package/src/ui/tabs/Tabs.stx +0 -202
  158. package/src/ui/tabs/index.ts +0 -50
  159. package/src/ui/teleport/Teleport.stx +0 -65
  160. package/src/ui/teleport/index.ts +0 -7
  161. package/src/ui/textarea/Textarea.stx +0 -108
  162. package/src/ui/textarea/index.ts +0 -20
  163. package/src/ui/tooltip/Tooltip.stx +0 -71
  164. package/src/ui/tooltip/index.ts +0 -10
  165. package/src/ui/transition/README.md +0 -30
  166. package/src/ui/transition/Transition.stx +0 -102
  167. package/src/ui/transition/index.ts +0 -25
  168. package/src/ui/video/README.md +0 -47
  169. package/src/ui/video/Video.stx +0 -45
  170. package/src/ui/video/index.ts +0 -16
  171. package/src/ui/virtual-list/VirtualList.stx +0 -67
  172. package/src/ui/virtual-list/index.ts +0 -10
  173. package/src/ui/virtual-table/VirtualTable.stx +0 -103
  174. package/src/ui/virtual-table/index.ts +0 -21
  175. package/src/utils/PROP_VALIDATION.md +0 -458
  176. package/src/utils/THEME_SYSTEM.md +0 -702
  177. package/src/utils/accessibility.ts +0 -841
  178. package/src/utils/animation/easing.ts +0 -368
  179. package/src/utils/animation/helpers.ts +0 -223
  180. package/src/utils/animation/index.ts +0 -5
  181. package/src/utils/animation/keyframes.ts +0 -230
  182. package/src/utils/error-handling.ts +0 -475
  183. package/src/utils/highlighter.ts +0 -93
  184. package/src/utils/i18n.ts +0 -634
  185. package/src/utils/keep-alive.ts +0 -819
  186. package/src/utils/lazy-loading.ts +0 -530
  187. package/src/utils/prop-validation.ts +0 -478
  188. package/src/utils/theme.ts +0 -626
@@ -1,230 +0,0 @@
1
- /**
2
- * Keyframe animation utilities using Web Animations API
3
- *
4
- * @example
5
- * ```ts
6
- * import { animate, KeyframeAnimations } from '@stacksjs/components'
7
- *
8
- * // Use predefined animation
9
- * animate(element, 'fadeIn', { duration: 500 })
10
- *
11
- * // Custom keyframes
12
- * createKeyframeAnimation(element, [
13
- * { offset: 0, opacity: 0, transform: 'scale(0.5)' },
14
- * { offset: 1, opacity: 1, transform: 'scale(1)' }
15
- * ], { duration: 300 })
16
- * ```
17
- */
18
-
19
- /**
20
- * Single keyframe definition
21
- */
22
- export interface Keyframe {
23
- /** Keyframe position (0 to 1) */
24
- offset: number
25
- /** CSS properties to animate */
26
- [property: string]: any
27
- }
28
-
29
- /**
30
- * Animation configuration options
31
- */
32
- export interface AnimationOptions {
33
- /** Animation duration in milliseconds */
34
- duration?: number
35
- /** Easing function, CSS easing string */
36
- easing?: string
37
- /** Delay before animation starts in milliseconds */
38
- delay?: number
39
- /** Number of iterations (Infinity for infinite) */
40
- iterations?: number
41
- /** Animation direction */
42
- direction?: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse'
43
- /** Fill mode */
44
- fill?: 'none' | 'forwards' | 'backwards' | 'both'
45
- }
46
-
47
- /**
48
- * Create a keyframe animation using Web Animations API
49
- *
50
- * @param element - Target HTML element
51
- * @param keyframes - Array of keyframe definitions
52
- * @param options - Animation options
53
- * @returns Animation instance or null if not supported
54
- */
55
- export function createKeyframeAnimation(
56
- element: HTMLElement,
57
- keyframes: readonly Keyframe[] | Keyframe[],
58
- options: AnimationOptions = {},
59
- ): Animation | null {
60
- if (typeof element.animate !== 'function') {
61
- console.warn('Web Animations API not supported')
62
- return null
63
- }
64
-
65
- const {
66
- duration = 1000,
67
- easing = 'ease',
68
- delay = 0,
69
- iterations = 1,
70
- direction = 'normal',
71
- fill = 'both',
72
- } = options
73
-
74
- return element.animate([...keyframes], {
75
- duration,
76
- easing,
77
- delay,
78
- iterations,
79
- direction,
80
- fill,
81
- })
82
- }
83
-
84
- /**
85
- * Predefined keyframe animation presets
86
- *
87
- * Available animations:
88
- * - fadeIn/fadeOut
89
- * - slideInUp/Down/Left/Right, slideOutUp/Down/Left/Right
90
- * - zoomIn/Out
91
- * - rotate, bounce, pulse, shake, swing
92
- * - flip, rubberBand, heartbeat
93
- */
94
- export const KeyframeAnimations = {
95
- fadeIn: [
96
- { offset: 0, opacity: 0 },
97
- { offset: 1, opacity: 1 },
98
- ],
99
- fadeOut: [
100
- { offset: 0, opacity: 1 },
101
- { offset: 1, opacity: 0 },
102
- ],
103
- slideInUp: [
104
- { offset: 0, transform: 'translateY(100%)', opacity: 0 },
105
- { offset: 1, transform: 'translateY(0)', opacity: 1 },
106
- ],
107
- slideInDown: [
108
- { offset: 0, transform: 'translateY(-100%)', opacity: 0 },
109
- { offset: 1, transform: 'translateY(0)', opacity: 1 },
110
- ],
111
- slideInLeft: [
112
- { offset: 0, transform: 'translateX(-100%)', opacity: 0 },
113
- { offset: 1, transform: 'translateX(0)', opacity: 1 },
114
- ],
115
- slideInRight: [
116
- { offset: 0, transform: 'translateX(100%)', opacity: 0 },
117
- { offset: 1, transform: 'translateX(0)', opacity: 1 },
118
- ],
119
- slideOutUp: [
120
- { offset: 0, transform: 'translateY(0)', opacity: 1 },
121
- { offset: 1, transform: 'translateY(-100%)', opacity: 0 },
122
- ],
123
- slideOutDown: [
124
- { offset: 0, transform: 'translateY(0)', opacity: 1 },
125
- { offset: 1, transform: 'translateY(100%)', opacity: 0 },
126
- ],
127
- slideOutLeft: [
128
- { offset: 0, transform: 'translateX(0)', opacity: 1 },
129
- { offset: 1, transform: 'translateX(-100%)', opacity: 0 },
130
- ],
131
- slideOutRight: [
132
- { offset: 0, transform: 'translateX(0)', opacity: 1 },
133
- { offset: 1, transform: 'translateX(100%)', opacity: 0 },
134
- ],
135
- zoomIn: [
136
- { offset: 0, transform: 'scale(0)', opacity: 0 },
137
- { offset: 0.5, opacity: 1 },
138
- { offset: 1, transform: 'scale(1)' },
139
- ],
140
- zoomOut: [
141
- { offset: 0, transform: 'scale(1)', opacity: 1 },
142
- { offset: 0.5, opacity: 1 },
143
- { offset: 1, transform: 'scale(0)', opacity: 0 },
144
- ],
145
- rotate: [
146
- { offset: 0, transform: 'rotate(0deg)' },
147
- { offset: 1, transform: 'rotate(360deg)' },
148
- ],
149
- bounce: [
150
- { offset: 0, transform: 'translateY(0)' },
151
- { offset: 0.2, transform: 'translateY(0)' },
152
- { offset: 0.4, transform: 'translateY(-30px)' },
153
- { offset: 0.43, transform: 'translateY(-30px)' },
154
- { offset: 0.53, transform: 'translateY(0)' },
155
- { offset: 0.7, transform: 'translateY(-15px)' },
156
- { offset: 0.8, transform: 'translateY(0)' },
157
- { offset: 0.9, transform: 'translateY(-4px)' },
158
- { offset: 1, transform: 'translateY(0)' },
159
- ],
160
- pulse: [
161
- { offset: 0, transform: 'scale(1)' },
162
- { offset: 0.5, transform: 'scale(1.05)' },
163
- { offset: 1, transform: 'scale(1)' },
164
- ],
165
- shake: [
166
- { offset: 0, transform: 'translateX(0)' },
167
- { offset: 0.1, transform: 'translateX(-10px)' },
168
- { offset: 0.2, transform: 'translateX(10px)' },
169
- { offset: 0.3, transform: 'translateX(-10px)' },
170
- { offset: 0.4, transform: 'translateX(10px)' },
171
- { offset: 0.5, transform: 'translateX(-10px)' },
172
- { offset: 0.6, transform: 'translateX(10px)' },
173
- { offset: 0.7, transform: 'translateX(-10px)' },
174
- { offset: 0.8, transform: 'translateX(10px)' },
175
- { offset: 0.9, transform: 'translateX(-10px)' },
176
- { offset: 1, transform: 'translateX(0)' },
177
- ],
178
- swing: [
179
- { offset: 0, transform: 'rotate(0deg)' },
180
- { offset: 0.2, transform: 'rotate(15deg)' },
181
- { offset: 0.4, transform: 'rotate(-10deg)' },
182
- { offset: 0.6, transform: 'rotate(5deg)' },
183
- { offset: 0.8, transform: 'rotate(-5deg)' },
184
- { offset: 1, transform: 'rotate(0deg)' },
185
- ],
186
- flip: [
187
- { offset: 0, transform: 'perspective(400px) rotateY(0)', animationTimingFunction: 'ease-out' },
188
- { offset: 0.4, transform: 'perspective(400px) translateZ(150px) rotateY(170deg)', animationTimingFunction: 'ease-out' },
189
- { offset: 0.5, transform: 'perspective(400px) translateZ(150px) rotateY(190deg)', animationTimingFunction: 'ease-in' },
190
- { offset: 0.8, transform: 'perspective(400px) rotateY(360deg)', animationTimingFunction: 'ease-in' },
191
- { offset: 1, transform: 'perspective(400px)', animationTimingFunction: 'ease-in' },
192
- ],
193
- rubberBand: [
194
- { offset: 0, transform: 'scale(1)' },
195
- { offset: 0.3, transform: 'scaleX(1.25) scaleY(0.75)' },
196
- { offset: 0.4, transform: 'scaleX(0.75) scaleY(1.25)' },
197
- { offset: 0.5, transform: 'scaleX(1.15) scaleY(0.85)' },
198
- { offset: 0.65, transform: 'scaleX(0.95) scaleY(1.05)' },
199
- { offset: 0.75, transform: 'scaleX(1.05) scaleY(0.95)' },
200
- { offset: 1, transform: 'scale(1)' },
201
- ],
202
- heartbeat: [
203
- { offset: 0, transform: 'scale(1)' },
204
- { offset: 0.14, transform: 'scale(1.3)' },
205
- { offset: 0.28, transform: 'scale(1)' },
206
- { offset: 0.42, transform: 'scale(1.3)' },
207
- { offset: 0.7, transform: 'scale(1)' },
208
- ],
209
- } as const
210
-
211
- /**
212
- * Type for predefined animation names
213
- */
214
- export type KeyframeAnimationName = keyof typeof KeyframeAnimations
215
-
216
- /**
217
- * Animate an element with a predefined animation
218
- *
219
- * @param element - Target HTML element
220
- * @param animationName - Name of predefined animation
221
- * @param options - Animation options
222
- * @returns Animation instance or null if not supported
223
- */
224
- export function animate(
225
- element: HTMLElement,
226
- animationName: KeyframeAnimationName,
227
- options: AnimationOptions = {},
228
- ): Animation | null {
229
- return createKeyframeAnimation(element, KeyframeAnimations[animationName], options)
230
- }
@@ -1,475 +0,0 @@
1
- /**
2
- * Error handling utilities for @stacksjs/components
3
- *
4
- * Provides clear, actionable error messages with links to documentation
5
- *
6
- * @example
7
- * ```ts
8
- * import { ComponentError, handleError } from '@stacksjs/components'
9
- *
10
- * throw new ComponentError('Button', 'Invalid variant provided', {
11
- * received: 'invalid',
12
- * expected: ['primary', 'secondary', 'outline']
13
- * })
14
- * ```
15
- */
16
- import process from 'node:process'
17
-
18
- /**
19
- * Base URL for documentation
20
- */
21
- const DOCS_BASE_URL = 'https://stacksjs.com/components'
22
-
23
- /**
24
- * Error context information
25
- */
26
- export interface ErrorContext {
27
- /** The component name where the error occurred */
28
- component?: string
29
- /** The prop name that caused the error */
30
- prop?: string
31
- /** The value that was received */
32
- received?: any
33
- /** The expected value(s) */
34
- expected?: any
35
- /** Additional details about the error */
36
- details?: string
37
- /** Link to relevant documentation */
38
- docsUrl?: string
39
- }
40
-
41
- /**
42
- * Custom error class for component errors
43
- */
44
- export class ComponentError extends Error {
45
- component: string
46
- context: ErrorContext
47
-
48
- constructor(component: string, message: string, context: ErrorContext = {}) {
49
- const fullMessage = ComponentError.formatMessage(component, message, context)
50
- super(fullMessage)
51
-
52
- this.name = 'ComponentError'
53
- this.component = component
54
- this.context = { component, ...context }
55
-
56
- // Maintains proper stack trace for where error was thrown (only available on V8)
57
- if (Error.captureStackTrace) {
58
- Error.captureStackTrace(this, ComponentError)
59
- }
60
- }
61
-
62
- /**
63
- * Format error message with context
64
- */
65
- static formatMessage(component: string, message: string, context: ErrorContext): string {
66
- let formatted = `[${component}] ${message}`
67
-
68
- if (context.prop) {
69
- formatted += `\n Prop: "${context.prop}"`
70
- }
71
-
72
- if (context.received !== undefined) {
73
- formatted += `\n Received: ${JSON.stringify(context.received)}`
74
- }
75
-
76
- if (context.expected !== undefined) {
77
- const expected = Array.isArray(context.expected)
78
- ? context.expected.map(v => JSON.stringify(v)).join(', ')
79
- : JSON.stringify(context.expected)
80
- formatted += `\n Expected: ${expected}`
81
- }
82
-
83
- if (context.details) {
84
- formatted += `\n Details: ${context.details}`
85
- }
86
-
87
- if (context.docsUrl) {
88
- formatted += `\n Documentation: ${context.docsUrl}`
89
- }
90
- else if (component) {
91
- formatted += `\n Documentation: ${DOCS_BASE_URL}/${component.toLowerCase()}`
92
- }
93
-
94
- return formatted
95
- }
96
-
97
- /**
98
- * Get error as plain object
99
- */
100
- toJSON(): { name: string, message: string, component: string, context: ErrorContext | undefined, stack: string | undefined } {
101
- return {
102
- name: this.name,
103
- message: this.message,
104
- component: this.component,
105
- context: this.context,
106
- stack: this.stack,
107
- }
108
- }
109
- }
110
-
111
- /**
112
- * Error types for common scenarios
113
- */
114
- export const ErrorTypes = {
115
- /**
116
- * Invalid prop value error
117
- */
118
- invalidProp(component: string, prop: string, received: any, expected: any): ComponentError {
119
- return new ComponentError(
120
- component,
121
- `Invalid prop value for "${prop}"`,
122
- {
123
- prop,
124
- received,
125
- expected,
126
- details: Array.isArray(expected)
127
- ? `The "${prop}" prop must be one of the allowed values`
128
- : `The "${prop}" prop does not match the expected type`,
129
- },
130
- )
131
- },
132
-
133
- /**
134
- * Required prop missing error
135
- */
136
- requiredProp(component: string, prop: string): ComponentError {
137
- return new ComponentError(
138
- component,
139
- `Required prop "${prop}" is missing`,
140
- {
141
- prop,
142
- details: `The "${prop}" prop is required for ${component} to function correctly`,
143
- },
144
- )
145
- },
146
-
147
- /**
148
- * Invalid prop type error
149
- */
150
- invalidType(component: string, prop: string, received: any, expected: string): ComponentError {
151
- return new ComponentError(
152
- component,
153
- `Invalid type for prop "${prop}"`,
154
- {
155
- prop,
156
- received: typeof received,
157
- expected,
158
- details: `Expected "${prop}" to be of type ${expected}, but received ${typeof received}`,
159
- },
160
- )
161
- },
162
-
163
- /**
164
- * Component not found error
165
- */
166
- componentNotFound(componentName: string): ComponentError {
167
- return new ComponentError(
168
- 'ComponentLoader',
169
- `Component "${componentName}" not found`,
170
- {
171
- component: componentName,
172
- details: 'Make sure the component is properly imported and registered',
173
- },
174
- )
175
- },
176
-
177
- /**
178
- * Invalid children error
179
- */
180
- invalidChildren(component: string, received: any, expected: string): ComponentError {
181
- return new ComponentError(
182
- component,
183
- 'Invalid children',
184
- {
185
- received: typeof received,
186
- expected,
187
- details: `${component} expects ${expected} as children`,
188
- },
189
- )
190
- },
191
-
192
- /**
193
- * State update error
194
- */
195
- stateUpdate(component: string, stateName: string, error: Error): ComponentError {
196
- return new ComponentError(
197
- component,
198
- `Failed to update state "${stateName}"`,
199
- {
200
- details: error.message,
201
- },
202
- )
203
- },
204
-
205
- /**
206
- * Event handler error
207
- */
208
- eventHandler(component: string, eventName: string, error: Error): ComponentError {
209
- return new ComponentError(
210
- component,
211
- `Error in "${eventName}" event handler`,
212
- {
213
- details: error.message,
214
- },
215
- )
216
- },
217
-
218
- /**
219
- * Render error
220
- */
221
- render(component: string, error: Error): ComponentError {
222
- return new ComponentError(
223
- component,
224
- 'Failed to render component',
225
- {
226
- details: error.message,
227
- },
228
- )
229
- },
230
- }
231
-
232
- /**
233
- * Global error handler
234
- */
235
- let errorHandler: ((_error: Error) => void) | null = null
236
-
237
- /**
238
- * Set global error handler
239
- *
240
- * @param handler - Error handler function
241
- *
242
- * @example
243
- * ```ts
244
- * setErrorHandler((error) => {
245
- * console.error('Component error:', error)
246
- * // Send to error tracking service
247
- * })
248
- * ```
249
- */
250
- export function setErrorHandler(handler: (error: Error) => void): void {
251
- errorHandler = handler
252
- }
253
-
254
- /**
255
- * Clear the global error handler (useful for testing)
256
- */
257
- export function clearErrorHandler(): void {
258
- errorHandler = null
259
- }
260
-
261
- /**
262
- * Handle an error
263
- *
264
- * @param error - Error to handle
265
- * @param options - Handling options
266
- * @param options.throwError - Whether to re-throw the error after handling
267
- * @param options.logError - Whether to log the error to console
268
- *
269
- * @example
270
- * ```ts
271
- * try {
272
- * // Component code
273
- * }
274
- catch (error) {
275
- * handleError(error, { throwError: false })
276
- * }
277
- * ```
278
- */
279
- export function handleError(
280
- error: Error,
281
- options: {
282
- /** Whether to re-throw the error after handling */
283
- throwError?: boolean
284
- /** Whether to log the error to console */
285
- logError?: boolean
286
- } = {},
287
- ): void {
288
- const { throwError = true, logError = true } = options
289
-
290
- // Log to console in development
291
- if (logError && process.env.NODE_ENV !== 'production') {
292
- console.error('Component Error:', error)
293
-
294
- if (error instanceof ComponentError) {
295
- console.group('Error Details')
296
- // eslint-disable-next-line no-console
297
- console.log('Component:', error.component)
298
- // eslint-disable-next-line no-console
299
- console.log('Context:', error.context)
300
- console.groupEnd()
301
- }
302
- }
303
-
304
- // Call global error handler if set
305
- if (errorHandler) {
306
- try {
307
- errorHandler(error)
308
- }
309
- catch (handlerError) {
310
- console.error('Error in error handler:', handlerError)
311
- }
312
- }
313
-
314
- // Re-throw if requested
315
- if (throwError) {
316
- throw error
317
- }
318
- }
319
-
320
- /**
321
- * Create a wrapped function that catches and handles errors
322
- *
323
- * @param fn - Function to wrap
324
- * @param component - Component name for error context
325
- * @param eventName - Event name for error context
326
- * @returns Wrapped function
327
- *
328
- * @example
329
- * ```ts
330
- * const safeHandler = wrapErrorHandler(
331
- * () => { /* ... * / },
332
- * 'Button',
333
- * 'onClick'
334
- * )
335
- * ```
336
- */
337
- export function wrapErrorHandler<T extends (...args: any[]) => any>(
338
- fn: T,
339
- component: string,
340
- eventName: string,
341
- ): T {
342
- return ((...args: any[]) => {
343
- try {
344
- const result = fn(...args)
345
-
346
- // Handle async functions
347
- if (result instanceof Promise) {
348
- return result.catch((error) => {
349
- const wrappedError = ErrorTypes.eventHandler(component, eventName, error)
350
- handleError(wrappedError, { throwError: false })
351
- throw wrappedError
352
- })
353
- }
354
-
355
- return result
356
- }
357
- catch (error) {
358
- const wrappedError = ErrorTypes.eventHandler(component, eventName, error as Error)
359
- handleError(wrappedError, { throwError: false })
360
- throw wrappedError
361
- }
362
- }) as T
363
- }
364
-
365
- /**
366
- * Assert a condition with a custom error
367
- *
368
- * @param condition - Condition to check
369
- * @param error - Error to throw if condition is false
370
- *
371
- * @example
372
- * ```ts
373
- * assert(
374
- * props.variant !== undefined,
375
- * ErrorTypes.requiredProp('Button', 'variant')
376
- * )
377
- * ```
378
- */
379
- export function assert(condition: any, error: Error): asserts condition {
380
- if (!condition) {
381
- throw error
382
- }
383
- }
384
-
385
- /**
386
- * Warning message handler
387
- */
388
- export function warn(component: string, message: string, context: Partial<ErrorContext> = {}): void {
389
- if (process.env.NODE_ENV === 'production') {
390
- return
391
- }
392
-
393
- const fullMessage = ComponentError.formatMessage(component, message, context as ErrorContext)
394
-
395
- console.warn(`⚠️ ${fullMessage}`)
396
- }
397
-
398
- /**
399
- * Development-only assertion
400
- */
401
- export function devAssert(condition: any, error: Error): asserts condition {
402
- if (process.env.NODE_ENV === 'production') {
403
- return
404
- }
405
-
406
- assert(condition, error)
407
- }
408
-
409
- /**
410
- * Create a debug logger for a component
411
- *
412
- * @param component - Component name
413
- * @returns Debug logger functions
414
- *
415
- * @example
416
- * ```ts
417
- * const debug = createDebugger('Button')
418
- * debug.log('Button rendered')
419
- * debug.error('Invalid prop')
420
- * ```
421
- */
422
- export function createDebugger(component: string): {
423
- log: (...args: any[]) => void
424
- warn: (message: string, context?: Partial<ErrorContext>) => void
425
- error: (message: string, context?: ErrorContext) => void
426
- } {
427
- const isDebug = typeof process !== 'undefined' && process.env.DEBUG === 'true'
428
-
429
- return {
430
- log(...args: any[]): void {
431
- if (isDebug) {
432
- console.log(`[${component}]`, ...args)
433
- }
434
- },
435
- warn(message: string, context?: Partial<ErrorContext>): void {
436
- warn(component, message, context)
437
- },
438
- error(message: string, context?: ErrorContext): void {
439
- const error = new ComponentError(component, message, context)
440
- handleError(error, { throwError: false })
441
- },
442
- }
443
- }
444
-
445
- /**
446
- * Error boundary helper for catching render errors
447
- *
448
- * @param component - Component name
449
- * @param renderFn - Render function
450
- * @param fallback - Fallback content on error
451
- * @returns Rendered content or fallback
452
- *
453
- * @example
454
- * ```ts
455
- * const content = errorBoundary(
456
- * 'MyComponent',
457
- * () => renderComponent(),
458
- * '<div>Error loading component</div>'
459
- * )
460
- * ```
461
- */
462
- export function errorBoundary<T>(
463
- component: string,
464
- renderFn: () => T,
465
- fallback: T,
466
- ): T {
467
- try {
468
- return renderFn()
469
- }
470
- catch (error) {
471
- const wrappedError = ErrorTypes.render(component, error as Error)
472
- handleError(wrappedError, { throwError: false })
473
- return fallback
474
- }
475
- }