@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,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>
@@ -1,44 +0,0 @@
1
- <script server>
2
- export const value = $props.value || ''
3
- export const placeholder = $props.placeholder || 'Search...'
4
- export const disabled = $props.disabled || false
5
- export const size = $props.size || 'md'
6
- export const error = $props.error || false
7
- export const label = $props.label || ''
8
- export const helperText = $props.helperText || ''
9
- export const loading = $props.loading || false
10
- export const className = $props.className || ''
11
-
12
- export const searchIcon = '<svg fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>'
13
- export const spinnerIcon = '<svg class="animate-spin" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>'
14
- export const rightIcon = loading ? spinnerIcon : ''
15
- </script>
16
-
17
- <script client>
18
- const emit = defineEmits()
19
- let searchTimeout = null
20
-
21
- function onSearchInput(event) {
22
- const target = event.target
23
- const value = target?.value ?? event.detail
24
- if (searchTimeout) clearTimeout(searchTimeout)
25
- searchTimeout = setTimeout(() => emit('search', value), 300)
26
- }
27
- </script>
28
-
29
- <div @input="onSearchInput($event)">
30
- <TextInput
31
- :value="value"
32
- :placeholder="placeholder"
33
- :disabled="disabled"
34
- :size="size"
35
- :error="error"
36
- :label="label"
37
- :helperText="helperText"
38
- :leftIcon="searchIcon"
39
- :rightIcon="rightIcon"
40
- :clearable="!loading"
41
- :className="className"
42
- inputClassName="pr-20"
43
- />
44
- </div>
@@ -1,132 +0,0 @@
1
- <script server>
2
- export const value = $props.value || ''
3
- export const placeholder = $props.placeholder || ''
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 leftIcon = $props.leftIcon || ''
12
- export const rightIcon = $props.rightIcon || ''
13
- export const clearable = $props.clearable || false
14
- export const maxLength = $props.maxLength
15
- export const showCount = $props.showCount || false
16
- export const className = $props.className || ''
17
- export const inputClassName = $props.inputClassName || ''
18
-
19
- 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' }
20
- const iconSizeClasses = { sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6' }
21
-
22
- 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'
23
- const stateClasses = error
24
- ? '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'
25
- : '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'
26
- const disabledClasses = disabled
27
- ? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
28
- : readonly
29
- ? 'bg-gray-50 dark:bg-gray-800 cursor-default'
30
- : 'bg-white dark:bg-blue-gray-700'
31
- const paddingClasses = leftIcon ? 'pl-10' : rightIcon || clearable ? 'pr-10' : ''
32
-
33
- export const inputClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses} ${paddingClasses} ${inputClassName}`.trim()
34
- 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()
35
- export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
36
- export const iconClasses = iconSizeClasses[size]
37
- export const inputId = `input-${label}`
38
- </script>
39
-
40
- <script client>
41
- const emit = defineEmits()
42
- // Reactive prop binding so the parent can drive `value` via signals.
43
- // Parent listens to @input to push user changes back into its signal.
44
- // See stacksjs/stx#1704.
45
- const inputValue = useReactiveProp('value', {{ value }}, { parse: (v) => v == null ? '' : String(v) })
46
-
47
- function onInput(event) {
48
- inputValue.set(event.target.value)
49
- emit('input', inputValue())
50
- }
51
-
52
- function onClear() {
53
- inputValue.set('')
54
- emit('input', '')
55
- emit('clear')
56
- }
57
-
58
- const hasValue = derived(() => inputValue().length > 0)
59
- const charCount = derived(() => `${inputValue().length} / {{ maxLength || 0 }}`)
60
-
61
- defineExpose({ value: inputValue, clear: onClear })
62
- </script>
63
-
64
- <div class="w-full {{ className }}">
65
- @if(label)
66
- <label for="{{ inputId }}" class="{{ labelClasses }}">
67
- {{ label }}
68
- @if(required)
69
- <span class="text-red-500 ml-1">*</span>
70
- @endif
71
- </label>
72
- @endif
73
-
74
- <div class="relative">
75
- @if(leftIcon)
76
- <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
77
- <span class="{{ iconClasses }} text-gray-400 dark:text-gray-500">{!! leftIcon !!}</span>
78
- </div>
79
- @endif
80
-
81
- <input
82
- type="text"
83
- id="{{ inputId }}"
84
- class="{{ inputClasses }}"
85
- :value="inputValue()"
86
- placeholder="{{ placeholder }}"
87
- @if(disabled)disabled@endif
88
- @if(readonly)readonly@endif
89
- @if(required)required@endif
90
- @if(maxLength)maxlength="{{ maxLength }}"@endif
91
- @input="onInput($event)"
92
- :aria-invalid="{{ error ? 'true' : 'false' }}"
93
- @if(helperText)aria-describedby="helper-text"@endif
94
- />
95
-
96
- @if(clearable)
97
- <div :show="hasValue() && !{{ disabled }} && !{{ readonly }}" class="absolute inset-y-0 right-0 flex items-center pr-3">
98
- <button
99
- type="button"
100
- @click="onClear()"
101
- class="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 focus:outline-none"
102
- aria-label="Clear input"
103
- >
104
- <svg class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
105
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
106
- </svg>
107
- </button>
108
- </div>
109
- @endif
110
- @if(rightIcon && !clearable)
111
- <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
112
- <span class="{{ iconClasses }} text-gray-400 dark:text-gray-500">{!! rightIcon !!}</span>
113
- </div>
114
- @endif
115
- </div>
116
-
117
- @if(helperText || (showCount && maxLength))
118
- <div class="flex items-center justify-between mt-2">
119
- @if(helperText)
120
- <p id="helper-text" class="{{ helperClasses }}">
121
- {{ helperText }}
122
- </p>
123
- @else
124
- <span></span>
125
- @endif
126
-
127
- @if(showCount && maxLength)
128
- <p class="text-sm text-gray-500 dark:text-gray-400" x-text="charCount()"></p>
129
- @endif
130
- </div>
131
- @endif
132
- </div>