@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,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>
@@ -1,164 +0,0 @@
1
- # Image Component
2
-
3
- An optimized image component with lazy loading, placeholders, and responsive features.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- bun add @stacksjs/components
9
- ```
10
-
11
- ## Usage
12
-
13
- ### Basic Image
14
-
15
- ```stx
16
- <Image
17
- src="/path/to/image.jpg"
18
- alt="Description of image"
19
- :width="400"
20
- :height="300"
21
- />
22
- ```
23
-
24
- ### Lazy Loading (default)
25
-
26
- ```stx
27
- <Image
28
- src="/large-image.jpg"
29
- alt="Large image"
30
- lazy
31
- placeholder="/thumbnail.jpg"
32
- />
33
- ```
34
-
35
- ### Aspect Ratio
36
-
37
- ```stx
38
- <Image
39
- src="/hero.jpg"
40
- alt="Hero image"
41
- :aspectRatio="16/9"
42
- objectFit="cover"
43
- />
44
- ```
45
-
46
- ### Rounded Images
47
-
48
- ```stx
49
- <!-- Rounded corners -->
50
- <Image
51
- src="/avatar.jpg"
52
- alt="Avatar"
53
- rounded
54
- />
55
-
56
- <!-- Circular -->
57
- <Image
58
- src="/profile.jpg"
59
- alt="Profile"
60
- rounded="full"
61
- />
62
- ```
63
-
64
- ### With Callbacks
65
-
66
- ```stx
67
- <script server>
68
- const handleLoad = () => {
69
- console.log('Image loaded!')
70
- }
71
-
72
- const handleError = () => {
73
- console.error('Failed to load image')
74
- }
75
- </script>
76
-
77
- <Image
78
- src="/photo.jpg"
79
- alt="Photo"
80
- :onLoad="handleLoad"
81
- :onError="handleError"
82
- />
83
- ```
84
-
85
- ## Props
86
-
87
- | Prop | Type | Default | Description |
88
- |------|------|---------|-------------|
89
- | `src` | `string` | - | Image source URL (required) |
90
- | `alt` | `string` | - | Alt text (required) |
91
- | `width` | `number \| string` | - | Image width |
92
- | `height` | `number \| string` | - | Image height |
93
- | `lazy` | `boolean` | `true` | Enable lazy loading |
94
- | `placeholder` | `string` | gray SVG | Placeholder image while loading |
95
- | `onLoad` | `function` | - | Callback when image loads |
96
- | `onError` | `function` | - | Callback when image fails |
97
- | `aspectRatio` | `number` | - | Aspect ratio (e.g., 16/9, 4/3) |
98
- | `objectFit` | `'contain' \| 'cover' \| 'fill' \| 'none' \| 'scale-down'` | `'cover'` | How image fits container |
99
- | `rounded` | `boolean \| 'full' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | `false` | Border radius |
100
- | `className` | `string` | `''` | Additional CSS classes |
101
-
102
- ## Features
103
-
104
- - **Lazy loading** - Loads images only when visible (Intersection Observer)
105
- - **Placeholders** - Shows placeholder until image loads
106
- - **Aspect ratio** - Maintains aspect ratio to prevent layout shift
107
- - **Object fit** - Control how images fill their container
108
- - **Responsive** - Adapts to container size
109
- - **Error handling** - Shows fallback on load failure
110
- - **Dark mode** - Skeleton loader adapts to dark mode
111
- - **Smooth transitions** - Fade-in effect on load
112
- - **Performance** - Optimized with intersection observer
113
- - **Modern ES modules** - Clean export syntax
114
- - **Headwind styling** - Utility-first CSS classes
115
-
116
- ## Object Fit Options
117
-
118
- - `contain` - Image fits within container, maintains aspect ratio
119
- - `cover` - Image covers container, may crop
120
- - `fill` - Image stretches to fill container
121
- - `none` - Image maintains original size
122
- - `scale-down` - Uses smallest of `none` or `contain`
123
-
124
- ## Rounded Options
125
-
126
- - `true` - `rounded-lg` (8px)
127
- - `'full'` - `rounded-full` (fully circular)
128
- - `'sm'` - `rounded-sm` (2px)
129
- - `'md'` - `rounded-md` (6px)
130
- - `'lg'` - `rounded-lg` (8px)
131
- - `'xl'` - `rounded-xl` (12px)
132
- - `'2xl'` - `rounded-2xl` (16px)
133
-
134
- ## Accessibility
135
-
136
- - Always provide meaningful `alt` text
137
- - Component automatically handles loading states
138
- - Error states show user-friendly fallback
139
- - Works without JavaScript (progressive enhancement)
140
-
141
- ## Performance Tips
142
-
143
- 1. Use `aspectRatio` to prevent layout shift
144
- 2. Enable `lazy` loading for below-the-fold images
145
- 3. Provide appropriate `width` and `height`
146
- 4. Use appropriate image formats (WebP, AVIF)
147
- 5. Optimize images before upload
148
-
149
- ## Example: Image Gallery
150
-
151
- ```stx
152
- <div class="grid grid-cols-3 gap-4">
153
- @foreach(image in images)
154
- <Image
155
- :src="image.url"
156
- :alt="image.description"
157
- :aspectRatio="1"
158
- objectFit="cover"
159
- rounded
160
- lazy
161
- />
162
- @endforeach
163
- </div>
164
- ```
@@ -1,22 +0,0 @@
1
- export { default as Image } from './Image.stx'
2
-
3
- export interface ImageProps {
4
- src: string
5
- srcSet?: string
6
- sizes?: string
7
- webpSrc?: string
8
- webpSrcSet?: string
9
- alt: string
10
- width?: number | string
11
- height?: number | string
12
- lazy?: boolean
13
- placeholder?: string
14
- blurDataURL?: string
15
- onLoad?: (event: Event) => void
16
- onError?: (event: Event) => void
17
- aspectRatio?: number
18
- objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down'
19
- rounded?: boolean | 'full' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
20
- zoomOnHover?: boolean
21
- className?: string
22
- }
@@ -1,30 +0,0 @@
1
- <script server>
2
- export const value = $props.value || ''
3
- export const placeholder = $props.placeholder || 'Enter your email'
4
- export const disabled = $props.disabled || false
5
- export const readonly = $props.readonly || false
6
- export const size = $props.size || 'md'
7
- export const error = $props.error || false
8
- export const label = $props.label || ''
9
- export const helperText = $props.helperText || ''
10
- export const required = $props.required || false
11
- export const className = $props.className || ''
12
-
13
- export const emailIcon = '<svg fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" /></svg>'
14
- </script>
15
-
16
- <TextInput
17
- :value="value"
18
- :placeholder="placeholder"
19
- :disabled="disabled"
20
- :readonly="readonly"
21
- :size="size"
22
- :error="error"
23
- :label="label"
24
- :helperText="helperText"
25
- :required="required"
26
- :leftIcon="emailIcon"
27
- :clearable="true"
28
- :className="className"
29
- inputClassName="font-mono"
30
- />
@@ -1,126 +0,0 @@
1
- <script server>
2
- export const value = $props.value || ''
3
- export const placeholder = $props.placeholder || '0'
4
- export const disabled = $props.disabled || false
5
- export const readonly = $props.readonly || false
6
- export const size = $props.size || 'md'
7
- export const error = $props.error || false
8
- export const label = $props.label || ''
9
- export const helperText = $props.helperText || ''
10
- export const required = $props.required || false
11
- export const min = $props.min
12
- export const max = $props.max
13
- export const step = $props.step || 1
14
- export const showControls = $props.showControls ?? true
15
- export const className = $props.className || ''
16
- export const inputClassName = $props.inputClassName || ''
17
-
18
- const sizeClasses = { sm: 'px-3 py-1.5 text-sm h-8', md: 'px-4 py-2 text-base h-10', lg: 'px-5 py-3 text-lg h-12' }
19
- const baseClasses = 'block w-full border-0 rounded-md shadow-sm ring-1 ring-inset transition-colors duration-200 focus:ring-2 focus:ring-inset dark:bg-blue-gray-700 dark:text-gray-100'
20
- const stateClasses = error
21
- ? 'ring-red-300 dark:ring-red-600 text-red-900 dark:text-red-300 placeholder-red-300 dark:placeholder-red-500 focus:ring-red-500 dark:focus:ring-red-400'
22
- : 'ring-gray-300 dark:ring-gray-600 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:ring-indigo-600 dark:focus:ring-indigo-400'
23
- const disabledClasses = disabled
24
- ? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
25
- : readonly
26
- ? 'bg-gray-50 dark:bg-gray-800 cursor-default'
27
- : 'bg-white dark:bg-blue-gray-700'
28
- const paddingClasses = showControls ? 'pr-10' : ''
29
-
30
- export const inputClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses} ${paddingClasses} ${inputClassName}`.trim()
31
- export const labelClasses = `block text-sm font-medium leading-6 mb-2 ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-gray-100'}`.trim()
32
- export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
33
- </script>
34
-
35
- <script client>
36
- const emit = defineEmits()
37
- const stepSize = {{ step }}
38
- const minVal = {{ min === undefined ? 'undefined' : min }}
39
- const maxVal = {{ max === undefined ? 'undefined' : max }}
40
- // Reactive prop binding — parent can drive value via signals. See #1704.
41
- const inputValue = useReactiveProp('value', {{ value }}, { parse: (v) => v == null || v === '' ? '' : String(v) })
42
-
43
- function onInput(event) {
44
- inputValue.set(event.target.value)
45
- emit('input', inputValue())
46
- }
47
-
48
- function increment() {
49
- const current = Number.parseFloat(inputValue()) || 0
50
- const next = current + stepSize
51
- if (maxVal !== undefined && next > maxVal) return
52
- inputValue.set(String(next))
53
- emit('input', String(next))
54
- }
55
-
56
- function decrement() {
57
- const current = Number.parseFloat(inputValue()) || 0
58
- const next = current - stepSize
59
- if (minVal !== undefined && next < minVal) return
60
- inputValue.set(String(next))
61
- emit('input', String(next))
62
- }
63
-
64
- defineExpose({ value: inputValue, increment, decrement })
65
- </script>
66
-
67
- <div class="w-full {{ className }}">
68
- @if(label)
69
- <label for="number-input" class="{{ labelClasses }}">
70
- {{ label }}
71
- @if(required)
72
- <span class="text-red-500 ml-1">*</span>
73
- @endif
74
- </label>
75
- @endif
76
-
77
- <div class="relative">
78
- <input
79
- type="number"
80
- id="number-input"
81
- class="{{ inputClasses }}"
82
- :value="inputValue()"
83
- placeholder="{{ placeholder }}"
84
- @if(disabled)disabled@endif
85
- @if(readonly)readonly@endif
86
- @if(required)required@endif
87
- @if(min !== undefined)min="{{ min }}"@endif
88
- @if(max !== undefined)max="{{ max }}"@endif
89
- step="{{ step }}"
90
- @input="onInput($event)"
91
- :aria-invalid="{{ error ? 'true' : 'false' }}"
92
- @if(helperText)aria-describedby="helper-text"@endif
93
- />
94
-
95
- @if(showControls && !disabled && !readonly)
96
- <div class="absolute inset-y-0 right-0 flex flex-col">
97
- <button
98
- type="button"
99
- @click="increment()"
100
- class="flex items-center justify-center w-8 h-1/2 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 focus:outline-none border-b border-gray-300 dark:border-gray-600"
101
- aria-label="Increment"
102
- >
103
- <svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
104
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7" />
105
- </svg>
106
- </button>
107
- <button
108
- type="button"
109
- @click="decrement()"
110
- class="flex items-center justify-center w-8 h-1/2 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 focus:outline-none"
111
- aria-label="Decrement"
112
- >
113
- <svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
114
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
115
- </svg>
116
- </button>
117
- </div>
118
- @endif
119
- </div>
120
-
121
- @if(helperText)
122
- <p id="helper-text" class="{{ helperClasses }}">
123
- {{ helperText }}
124
- </p>
125
- @endif
126
- </div>
@@ -1,143 +0,0 @@
1
- <script server>
2
- export const value = $props.value || ''
3
- export const placeholder = $props.placeholder || 'Enter your password'
4
- export const disabled = $props.disabled || false
5
- export const readonly = $props.readonly || false
6
- export const size = $props.size || 'md'
7
- export const error = $props.error || false
8
- export const label = $props.label || ''
9
- export const helperText = $props.helperText || ''
10
- export const required = $props.required || false
11
- export const showStrength = $props.showStrength || false
12
- export const className = $props.className || ''
13
- export const inputClassName = $props.inputClassName || ''
14
-
15
- const sizeClasses = { sm: 'px-3 py-1.5 text-sm h-8', md: 'px-4 py-2 text-base h-10', lg: 'px-5 py-3 text-lg h-12' }
16
- const iconSizeClasses = { sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6' }
17
-
18
- const baseClasses = 'block w-full border-0 rounded-md shadow-sm ring-1 ring-inset transition-colors duration-200 focus:ring-2 focus:ring-inset dark:bg-blue-gray-700 dark:text-gray-100 pr-10'
19
- const stateClasses = error
20
- ? 'ring-red-300 dark:ring-red-600 text-red-900 dark:text-red-300 placeholder-red-300 dark:placeholder-red-500 focus:ring-red-500 dark:focus:ring-red-400'
21
- : 'ring-gray-300 dark:ring-gray-600 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:ring-indigo-600 dark:focus:ring-indigo-400'
22
- const disabledClasses = disabled
23
- ? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
24
- : readonly
25
- ? 'bg-gray-50 dark:bg-gray-800 cursor-default'
26
- : 'bg-white dark:bg-blue-gray-700'
27
-
28
- export const inputClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses} ${inputClassName}`.trim()
29
- export const labelClasses = `block text-sm font-medium leading-6 mb-2 ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-gray-100'}`.trim()
30
- export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
31
- export const iconClasses = iconSizeClasses[size]
32
- </script>
33
-
34
- <script client>
35
- const emit = defineEmits()
36
- const showPassword = state(false)
37
- // Reactive prop binding — parent can drive value via signals. See #1704.
38
- const inputValue = useReactiveProp('value', {{ value }}, { parse: (v) => v == null ? '' : String(v) })
39
-
40
- function onInput(event) {
41
- inputValue.set(event.target.value)
42
- emit('input', inputValue())
43
- }
44
-
45
- function toggleVisibility() {
46
- showPassword.set(!showPassword())
47
- }
48
-
49
- function calcStrength(pw) {
50
- if (!pw) return 0
51
- let s = 0
52
- if (pw.length >= 8) s += 25
53
- if (pw.length >= 12) s += 25
54
- if (/[a-z]/.test(pw) && /[A-Z]/.test(pw)) s += 25
55
- if (/\d/.test(pw)) s += 15
56
- if (/[^a-zA-Z\d]/.test(pw)) s += 10
57
- return Math.min(s, 100)
58
- }
59
-
60
- const strength = derived(() => calcStrength(inputValue()))
61
- const strengthText = derived(() => {
62
- const s = strength()
63
- return s < 40 ? 'Weak' : s < 70 ? 'Medium' : 'Strong'
64
- })
65
- const strengthColor = derived(() => {
66
- const s = strength()
67
- return s < 40 ? 'bg-red-500' : s < 70 ? 'bg-yellow-500' : 'bg-green-500'
68
- })
69
- const strengthLabelColor = derived(() => {
70
- const s = strength()
71
- return s < 40 ? 'text-red-600' : s < 70 ? 'text-yellow-600' : 'text-green-600'
72
- })
73
- const inputType = derived(() => showPassword() ? 'text' : 'password')
74
- const toggleAriaLabel = derived(() => showPassword() ? 'Hide password' : 'Show password')
75
-
76
- defineExpose({ value: inputValue })
77
- </script>
78
-
79
- <div class="w-full {{ className }}">
80
- @if(label)
81
- <label for="password-input" class="{{ labelClasses }}">
82
- {{ label }}
83
- @if(required)
84
- <span class="text-red-500 ml-1">*</span>
85
- @endif
86
- </label>
87
- @endif
88
-
89
- <div class="relative">
90
- <input
91
- :type="inputType()"
92
- id="password-input"
93
- class="{{ inputClasses }}"
94
- :value="inputValue()"
95
- placeholder="{{ placeholder }}"
96
- @if(disabled)disabled@endif
97
- @if(readonly)readonly@endif
98
- @if(required)required@endif
99
- @input="onInput($event)"
100
- :aria-invalid="{{ error ? 'true' : 'false' }}"
101
- @if(helperText)aria-describedby="helper-text"@endif
102
- />
103
-
104
- <div class="absolute inset-y-0 right-0 flex items-center pr-3">
105
- <button
106
- type="button"
107
- @click="toggleVisibility()"
108
- class="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 focus:outline-none"
109
- :aria-label="toggleAriaLabel()"
110
- >
111
- <svg :show="showPassword()" class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
112
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
113
- </svg>
114
- <svg :show="!showPassword()" class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
115
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
116
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
117
- </svg>
118
- </button>
119
- </div>
120
- </div>
121
-
122
- @if(showStrength)
123
- <div :show="inputValue().length > 0" class="mt-2">
124
- <div class="flex items-center justify-between mb-1">
125
- <span class="text-xs text-gray-600 dark:text-gray-400">Password strength</span>
126
- <span class="text-xs font-medium" :class="strengthLabelColor()" x-text="strengthText()"></span>
127
- </div>
128
- <div class="w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
129
- <div
130
- class="h-full transition-all duration-300 rounded-full"
131
- :class="strengthColor()"
132
- :style="`width: ${strength()}%`"
133
- ></div>
134
- </div>
135
- </div>
136
- @endif
137
-
138
- @if(helperText)
139
- <p id="helper-text" class="{{ helperClasses }}">
140
- {{ helperText }}
141
- </p>
142
- @endif
143
- </div>