@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.
- package/dist/index.js +4 -4
- package/package.json +5 -6
- package/src/components/CodeBlock.stx +0 -54
- package/src/components/Footer.stx +0 -66
- package/src/components/Hero.stx +0 -25
- package/src/components/Installation.stx +0 -37
- package/src/components/index.ts +0 -4
- package/src/composables/index.ts +0 -4
- package/src/composables/useCopyCode.ts +0 -73
- package/src/composables/useDarkMode.ts +0 -100
- package/src/composables/useSEO.ts +0 -230
- package/src/composables/utilities/index.ts +0 -7
- package/src/composables/utilities/useClickOutside.ts +0 -72
- package/src/composables/utilities/useFocusTrap.ts +0 -159
- package/src/composables/utilities/useKeyboard.ts +0 -124
- package/src/composables/utilities/useLocalStorage.ts +0 -132
- package/src/composables/utilities/useMediaQuery.ts +0 -118
- package/src/index.ts +0 -99
- package/src/stx.d.ts +0 -4
- package/src/ui/accordion/Accordion.stx +0 -146
- package/src/ui/accordion/AccordionItem.stx +0 -37
- package/src/ui/accordion/index.ts +0 -47
- package/src/ui/audio/Audio.stx +0 -50
- package/src/ui/audio/README.md +0 -41
- package/src/ui/audio/index.ts +0 -13
- package/src/ui/auth/Login.stx +0 -161
- package/src/ui/auth/README.md +0 -142
- package/src/ui/auth/Signup.stx +0 -159
- package/src/ui/auth/TwoFactorChallenge.stx +0 -111
- package/src/ui/auth/index.ts +0 -32
- package/src/ui/avatar/Avatar.stx +0 -77
- package/src/ui/avatar/index.ts +0 -12
- package/src/ui/badge/Badge.stx +0 -62
- package/src/ui/badge/index.ts +0 -10
- package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
- package/src/ui/breadcrumb/index.ts +0 -15
- package/src/ui/button/Button.stx +0 -74
- package/src/ui/button/README.md +0 -80
- package/src/ui/button/index.ts +0 -85
- package/src/ui/calendar/Calendar.stx +0 -189
- package/src/ui/calendar/README.md +0 -254
- package/src/ui/calendar/index.ts +0 -14
- package/src/ui/card/Card.stx +0 -68
- package/src/ui/card/index.ts +0 -13
- package/src/ui/checkbox/Checkbox.stx +0 -86
- package/src/ui/checkbox/index.ts +0 -16
- package/src/ui/combobox/Combobox.stx +0 -96
- package/src/ui/combobox/ComboboxButton.stx +0 -25
- package/src/ui/combobox/ComboboxInput.stx +0 -32
- package/src/ui/combobox/ComboboxOption.stx +0 -49
- package/src/ui/combobox/ComboboxOptions.stx +0 -16
- package/src/ui/combobox/README.md +0 -150
- package/src/ui/combobox/index.ts +0 -40
- package/src/ui/command-palette/CommandPalette.stx +0 -81
- package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
- package/src/ui/command-palette/README.md +0 -42
- package/src/ui/command-palette/index.ts +0 -15
- package/src/ui/dialog/Dialog.stx +0 -41
- package/src/ui/dialog/DialogBackdrop.stx +0 -9
- package/src/ui/dialog/DialogDescription.stx +0 -11
- package/src/ui/dialog/DialogPanel.stx +0 -16
- package/src/ui/dialog/DialogTitle.stx +0 -11
- package/src/ui/dialog/README.md +0 -121
- package/src/ui/dialog/index.ts +0 -32
- package/src/ui/drawer/Drawer.stx +0 -120
- package/src/ui/drawer/README.md +0 -90
- package/src/ui/drawer/index.ts +0 -9
- package/src/ui/dropdown/Dropdown.stx +0 -82
- package/src/ui/dropdown/DropdownButton.stx +0 -29
- package/src/ui/dropdown/DropdownItem.stx +0 -35
- package/src/ui/dropdown/DropdownItems.stx +0 -18
- package/src/ui/dropdown/README.md +0 -114
- package/src/ui/dropdown/index.ts +0 -29
- package/src/ui/form/Form.stx +0 -95
- package/src/ui/form/index.ts +0 -67
- package/src/ui/heatmap/Heatmap.stx +0 -294
- package/src/ui/image/Image.stx +0 -165
- package/src/ui/image/README.md +0 -164
- package/src/ui/image/index.ts +0 -22
- package/src/ui/input/EmailInput.stx +0 -30
- package/src/ui/input/NumberInput.stx +0 -126
- package/src/ui/input/PasswordInput.stx +0 -143
- package/src/ui/input/SearchInput.stx +0 -44
- package/src/ui/input/TextInput.stx +0 -132
- package/src/ui/input/index.ts +0 -180
- package/src/ui/listbox/Listbox.stx +0 -101
- package/src/ui/listbox/ListboxButton.stx +0 -28
- package/src/ui/listbox/ListboxLabel.stx +0 -11
- package/src/ui/listbox/ListboxOption.stx +0 -49
- package/src/ui/listbox/ListboxOptions.stx +0 -16
- package/src/ui/listbox/README.md +0 -135
- package/src/ui/listbox/index.ts +0 -39
- package/src/ui/navigator/Navigator.stx +0 -113
- package/src/ui/navigator/README.md +0 -352
- package/src/ui/navigator/index.ts +0 -20
- package/src/ui/notification/Notification.stx +0 -117
- package/src/ui/notification/README.md +0 -114
- package/src/ui/notification/index.ts +0 -12
- package/src/ui/pagination/Pagination.stx +0 -150
- package/src/ui/pagination/index.ts +0 -11
- package/src/ui/payment/Checkout.stx +0 -31
- package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
- package/src/ui/payment/PaymentMethods.stx +0 -104
- package/src/ui/payment/README.md +0 -345
- package/src/ui/payment/SubscriptionCheckout.stx +0 -323
- package/src/ui/payment/index.ts +0 -59
- package/src/ui/popover/Popover.stx +0 -60
- package/src/ui/popover/PopoverButton.stx +0 -29
- package/src/ui/popover/PopoverPanel.stx +0 -23
- package/src/ui/popover/README.md +0 -91
- package/src/ui/popover/index.ts +0 -22
- package/src/ui/portal/Portal.stx +0 -57
- package/src/ui/portal/index.ts +0 -6
- package/src/ui/progress/Progress.stx +0 -131
- package/src/ui/progress/index.ts +0 -12
- package/src/ui/radio/Radio.stx +0 -77
- package/src/ui/radio/index.ts +0 -15
- package/src/ui/radio-group/README.md +0 -139
- package/src/ui/radio-group/RadioGroup.stx +0 -60
- package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
- package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
- package/src/ui/radio-group/RadioGroupOption.stx +0 -58
- package/src/ui/radio-group/index.ts +0 -30
- package/src/ui/select/README.md +0 -180
- package/src/ui/select/Select.stx +0 -82
- package/src/ui/select/index.ts +0 -21
- package/src/ui/sidebar/Sidebar.stx +0 -476
- package/src/ui/sidebar/SidebarFooter.stx +0 -71
- package/src/ui/sidebar/SidebarHeader.stx +0 -103
- package/src/ui/sidebar/SidebarItem.stx +0 -86
- package/src/ui/sidebar/SidebarSection.stx +0 -83
- package/src/ui/sidebar/index.ts +0 -153
- package/src/ui/sidebar/themes.ts +0 -240
- package/src/ui/skeleton/Skeleton.stx +0 -48
- package/src/ui/skeleton/index.ts +0 -11
- package/src/ui/spinner/Spinner.stx +0 -109
- package/src/ui/spinner/index.ts +0 -9
- package/src/ui/stepper/README.md +0 -36
- package/src/ui/stepper/Stepper.stx +0 -17
- package/src/ui/stepper/StepperStep.stx +0 -57
- package/src/ui/stepper/index.ts +0 -19
- package/src/ui/storage/README.md +0 -193
- package/src/ui/storage/index.ts +0 -347
- package/src/ui/switch/README.md +0 -114
- package/src/ui/switch/Switch.stx +0 -72
- package/src/ui/switch/index.ts +0 -10
- package/src/ui/table/README.md +0 -39
- package/src/ui/table/Table.stx +0 -14
- package/src/ui/table/TableBody.stx +0 -10
- package/src/ui/table/TableCell.stx +0 -10
- package/src/ui/table/TableHead.stx +0 -10
- package/src/ui/table/TableHeader.stx +0 -20
- package/src/ui/table/TableRow.stx +0 -11
- package/src/ui/table/index.ts +0 -35
- package/src/ui/tabs/TabPanel.stx +0 -25
- package/src/ui/tabs/Tabs.stx +0 -202
- package/src/ui/tabs/index.ts +0 -50
- package/src/ui/teleport/Teleport.stx +0 -65
- package/src/ui/teleport/index.ts +0 -7
- package/src/ui/textarea/Textarea.stx +0 -108
- package/src/ui/textarea/index.ts +0 -20
- package/src/ui/tooltip/Tooltip.stx +0 -71
- package/src/ui/tooltip/index.ts +0 -10
- package/src/ui/transition/README.md +0 -30
- package/src/ui/transition/Transition.stx +0 -102
- package/src/ui/transition/index.ts +0 -25
- package/src/ui/video/README.md +0 -47
- package/src/ui/video/Video.stx +0 -45
- package/src/ui/video/index.ts +0 -16
- package/src/ui/virtual-list/VirtualList.stx +0 -67
- package/src/ui/virtual-list/index.ts +0 -10
- package/src/ui/virtual-table/VirtualTable.stx +0 -103
- package/src/ui/virtual-table/index.ts +0 -21
- package/src/utils/PROP_VALIDATION.md +0 -458
- package/src/utils/THEME_SYSTEM.md +0 -702
- package/src/utils/accessibility.ts +0 -841
- package/src/utils/animation/easing.ts +0 -368
- package/src/utils/animation/helpers.ts +0 -223
- package/src/utils/animation/index.ts +0 -5
- package/src/utils/animation/keyframes.ts +0 -230
- package/src/utils/error-handling.ts +0 -475
- package/src/utils/highlighter.ts +0 -93
- package/src/utils/i18n.ts +0 -634
- package/src/utils/keep-alive.ts +0 -819
- package/src/utils/lazy-loading.ts +0 -530
- package/src/utils/prop-validation.ts +0 -478
- package/src/utils/theme.ts +0 -626
|
@@ -1,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
|
package/src/ui/dropdown/index.ts
DELETED
|
@@ -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
|
-
}
|
package/src/ui/form/Form.stx
DELETED
|
@@ -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>
|
package/src/ui/form/index.ts
DELETED
|
@@ -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>
|