@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,114 +0,0 @@
1
- # Dropdown Component
2
-
3
- A flexible dropdown menu component inspired by Headless UI, built with STX and headwind.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- bun add @stacksjs/components
9
- ```
10
-
11
- ## Usage
12
-
13
- ```stx
14
- <Dropdown>
15
- <DropdownButton className="btn-primary">Options</DropdownButton>
16
-
17
- <DropdownItems>
18
- <DropdownItem>Edit</DropdownItem>
19
- <DropdownItem>Delete</DropdownItem>
20
- </DropdownItems>
21
- </Dropdown>
22
- ```
23
-
24
- ## Props
25
-
26
- ### Dropdown
27
- | Prop | Type | Default | Description |
28
- |------|------|---------|-------------|
29
- | `className` | `string` | `''` | Additional CSS classes |
30
- | `as` | `string` | `'div'` | HTML element to render |
31
-
32
- ### DropdownButton
33
- | Prop | Type | Default | Description |
34
- |------|------|---------|-------------|
35
- | `className` | `string` | `''` | Additional CSS classes |
36
- | `as` | `string` | `'button'` | HTML element to render |
37
- | `disabled` | `boolean` | `false` | Disable the button |
38
- | `onClick` | `(e: Event) => void` | - | Click handler |
39
-
40
- ### DropdownItems
41
- | Prop | Type | Default | Description |
42
- |------|------|---------|-------------|
43
- | `className` | `string` | `''` | Additional CSS classes |
44
- | `as` | `string` | `'div'` | HTML element to render |
45
- | `static` | `boolean` | `false` | Keep visible regardless of state |
46
-
47
- ### DropdownItem
48
- | Prop | Type | Default | Description |
49
- |------|------|---------|-------------|
50
- | `className` | `string` | `''` | Additional CSS classes |
51
- | `as` | `string` | `'div'` | HTML element to render |
52
- | `disabled` | `boolean` | `false` | Disable the item |
53
- | `onClick` | `(e: Event) => void` | - | Click handler |
54
-
55
- ## Examples
56
-
57
- ### Basic Dropdown
58
-
59
- ```stx
60
- <Dropdown>
61
- <DropdownButton className="px-4 py-2 bg-blue-500 text-white rounded-md">
62
- Actions
63
- </DropdownButton>
64
-
65
- <DropdownItems className="w-56">
66
- <DropdownItem>Edit</DropdownItem>
67
- <DropdownItem>Duplicate</DropdownItem>
68
- <DropdownItem disabled>Archive (coming soon)</DropdownItem>
69
- <DropdownItem>Delete</DropdownItem>
70
- </DropdownItems>
71
- </Dropdown>
72
- ```
73
-
74
- ### With Icons
75
-
76
- ```stx
77
- <DropdownItem>
78
- <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
79
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
80
- </svg>
81
- Edit
82
- </DropdownItem>
83
- ```
84
-
85
- ### Grouped Items
86
-
87
- ```stx
88
- <DropdownItems>
89
- <div class="px-1 py-1">
90
- <DropdownItem>Edit</DropdownItem>
91
- <DropdownItem>Duplicate</DropdownItem>
92
- </div>
93
-
94
- <div class="px-1 py-1">
95
- <DropdownItem>Archive</DropdownItem>
96
- <DropdownItem>Move</DropdownItem>
97
- </div>
98
-
99
- <div class="px-1 py-1">
100
- <DropdownItem>Delete</DropdownItem>
101
- </div>
102
- </DropdownItems>
103
- ```
104
-
105
- ## Features
106
-
107
- - Automatic positioning
108
- - Keyboard navigation
109
- - Focus management
110
- - Dark mode support
111
- - Accessible (WAI-ARIA)
112
- - Customizable styling with headwind
113
- - Disabled state support
114
- - Active state on hover
@@ -1,29 +0,0 @@
1
- export { default as Dropdown } from './Dropdown.stx'
2
- export { default as DropdownButton } from './DropdownButton.stx'
3
- export { default as DropdownItem } from './DropdownItem.stx'
4
- export { default as DropdownItems } from './DropdownItems.stx'
5
-
6
- export interface DropdownProps {
7
- className?: string
8
- as?: string
9
- }
10
-
11
- export interface DropdownButtonProps {
12
- className?: string
13
- as?: string
14
- disabled?: boolean
15
- onClick?: (e: Event) => void
16
- }
17
-
18
- export interface DropdownItemsProps {
19
- className?: string
20
- as?: string
21
- static?: boolean
22
- }
23
-
24
- export interface DropdownItemProps {
25
- className?: string
26
- as?: string
27
- disabled?: boolean
28
- onClick?: (e: Event) => void
29
- }
@@ -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>