@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,95 +0,0 @@
1
- <script server>
2
- export const action = $props.action || ''
3
- export const method = $props.method || 'POST'
4
- export const validationSchema = $props.validationSchema || {}
5
- export const className = $props.className || ''
6
-
7
- export const formClasses = `space-y-6 ${className}`.trim()
8
- export const errorClasses = `mt-1 text-sm text-red-600 dark:text-red-400`.trim()
9
- </script>
10
-
11
- <script client>
12
- const emit = defineEmits()
13
- const validationSchema = {{ validationSchema }}
14
-
15
- const errors = state({})
16
- const isSubmitting = state(false)
17
- const submitCount = state(0)
18
-
19
- function validateField(name, value, allValues) {
20
- const schema = validationSchema[name]
21
- if (!schema) return null
22
- if (schema.required && !value) {
23
- return schema.requiredMessage || `${name} is required`
24
- }
25
- if (schema.minLength && String(value).length < schema.minLength) {
26
- return schema.minLengthMessage || `${name} must be at least ${schema.minLength} characters`
27
- }
28
- if (schema.maxLength && String(value).length > schema.maxLength) {
29
- return schema.maxLengthMessage || `${name} must be at most ${schema.maxLength} characters`
30
- }
31
- if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
32
- return schema.patternMessage || `${name} is invalid`
33
- }
34
- return null
35
- }
36
-
37
- function validateForm(values) {
38
- const next = {}
39
- let valid = true
40
- for (const name of Object.keys(validationSchema)) {
41
- const err = validateField(name, values[name], values)
42
- if (err) {
43
- next[name] = err
44
- valid = false
45
- }
46
- }
47
- errors.set(next)
48
- return valid
49
- }
50
-
51
- async function onSubmit(event) {
52
- event.preventDefault()
53
- submitCount.set(submitCount() + 1)
54
-
55
- const formData = new FormData(event.target)
56
- const values = {}
57
- for (const [k, v] of formData.entries()) values[k] = v
58
-
59
- if (!validateForm(values)) {
60
- emit('error', errors())
61
- return
62
- }
63
-
64
- isSubmitting.set(true)
65
- try {
66
- emit('submit', values)
67
- }
68
- finally {
69
- isSubmitting.set(false)
70
- }
71
- }
72
-
73
- function setErrors(next) {
74
- errors.set({ ...errors(), ...next })
75
- }
76
-
77
- function reset(form) {
78
- errors.set({})
79
- isSubmitting.set(false)
80
- submitCount.set(0)
81
- if (form?.reset) form.reset()
82
- }
83
-
84
- defineExpose({ errors, isSubmitting, submitCount, setErrors, reset, validateForm })
85
- </script>
86
-
87
- <form
88
- class="{{ formClasses }}"
89
- @if(action)action="{{ action }}"@endif
90
- method="{{ method }}"
91
- @submit="onSubmit($event)"
92
- novalidate
93
- >
94
- <slot />
95
- </form>
@@ -1,67 +0,0 @@
1
- import type { PropSchema, ValidationResult } from '../../utils/prop-validation'
2
- import { createPropValidator, PropTypes } from '../../utils/prop-validation'
3
-
4
- export { default as Form } from './Form.stx'
5
-
6
- export interface FieldSchema {
7
- required?: boolean
8
- requiredMessage?: string
9
- minLength?: number
10
- minLengthMessage?: string
11
- maxLength?: number
12
- maxLengthMessage?: string
13
- pattern?: RegExp
14
- patternMessage?: string
15
- validate?: (value: any, values: Record<string, any>) => string | null | undefined
16
- }
17
-
18
- export interface ValidationSchema {
19
- [fieldName: string]: FieldSchema
20
- }
21
-
22
- export interface FormHelpers {
23
- setErrors: (errors: Record<string, string>) => void
24
- setValues: (values: Record<string, any>) => void
25
- resetForm: () => void
26
- }
27
-
28
- export interface FormProps {
29
- action?: string
30
- method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
31
- validationSchema?: ValidationSchema
32
- initialValues?: Record<string, any>
33
- validateOnChange?: boolean
34
- validateOnBlur?: boolean
35
- onSubmit?: (values: Record<string, any>, helpers: FormHelpers) => void | Promise<void>
36
- onError?: (errors: Record<string, string> | Error) => void
37
- className?: string
38
- }
39
-
40
- export interface FieldProps {
41
- name: string
42
- value: any
43
- error?: string
44
- touched?: boolean
45
- onChange: (event: Event) => void
46
- onBlur: (event: Event) => void
47
- }
48
-
49
- /**
50
- * Form prop validation schema
51
- */
52
- export const formPropSchema: PropSchema = {
53
- action: PropTypes.string,
54
- method: PropTypes.oneOf(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']),
55
- validationSchema: PropTypes.object,
56
- initialValues: PropTypes.object,
57
- validateOnChange: PropTypes.boolean,
58
- validateOnBlur: PropTypes.boolean,
59
- onSubmit: PropTypes.func,
60
- onError: PropTypes.func,
61
- className: PropTypes.string,
62
- }
63
-
64
- /**
65
- * Validate Form component props
66
- */
67
- export const validateFormProps: (props: Record<string, any>) => ValidationResult = createPropValidator('Form', formPropSchema)
@@ -1,294 +0,0 @@
1
- <script server>
2
- export const id = $props.id || `stx-heatmap-${Math.random().toString(36).slice(2, 8)}`
3
- export const mode = $props.mode || 'overlay'
4
- export const width = $props.width || null
5
- export const height = $props.height || null
6
- export const radius = $props.radius || 20
7
- export const blur = $props.blur || 15
8
- export const opacity = $props.opacity || 0.6
9
- export const showLegend = $props.showLegend !== false
10
- export const interactive = $props.interactive || false
11
- export const dataSource = $props.dataSource || 'auto'
12
- export const endpoint = $props.endpoint || ''
13
- export const colorScheme = $props.colorScheme || 'default'
14
-
15
- const colorSchemes = {
16
- default: ['#0000ff', '#00ffff', '#00ff00', '#ffff00', '#ff0000'],
17
- fire: ['#000000', '#ff0000', '#ffff00', '#ffffff'],
18
- cool: ['#000033', '#0066ff', '#00ccff', '#ffffff'],
19
- grayscale: ['#000000', '#444444', '#888888', '#cccccc', '#ffffff'],
20
- }
21
-
22
- export const colors = colorSchemes[colorScheme] || colorSchemes.default
23
- </script>
24
-
25
- <div
26
- id="{{ id }}"
27
- class="stx-heatmap stx-heatmap--{{ mode }}"
28
- data-heatmap-radius="{{ radius }}"
29
- data-heatmap-blur="{{ blur }}"
30
- data-heatmap-opacity="{{ opacity }}"
31
- data-heatmap-colors="{{ JSON.stringify(colors) }}"
32
- data-heatmap-source="{{ dataSource }}"
33
- @if(endpoint)
34
- data-heatmap-endpoint="{{ endpoint }}"
35
- @endif
36
- @if(width)
37
- style="width: {{ width }}px; {{ height ? 'height: ' + height + 'px;' : '' }}"
38
- @endif
39
- >
40
- <canvas class="stx-heatmap__canvas"></canvas>
41
-
42
- @if(showLegend)
43
- <div class="stx-heatmap__legend">
44
- <span class="stx-heatmap__legend-label">Low</span>
45
- <div class="stx-heatmap__legend-gradient" style="background: linear-gradient(to right, {{ colors.join(', ') }})"></div>
46
- <span class="stx-heatmap__legend-label">High</span>
47
- </div>
48
- @endif
49
-
50
- @if(interactive)
51
- <div class="stx-heatmap__controls">
52
- <button type="button" class="stx-heatmap__control" data-action="zoom-in" aria-label="Zoom in">+</button>
53
- <button type="button" class="stx-heatmap__control" data-action="zoom-out" aria-label="Zoom out">-</button>
54
- <button type="button" class="stx-heatmap__control" data-action="reset" aria-label="Reset view">Reset</button>
55
- </div>
56
- @endif
57
-
58
- {{ slot }}
59
- </div>
60
-
61
- <style>
62
- .stx-heatmap {
63
- position: relative;
64
- overflow: hidden;
65
- }
66
-
67
- .stx-heatmap--overlay {
68
- position: fixed;
69
- top: 0;
70
- left: 0;
71
- width: 100%;
72
- height: 100%;
73
- pointer-events: none;
74
- z-index: 9999;
75
- }
76
-
77
- .stx-heatmap--standalone {
78
- display: block;
79
- border: 1px solid #e0e0e0;
80
- border-radius: 8px;
81
- background: #f9f9f9;
82
- }
83
-
84
- .stx-heatmap--thumbnail {
85
- display: inline-block;
86
- border: 1px solid #e0e0e0;
87
- border-radius: 4px;
88
- background: #f9f9f9;
89
- }
90
-
91
- .stx-heatmap__canvas {
92
- width: 100%;
93
- height: 100%;
94
- display: block;
95
- }
96
-
97
- .stx-heatmap__legend {
98
- position: absolute;
99
- bottom: 10px;
100
- left: 50%;
101
- transform: translateX(-50%);
102
- display: flex;
103
- align-items: center;
104
- gap: 8px;
105
- padding: 6px 12px;
106
- background: rgba(255, 255, 255, 0.9);
107
- border-radius: 4px;
108
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
109
- font-size: 12px;
110
- pointer-events: auto;
111
- }
112
-
113
- .stx-heatmap__legend-gradient {
114
- width: 100px;
115
- height: 12px;
116
- border-radius: 2px;
117
- }
118
-
119
- .stx-heatmap__legend-label {
120
- color: #666;
121
- }
122
-
123
- .stx-heatmap__controls {
124
- position: absolute;
125
- top: 10px;
126
- right: 10px;
127
- display: flex;
128
- flex-direction: column;
129
- gap: 4px;
130
- pointer-events: auto;
131
- }
132
-
133
- .stx-heatmap__control {
134
- width: 32px;
135
- height: 32px;
136
- display: flex;
137
- align-items: center;
138
- justify-content: center;
139
- border: 1px solid #e0e0e0;
140
- border-radius: 4px;
141
- background: rgba(255, 255, 255, 0.9);
142
- cursor: pointer;
143
- font-size: 16px;
144
- transition: background-color 0.2s;
145
- }
146
-
147
- .stx-heatmap__control:hover {
148
- background: #f0f0f0;
149
- }
150
-
151
- .stx-heatmap__control:active {
152
- background: #e0e0e0;
153
- }
154
- </style>
155
-
156
- <script client>
157
- (function() {
158
- const container = document.getElementById('{{ id }}');
159
- if (!container) return;
160
-
161
- const canvas = container.querySelector('.stx-heatmap__canvas');
162
- const ctx = canvas.getContext('2d');
163
-
164
- const config = {
165
- radius: parseInt(container.dataset.heatmapRadius) || 20,
166
- blur: parseInt(container.dataset.heatmapBlur) || 15,
167
- opacity: parseFloat(container.dataset.heatmapOpacity) || 0.6,
168
- colors: JSON.parse(container.dataset.heatmapColors || '[]'),
169
- source: container.dataset.heatmapSource || 'auto',
170
- endpoint: container.dataset.heatmapEndpoint || ''
171
- };
172
-
173
- // Resize canvas to match container
174
- function resizeCanvas() {
175
- const rect = container.getBoundingClientRect();
176
- canvas.width = rect.width || window.innerWidth;
177
- canvas.height = rect.height || window.innerHeight;
178
- }
179
-
180
- // Create color gradient for heatmap
181
- function createGradient(colors) {
182
- const gradientCanvas = document.createElement('canvas');
183
- gradientCanvas.width = 256;
184
- gradientCanvas.height = 1;
185
- const gradientCtx = gradientCanvas.getContext('2d');
186
- const gradient = gradientCtx.createLinearGradient(0, 0, 256, 0);
187
-
188
- colors.forEach((color, i) => {
189
- gradient.addColorStop(i / (colors.length - 1), color);
190
- });
191
-
192
- gradientCtx.fillStyle = gradient;
193
- gradientCtx.fillRect(0, 0, 256, 1);
194
- return gradientCtx.getImageData(0, 0, 256, 1).data;
195
- }
196
-
197
- // Render heatmap from data points
198
- function render(points) {
199
- resizeCanvas();
200
- ctx.clearRect(0, 0, canvas.width, canvas.height);
201
-
202
- // Draw heat points
203
- points.forEach(function(p) {
204
- const x = (p.x / 100) * canvas.width;
205
- const y = (p.y / 100) * canvas.height;
206
- const intensity = p.type === 'click' ? 1 : 0.3;
207
-
208
- const gradient = ctx.createRadialGradient(x, y, 0, x, y, config.radius);
209
- gradient.addColorStop(0, 'rgba(0,0,0,' + intensity + ')');
210
- gradient.addColorStop(1, 'rgba(0,0,0,0)');
211
- ctx.fillStyle = gradient;
212
- ctx.fillRect(x - config.radius, y - config.radius, config.radius * 2, config.radius * 2);
213
- });
214
-
215
- // Apply blur
216
- if (config.blur > 0) {
217
- ctx.filter = 'blur(' + config.blur + 'px)';
218
- ctx.drawImage(canvas, 0, 0);
219
- ctx.filter = 'none';
220
- }
221
-
222
- // Colorize
223
- const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
224
- const pixels = imageData.data;
225
- const gradientColors = createGradient(config.colors);
226
-
227
- for (let i = 0; i < pixels.length; i += 4) {
228
- const alpha = pixels[i + 3];
229
- if (alpha > 0) {
230
- const colorIndex = Math.min(alpha, 255) * 4;
231
- pixels[i] = gradientColors[colorIndex];
232
- pixels[i + 1] = gradientColors[colorIndex + 1];
233
- pixels[i + 2] = gradientColors[colorIndex + 2];
234
- pixels[i + 3] = Math.round(alpha * config.opacity);
235
- }
236
- }
237
-
238
- ctx.putImageData(imageData, 0, 0);
239
- }
240
-
241
- // Load data based on source
242
- function loadData() {
243
- if (config.source === 'auto' && window.stxHeatmap) {
244
- render(window.stxHeatmap.getData().points);
245
- } else if (config.source === 'endpoint' && config.endpoint) {
246
- fetch(config.endpoint)
247
- .then(r => r.json())
248
- .then(data => render(data.points || data))
249
- .catch(console.error);
250
- }
251
- }
252
-
253
- // Initialize
254
- resizeCanvas();
255
- loadData();
256
-
257
- // Handle resize
258
- window.addEventListener('resize', function() {
259
- resizeCanvas();
260
- loadData();
261
- });
262
-
263
- // Handle interactive controls
264
- const controls = container.querySelectorAll('.stx-heatmap__control');
265
- let zoom = 1;
266
- let panX = 0;
267
- let panY = 0;
268
-
269
- controls.forEach(function(btn) {
270
- btn.addEventListener('click', function() {
271
- const action = btn.dataset.action;
272
- if (action === 'zoom-in') {
273
- zoom = Math.min(zoom * 1.2, 5);
274
- } else if (action === 'zoom-out') {
275
- zoom = Math.max(zoom / 1.2, 0.5);
276
- } else if (action === 'reset') {
277
- zoom = 1;
278
- panX = 0;
279
- panY = 0;
280
- }
281
- canvas.style.transform = 'scale(' + zoom + ') translate(' + panX + 'px, ' + panY + 'px)';
282
- });
283
- });
284
-
285
- // Expose API
286
- container.stxHeatmap = {
287
- render: render,
288
- refresh: loadData,
289
- setData: function(points) { render(points); },
290
- getCanvas: function() { return canvas; },
291
- toDataURL: function() { return canvas.toDataURL(); }
292
- };
293
- })();
294
- </script>
@@ -1,165 +0,0 @@
1
- <script server>
2
- export const src = $props.src || ''
3
- export const srcSet = $props.srcSet || ''
4
- export const sizes = $props.sizes || ''
5
- export const webpSrc = $props.webpSrc || ''
6
- export const webpSrcSet = $props.webpSrcSet || ''
7
- export const alt = $props.alt || ''
8
- export const width = $props.width
9
- export const height = $props.height
10
- export const lazy = $props.lazy ?? true
11
- export const placeholder = $props.placeholder || 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 300"%3E%3Crect fill="%23e5e7eb" width="400" height="300"/%3E%3C/svg%3E'
12
- export const blurDataURL = $props.blurDataURL || ''
13
- export const aspectRatio = $props.aspectRatio
14
- export const objectFit = $props.objectFit || 'cover'
15
- export const rounded = $props.rounded ?? false
16
- export const zoomOnHover = $props.zoomOnHover || false
17
- export const className = $props.className || ''
18
-
19
- export const hasWebP = !!(webpSrc || webpSrcSet)
20
-
21
- const objectFitClasses = {
22
- contain: 'object-contain',
23
- cover: 'object-cover',
24
- fill: 'object-fill',
25
- none: 'object-none',
26
- 'scale-down': 'object-scale-down',
27
- }
28
-
29
- const roundedClass = rounded === true ? 'rounded-lg' : rounded === 'full' ? 'rounded-full' : rounded ? `rounded-${rounded}` : ''
30
- const zoomClasses = zoomOnHover ? 'transform hover:scale-110 transition-transform duration-300 ease-in-out cursor-zoom-in' : ''
31
-
32
- export const containerClasses = `relative overflow-hidden ${roundedClass} ${className}`.trim()
33
- export const imageClasses = `w-full h-full transition-opacity duration-300 ${objectFitClasses[objectFit]} ${zoomClasses}`.trim()
34
- export const blurPlaceholderClasses = `absolute inset-0 ${roundedClass} filter blur-lg scale-110`.trim()
35
- export const containerStyle = aspectRatio ? `padding-bottom: ${(1 / aspectRatio) * 100}%` : ''
36
- </script>
37
-
38
- <script client>
39
- const emit = defineEmits()
40
- const lazy = {{ lazy }}
41
- const realSrc = {{ src }}
42
- const realSrcSet = {{ srcSet }}
43
- const errorPlaceholder = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 300"%3E%3Crect fill="%23e5e7eb" width="400" height="300"/%3E%3Ctext x="50%25" y="50%25" fill="%239ca3af" font-size="16" text-anchor="middle" dominant-baseline="middle"%3EImage not found%3C/text%3E%3C/svg%3E'
44
-
45
- function onImageLoad(event) {
46
- const img = event.target
47
- img.classList.remove('opacity-0')
48
- img.classList.add('opacity-100')
49
- emit('load', event)
50
- }
51
-
52
- function onImageError(event) {
53
- const img = event.target
54
- img.src = errorPlaceholder
55
- emit('error', event)
56
- }
57
-
58
- onMount(() => {
59
- if (!lazy) return
60
- if (typeof IntersectionObserver === 'undefined') return
61
- const lazyImages = document.querySelectorAll('.lazy-image:not([data-stx-img-loaded])')
62
- const observer = new IntersectionObserver((entries, obs) => {
63
- for (const entry of entries) {
64
- if (!entry.isIntersecting) continue
65
- const img = entry.target
66
- img.setAttribute('data-stx-img-loaded', '1')
67
- const dataSrc = img.getAttribute('data-src')
68
- const dataSrcSet = img.getAttribute('data-srcset')
69
- if (!dataSrc) continue
70
- const probe = new Image()
71
- probe.onload = () => {
72
- img.src = dataSrc
73
- if (dataSrcSet) img.srcset = dataSrcSet
74
- img.classList.remove('opacity-0')
75
- img.classList.add('opacity-100')
76
- obs.unobserve(img)
77
- }
78
- probe.onerror = () => {
79
- onImageError({ target: img })
80
- obs.unobserve(img)
81
- }
82
- probe.src = dataSrc
83
- if (dataSrcSet) probe.srcset = dataSrcSet
84
- }
85
- }, { rootMargin: '50px' })
86
- for (const img of lazyImages) observer.observe(img)
87
- })
88
- </script>
89
-
90
- <div class="{{ containerClasses }}" style="{{ containerStyle }}">
91
- @if(blurDataURL)
92
- <img
93
- src="{{ blurDataURL }}"
94
- alt=""
95
- class="{{ blurPlaceholderClasses }}"
96
- aria-hidden="true"
97
- />
98
- @endif
99
-
100
- @if(hasWebP)
101
- <picture>
102
- <source
103
- type="image/webp"
104
- @if(webpSrcSet)srcset="{{ webpSrcSet }}"@endif
105
- @if(webpSrc && !webpSrcSet)srcset="{{ webpSrc }}"@endif
106
- @if(sizes)sizes="{{ sizes }}"@endif
107
- />
108
- @if(lazy)
109
- <img
110
- src="{{ placeholder }}"
111
- data-src="{{ src }}"
112
- @if(srcSet)data-srcset="{{ srcSet }}"@endif
113
- @if(sizes)sizes="{{ sizes }}"@endif
114
- alt="{{ alt }}"
115
- class="{{ imageClasses }} lazy-image opacity-0"
116
- width="{{ width }}"
117
- height="{{ height }}"
118
- loading="lazy"
119
- @load="onImageLoad($event)"
120
- @error="onImageError($event)"
121
- />
122
- @else
123
- <img
124
- src="{{ src }}"
125
- @if(srcSet)srcset="{{ srcSet }}"@endif
126
- @if(sizes)sizes="{{ sizes }}"@endif
127
- alt="{{ alt }}"
128
- class="{{ imageClasses }}"
129
- width="{{ width }}"
130
- height="{{ height }}"
131
- @load="onImageLoad($event)"
132
- @error="onImageError($event)"
133
- />
134
- @endif
135
- </picture>
136
- @else
137
- @if(lazy)
138
- <img
139
- src="{{ placeholder }}"
140
- data-src="{{ src }}"
141
- @if(srcSet)data-srcset="{{ srcSet }}"@endif
142
- @if(sizes)sizes="{{ sizes }}"@endif
143
- alt="{{ alt }}"
144
- class="{{ imageClasses }} lazy-image opacity-0"
145
- width="{{ width }}"
146
- height="{{ height }}"
147
- loading="lazy"
148
- @load="onImageLoad($event)"
149
- @error="onImageError($event)"
150
- />
151
- @else
152
- <img
153
- src="{{ src }}"
154
- @if(srcSet)srcset="{{ srcSet }}"@endif
155
- @if(sizes)sizes="{{ sizes }}"@endif
156
- alt="{{ alt }}"
157
- class="{{ imageClasses }}"
158
- width="{{ width }}"
159
- height="{{ height }}"
160
- @load="onImageLoad($event)"
161
- @error="onImageError($event)"
162
- />
163
- @endif
164
- @endif
165
- </div>