@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
package/src/ui/image/Image.stx
DELETED
|
@@ -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>
|
package/src/ui/image/README.md
DELETED
|
@@ -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
|
-
```
|
package/src/ui/image/index.ts
DELETED
|
@@ -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>
|