@stacksjs/components 0.2.35 → 0.2.53
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/Accordion-19x8q4rj.stx +88 -0
- package/dist/{Audio-vdmg1258.stx → Audio-hjywk3qc.stx} +4 -4
- package/dist/{Avatar-365cvrh4.stx → Avatar-h3jmwcq0.stx} +23 -34
- package/dist/{Badge-8m3qete4.stx → Badge-k8dy3d53.stx} +11 -19
- package/dist/{Button-3tt4vsqt.stx → Button-1vknasy3.stx} +18 -33
- package/dist/Calendar-reny0et7.stx +189 -0
- package/dist/{Card-6wdbexqr.stx → Card-c1g1fd1w.stx} +27 -33
- package/dist/{Checkbox-p3q3e3nf.stx → Checkbox-zgdmy3p5.stx} +36 -44
- package/dist/{Checkout-e4037tyy.stx → Checkout-agt0ngpw.stx} +8 -16
- package/dist/Combobox-fdkr6cdq.stx +96 -0
- package/dist/{ComboboxButton-8gxwkhdx.stx → ComboboxButton-g76stp1e.stx} +14 -2
- package/dist/{ComboboxInput-mk99z65n.stx → ComboboxInput-z1293bbh.stx} +12 -5
- package/dist/ComboboxOption-hmxt3gte.stx +49 -0
- package/dist/{ListboxOptions-8kqvfp7z.stx → ComboboxOptions-sq5360ae.stx} +2 -2
- package/dist/CommandPalette-36mxf05w.stx +81 -0
- package/dist/CommandPaletteItem-mvk6q7a2.stx +27 -0
- package/dist/Dialog-hczpexs4.stx +37 -0
- package/dist/{Drawer-p7m351bn.stx → Drawer-xhevp5cw.stx} +29 -49
- package/dist/Dropdown-vvhemtc0.stx +82 -0
- package/dist/{DropdownButton-9w8zkfwj.stx → DropdownButton-wcz67dxy.stx} +12 -6
- package/dist/DropdownItem-c7rz08e5.stx +35 -0
- package/dist/{DropdownItems-aygjpxkx.stx → DropdownItems-1sxnd5qn.stx} +2 -2
- package/dist/EmailInput-vak3kkmk.stx +30 -0
- package/dist/Form-mas630hy.stx +95 -0
- package/dist/{Image-jhkm6w1f.stx → Image-yhxcvgzs.stx} +68 -100
- package/dist/Listbox-q49tnr90.stx +101 -0
- package/dist/{ListboxButton-spwx7513.stx → ListboxButton-keb8gm0q.stx} +12 -1
- package/dist/ListboxOption-e0dtv1gc.stx +49 -0
- package/dist/{ComboboxOptions-6b5ywc5x.stx → ListboxOptions-ejkatcfg.stx} +7 -3
- package/dist/{Login-rx9gyqhx.stx → Login-gepemsth.stx} +17 -19
- package/dist/Navigator-rk2a5wvk.stx +113 -0
- package/dist/Notification-tb9b525s.stx +78 -0
- package/dist/{NumberInput-nzpkx1ry.stx → NumberInput-drnvtqej.stx} +40 -53
- package/dist/{Pagination-h61eq390.stx → Pagination-n3d31cns.stx} +73 -108
- package/dist/PasswordInput-n02x6053.stx +142 -0
- package/dist/{PaymentMethods-j42jsk5x.stx → PaymentMethods-x4vrx9yb.stx} +21 -30
- package/dist/Popover-et2q44m7.stx +60 -0
- package/dist/{PopoverButton-fnaqc77g.stx → PopoverButton-mffzc9pr.stx} +12 -6
- package/dist/{PopoverPanel-y0vtf5w9.stx → PopoverPanel-054yh38h.stx} +6 -6
- package/dist/{Portal-s2q37yp0.stx → Portal-1vf14s4x.stx} +20 -37
- package/dist/{Progress-vmdkm06p.stx → Progress-yxcntr4x.stx} +36 -48
- package/dist/{Radio-04bs3d21.stx → Radio-cgjv2hb2.stx} +29 -34
- package/dist/RadioGroup-qxmnm3xm.stx +60 -0
- package/dist/RadioGroupOption-s1d6r221.stx +58 -0
- package/dist/SearchInput-pdx7agn0.stx +44 -0
- package/dist/{Select-g4mmvfv8.stx → Select-28y0pg83.stx} +23 -31
- package/dist/Sidebar-73vc78as.stx +236 -0
- package/dist/SidebarFooter-zsfsgcxb.stx +109 -0
- package/dist/SidebarHeader-7v174a9c.stx +122 -0
- package/dist/SidebarItem-3y3e8g2t.stx +92 -0
- package/dist/SidebarSection-z5ey6awe.stx +113 -0
- package/dist/{Signup-c4dyv6f3.stx → Signup-hxm5hwkr.stx} +26 -28
- package/dist/{StepperStep-en4r00zv.stx → StepperStep-wxgembat.stx} +9 -5
- package/dist/{SubscriptionCheckout-gawjzf3q.stx → SubscriptionCheckout-zy2ycrmw.stx} +99 -129
- package/dist/Switch-r5t5w04w.stx +70 -0
- package/dist/{TableHeader-ydys9rzz.stx → TableHeader-0eyzbqpz.stx} +10 -3
- package/dist/Tabs-bzvvn4ke.stx +126 -0
- package/dist/Teleport-tsx4bt4n.stx +65 -0
- package/dist/{TextInput-k2kfkgy1.stx → TextInput-kf084763.stx} +42 -59
- package/dist/{Textarea-szad57dz.stx → Textarea-xg4rr2xz.stx} +42 -54
- package/dist/{Tooltip-em7t551n.stx → Tooltip-m0tqd72e.stx} +34 -40
- package/dist/Transition-7smenaz8.stx +102 -0
- package/dist/{TwoFactorChallenge-an9yvxvq.stx → TwoFactorChallenge-y5m3nk79.stx} +49 -63
- package/dist/{Video-crxva65p.stx → Video-84j4pv6h.stx} +4 -4
- package/dist/VirtualList-fa7v5a3h.stx +67 -0
- package/dist/VirtualTable-f9crbfjr.stx +103 -0
- package/dist/components/CodeBlock.stx +1 -6
- package/dist/index.js +13 -13
- package/dist/ui/accordion/Accordion.stx +1 -3
- package/dist/ui/auth/Login.stx +0 -1
- package/dist/ui/auth/Signup.stx +0 -1
- package/dist/ui/auth/TwoFactorChallenge.stx +1 -3
- package/dist/ui/avatar/Avatar.stx +0 -2
- package/dist/ui/badge/Badge.stx +0 -1
- package/dist/ui/button/Button.stx +1 -2
- package/dist/ui/calendar/Calendar.stx +12 -2
- package/dist/ui/card/Card.stx +0 -2
- package/dist/ui/checkbox/Checkbox.stx +2 -2
- package/dist/ui/combobox/Combobox.stx +2 -1
- package/dist/ui/combobox/ComboboxButton.stx +1 -0
- package/dist/ui/combobox/ComboboxInput.stx +1 -4
- package/dist/ui/combobox/ComboboxOption.stx +2 -9
- package/dist/ui/combobox/ComboboxOptions.stx +1 -1
- package/dist/ui/command-palette/CommandPalette.stx +3 -6
- package/dist/ui/command-palette/CommandPaletteItem.stx +2 -7
- package/dist/ui/dialog/Dialog.stx +1 -3
- package/dist/ui/drawer/Drawer.stx +1 -3
- package/dist/ui/dropdown/Dropdown.stx +2 -5
- package/dist/ui/dropdown/DropdownButton.stx +1 -3
- package/dist/ui/dropdown/DropdownItem.stx +2 -7
- package/dist/ui/dropdown/DropdownItems.stx +1 -1
- package/dist/ui/form/Form.stx +0 -5
- package/dist/ui/image/Image.stx +1 -6
- package/dist/ui/input/EmailInput.stx +1 -1
- package/dist/ui/input/NumberInput.stx +0 -1
- package/dist/ui/input/PasswordInput.stx +1 -4
- package/dist/ui/input/SearchInput.stx +2 -2
- package/dist/ui/input/TextInput.stx +1 -2
- package/dist/ui/listbox/Listbox.stx +2 -1
- package/dist/ui/listbox/ListboxButton.stx +1 -0
- package/dist/ui/listbox/ListboxOption.stx +2 -9
- package/dist/ui/listbox/ListboxOptions.stx +1 -1
- package/dist/ui/navigator/Navigator.stx +4 -4
- package/dist/ui/notification/Notification.stx +1 -5
- package/dist/ui/pagination/Pagination.stx +1 -2
- package/dist/ui/payment/Checkout.stx +0 -4
- package/dist/ui/payment/PaymentMethods.stx +15 -2
- package/dist/ui/payment/SubscriptionCheckout.stx +3 -184
- package/dist/ui/popover/Popover.stx +2 -5
- package/dist/ui/popover/PopoverButton.stx +1 -3
- package/dist/ui/popover/PopoverPanel.stx +1 -1
- package/dist/ui/portal/Portal.stx +1 -0
- package/dist/ui/progress/Progress.stx +6 -1
- package/dist/ui/radio/Radio.stx +1 -1
- package/dist/ui/radio-group/RadioGroup.stx +2 -1
- package/dist/ui/radio-group/RadioGroupOption.stx +2 -9
- package/dist/ui/select/Select.stx +1 -1
- package/dist/ui/sidebar/Sidebar.stx +9 -26
- package/dist/ui/sidebar/SidebarFooter.stx +6 -21
- package/dist/ui/sidebar/SidebarHeader.stx +7 -18
- package/dist/ui/sidebar/SidebarItem.stx +4 -20
- package/dist/ui/sidebar/SidebarSection.stx +5 -18
- package/dist/ui/sidebar/index.d.ts +13 -1
- package/dist/ui/stepper/StepperStep.stx +1 -5
- package/dist/ui/switch/Switch.stx +4 -10
- package/dist/ui/table/TableHeader.stx +1 -1
- package/dist/ui/tabs/Tabs.stx +0 -1
- package/dist/ui/teleport/Teleport.stx +1 -0
- package/dist/ui/textarea/Textarea.stx +1 -1
- package/dist/ui/tooltip/Tooltip.stx +1 -2
- package/dist/ui/transition/Transition.stx +9 -16
- package/dist/ui/virtual-list/VirtualList.stx +2 -4
- package/dist/ui/virtual-table/VirtualTable.stx +1 -1
- package/package.json +3 -3
- package/src/components/CodeBlock.stx +26 -29
- package/src/components/Footer.stx +1 -1
- package/src/components/Hero.stx +1 -1
- package/src/components/Installation.stx +5 -7
- package/src/ui/accordion/Accordion.stx +48 -68
- package/src/ui/audio/Audio.stx +4 -4
- package/src/ui/auth/Login.stx +17 -19
- package/src/ui/auth/Signup.stx +26 -28
- package/src/ui/auth/TwoFactorChallenge.stx +49 -63
- package/src/ui/avatar/Avatar.stx +23 -34
- package/src/ui/badge/Badge.stx +11 -19
- package/src/ui/button/Button.stx +18 -33
- package/src/ui/calendar/Calendar.stx +93 -136
- package/src/ui/card/Card.stx +27 -33
- package/src/ui/checkbox/Checkbox.stx +36 -44
- package/src/ui/combobox/Combobox.stx +87 -4
- package/src/ui/combobox/ComboboxButton.stx +14 -2
- package/src/ui/combobox/ComboboxInput.stx +12 -5
- package/src/ui/combobox/ComboboxOption.stx +29 -32
- package/src/ui/combobox/ComboboxOptions.stx +7 -3
- package/src/ui/command-palette/CommandPalette.stx +70 -28
- package/src/ui/command-palette/CommandPaletteItem.stx +15 -20
- package/src/ui/dialog/Dialog.stx +28 -10
- package/src/ui/drawer/Drawer.stx +29 -49
- package/src/ui/dropdown/Dropdown.stx +69 -11
- package/src/ui/dropdown/DropdownButton.stx +12 -6
- package/src/ui/dropdown/DropdownItem.stx +18 -26
- package/src/ui/dropdown/DropdownItems.stx +2 -2
- package/src/ui/form/Form.stx +50 -164
- package/src/ui/heatmap/Heatmap.stx +16 -37
- package/src/ui/image/Image.stx +68 -100
- package/src/ui/input/EmailInput.stx +16 -27
- package/src/ui/input/NumberInput.stx +40 -53
- package/src/ui/input/PasswordInput.stx +64 -77
- package/src/ui/input/SearchInput.stx +27 -46
- package/src/ui/input/TextInput.stx +42 -59
- package/src/ui/listbox/Listbox.stx +91 -4
- package/src/ui/listbox/ListboxButton.stx +12 -1
- package/src/ui/listbox/ListboxOption.stx +30 -38
- package/src/ui/listbox/ListboxOptions.stx +2 -2
- package/src/ui/navigator/Navigator.stx +52 -77
- package/src/ui/notification/Notification.stx +46 -39
- package/src/ui/pagination/Pagination.stx +73 -108
- package/src/ui/payment/Checkout.stx +8 -16
- package/src/ui/payment/PaymentMethods.stx +21 -30
- package/src/ui/payment/SubscriptionCheckout.stx +99 -129
- package/src/ui/popover/Popover.stx +50 -14
- package/src/ui/popover/PopoverButton.stx +12 -6
- package/src/ui/popover/PopoverPanel.stx +6 -6
- package/src/ui/portal/Portal.stx +20 -37
- package/src/ui/progress/Progress.stx +36 -48
- package/src/ui/radio/Radio.stx +29 -34
- package/src/ui/radio-group/RadioGroup.stx +51 -4
- package/src/ui/radio-group/RadioGroupOption.stx +37 -30
- package/src/ui/select/Select.stx +23 -31
- package/src/ui/sidebar/Sidebar.stx +136 -188
- package/src/ui/sidebar/SidebarFooter.stx +49 -35
- package/src/ui/sidebar/SidebarHeader.stx +52 -30
- package/src/ui/sidebar/SidebarItem.stx +52 -78
- package/src/ui/sidebar/SidebarSection.stx +56 -51
- package/src/ui/sidebar/index.ts +21 -1
- package/src/ui/stepper/StepperStep.stx +9 -5
- package/src/ui/switch/Switch.stx +38 -42
- package/src/ui/table/TableHeader.stx +10 -3
- package/src/ui/tabs/Tabs.stx +68 -87
- package/src/ui/teleport/Teleport.stx +23 -59
- package/src/ui/textarea/Textarea.stx +42 -54
- package/src/ui/tooltip/Tooltip.stx +34 -40
- package/src/ui/transition/Transition.stx +76 -148
- package/src/ui/video/Video.stx +4 -4
- package/src/ui/virtual-list/VirtualList.stx +45 -61
- package/src/ui/virtual-table/VirtualTable.stx +62 -72
- package/dist/Accordion-sbfra0mz.stx +0 -108
- package/dist/Calendar-58nnkdh8.stx +0 -232
- package/dist/Combobox-th5ebw62.stx +0 -13
- package/dist/ComboboxOption-an9zyan6.stx +0 -52
- package/dist/CommandPalette-kwmdf2np.stx +0 -39
- package/dist/CommandPaletteItem-zx2fbpts.stx +0 -32
- package/dist/Dialog-rgmj8q2j.stx +0 -19
- package/dist/Dropdown-jz4jy0n3.stx +0 -24
- package/dist/DropdownItem-wagdspb8.stx +0 -43
- package/dist/EmailInput-ajsp9vw6.stx +0 -41
- package/dist/Form-1mdmx9b6.stx +0 -209
- package/dist/Listbox-pqqh72g8.stx +0 -14
- package/dist/ListboxOption-7nxzaexq.stx +0 -57
- package/dist/Navigator-aay8bjab.stx +0 -138
- package/dist/Notification-gk33gv7b.stx +0 -71
- package/dist/PasswordInput-zv4hm43j.stx +0 -155
- package/dist/Popover-469gbt6r.stx +0 -24
- package/dist/RadioGroup-bvb1mrhd.stx +0 -13
- package/dist/RadioGroupOption-cpdwgnhw.stx +0 -51
- package/dist/SearchInput-2521m475.stx +0 -63
- package/dist/Sidebar-40ckbzh8.stx +0 -288
- package/dist/SidebarFooter-hnth56mg.stx +0 -95
- package/dist/SidebarHeader-3wrte2p2.stx +0 -100
- package/dist/SidebarItem-zqtvvzpc.stx +0 -118
- package/dist/SidebarSection-3etwnbjr.stx +0 -108
- package/dist/Switch-036j9vm1.stx +0 -74
- package/dist/Tabs-svh721m0.stx +0 -145
- package/dist/Teleport-qfch3w0b.stx +0 -101
- package/dist/Transition-0hmr6zxb.stx +0 -174
- package/dist/VirtualList-cndhjejh.stx +0 -83
- package/dist/VirtualTable-ng0b48yp.stx +0 -113
package/src/ui/image/Image.stx
CHANGED
|
@@ -10,18 +10,14 @@ export const height = $props.height
|
|
|
10
10
|
export const lazy = $props.lazy ?? true
|
|
11
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
12
|
export const blurDataURL = $props.blurDataURL || ''
|
|
13
|
-
export const onLoad = $props.onLoad
|
|
14
|
-
export const onError = $props.onError
|
|
15
13
|
export const aspectRatio = $props.aspectRatio
|
|
16
14
|
export const objectFit = $props.objectFit || 'cover'
|
|
17
15
|
export const rounded = $props.rounded ?? false
|
|
18
16
|
export const zoomOnHover = $props.zoomOnHover || false
|
|
19
17
|
export const className = $props.className || ''
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
export const hasWebP = webpSrc || webpSrcSet
|
|
19
|
+
export const hasWebP = !!(webpSrc || webpSrcSet)
|
|
23
20
|
|
|
24
|
-
// Object fit classes
|
|
25
21
|
const objectFitClasses = {
|
|
26
22
|
contain: 'object-contain',
|
|
27
23
|
cover: 'object-cover',
|
|
@@ -30,23 +26,69 @@ const objectFitClasses = {
|
|
|
30
26
|
'scale-down': 'object-scale-down',
|
|
31
27
|
}
|
|
32
28
|
|
|
33
|
-
// Rounded classes
|
|
34
29
|
const roundedClass = rounded === true ? 'rounded-lg' : rounded === 'full' ? 'rounded-full' : rounded ? `rounded-${rounded}` : ''
|
|
35
|
-
|
|
36
30
|
const zoomClasses = zoomOnHover ? 'transform hover:scale-110 transition-transform duration-300 ease-in-out cursor-zoom-in' : ''
|
|
37
31
|
|
|
38
32
|
export const containerClasses = `relative overflow-hidden ${roundedClass} ${className}`.trim()
|
|
39
33
|
export const imageClasses = `w-full h-full transition-opacity duration-300 ${objectFitClasses[objectFit]} ${zoomClasses}`.trim()
|
|
40
|
-
export const placeholderClasses = `absolute inset-0 bg-gray-200 dark:bg-gray-700 ${roundedClass}`.trim()
|
|
41
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
|
+
}
|
|
42
51
|
|
|
43
|
-
|
|
44
|
-
|
|
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
|
+
})
|
|
45
88
|
</script>
|
|
46
89
|
|
|
47
|
-
<div class="{{ containerClasses }}" style="{{
|
|
90
|
+
<div class="{{ containerClasses }}" style="{{ containerStyle }}">
|
|
48
91
|
@if(blurDataURL)
|
|
49
|
-
<!-- Blur placeholder -->
|
|
50
92
|
<img
|
|
51
93
|
src="{{ blurDataURL }}"
|
|
52
94
|
alt=""
|
|
@@ -56,17 +98,13 @@ export const paddingBottom = aspectRatio ? `${(1 / aspectRatio) * 100}%` : undef
|
|
|
56
98
|
@endif
|
|
57
99
|
|
|
58
100
|
@if(hasWebP)
|
|
59
|
-
<!-- Picture element with WebP support -->
|
|
60
101
|
<picture>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
/>
|
|
68
|
-
@endif
|
|
69
|
-
|
|
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
|
+
/>
|
|
70
108
|
@if(lazy)
|
|
71
109
|
<img
|
|
72
110
|
src="{{ placeholder }}"
|
|
@@ -78,8 +116,8 @@ export const paddingBottom = aspectRatio ? `${(1 / aspectRatio) * 100}%` : undef
|
|
|
78
116
|
width="{{ width }}"
|
|
79
117
|
height="{{ height }}"
|
|
80
118
|
loading="lazy"
|
|
81
|
-
|
|
82
|
-
|
|
119
|
+
@load="onImageLoad($event)"
|
|
120
|
+
@error="onImageError($event)"
|
|
83
121
|
/>
|
|
84
122
|
@else
|
|
85
123
|
<img
|
|
@@ -90,13 +128,12 @@ export const paddingBottom = aspectRatio ? `${(1 / aspectRatio) * 100}%` : undef
|
|
|
90
128
|
class="{{ imageClasses }}"
|
|
91
129
|
width="{{ width }}"
|
|
92
130
|
height="{{ height }}"
|
|
93
|
-
|
|
94
|
-
|
|
131
|
+
@load="onImageLoad($event)"
|
|
132
|
+
@error="onImageError($event)"
|
|
95
133
|
/>
|
|
96
134
|
@endif
|
|
97
135
|
</picture>
|
|
98
136
|
@else
|
|
99
|
-
<!-- Regular img element -->
|
|
100
137
|
@if(lazy)
|
|
101
138
|
<img
|
|
102
139
|
src="{{ placeholder }}"
|
|
@@ -108,8 +145,8 @@ export const paddingBottom = aspectRatio ? `${(1 / aspectRatio) * 100}%` : undef
|
|
|
108
145
|
width="{{ width }}"
|
|
109
146
|
height="{{ height }}"
|
|
110
147
|
loading="lazy"
|
|
111
|
-
|
|
112
|
-
|
|
148
|
+
@load="onImageLoad($event)"
|
|
149
|
+
@error="onImageError($event)"
|
|
113
150
|
/>
|
|
114
151
|
@else
|
|
115
152
|
<img
|
|
@@ -120,78 +157,9 @@ export const paddingBottom = aspectRatio ? `${(1 / aspectRatio) * 100}%` : undef
|
|
|
120
157
|
class="{{ imageClasses }}"
|
|
121
158
|
width="{{ width }}"
|
|
122
159
|
height="{{ height }}"
|
|
123
|
-
|
|
124
|
-
|
|
160
|
+
@load="onImageLoad($event)"
|
|
161
|
+
@error="onImageError($event)"
|
|
125
162
|
/>
|
|
126
163
|
@endif
|
|
127
164
|
@endif
|
|
128
|
-
|
|
129
|
-
<!-- Loading skeleton -->
|
|
130
|
-
<div class="{{ placeholderClasses }} skeleton" style="display: none;">
|
|
131
|
-
<div class="animate-pulse w-full h-full bg-gray-300 dark:bg-gray-600"></div>
|
|
132
|
-
</div>
|
|
133
165
|
</div>
|
|
134
|
-
|
|
135
|
-
<script>
|
|
136
|
-
// Intersection Observer for lazy loading
|
|
137
|
-
if (lazy && typeof window !== 'undefined' && 'IntersectionObserver' in window) {
|
|
138
|
-
const lazyImages = document.querySelectorAll('.lazy-image')
|
|
139
|
-
|
|
140
|
-
const imageObserver = new IntersectionObserver((entries, observer) => {
|
|
141
|
-
entries.forEach(entry => {
|
|
142
|
-
if (entry.isIntersecting) {
|
|
143
|
-
const img = entry.target
|
|
144
|
-
const realSrc = img.getAttribute('data-src')
|
|
145
|
-
const realSrcSet = img.getAttribute('data-srcset')
|
|
146
|
-
|
|
147
|
-
if (realSrc) {
|
|
148
|
-
// Load the real image
|
|
149
|
-
const tempImg = new Image()
|
|
150
|
-
tempImg.onload = () => {
|
|
151
|
-
img.src = realSrc
|
|
152
|
-
if (realSrcSet) {
|
|
153
|
-
img.srcset = realSrcSet
|
|
154
|
-
}
|
|
155
|
-
img.classList.remove('opacity-0')
|
|
156
|
-
img.classList.add('opacity-100')
|
|
157
|
-
observer.unobserve(img)
|
|
158
|
-
}
|
|
159
|
-
tempImg.onerror = () => {
|
|
160
|
-
handleImageError({ target: img })
|
|
161
|
-
observer.unobserve(img)
|
|
162
|
-
}
|
|
163
|
-
tempImg.src = realSrc
|
|
164
|
-
if (realSrcSet) {
|
|
165
|
-
tempImg.srcset = realSrcSet
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
})
|
|
170
|
-
}, {
|
|
171
|
-
rootMargin: '50px', // Start loading 50px before entering viewport
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
lazyImages.forEach(img => imageObserver.observe(img))
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function handleImageLoad(event) {
|
|
178
|
-
const img = event.target
|
|
179
|
-
img.classList.remove('opacity-0')
|
|
180
|
-
img.classList.add('opacity-100')
|
|
181
|
-
|
|
182
|
-
if (onLoad) {
|
|
183
|
-
onLoad(event)
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function handleImageError(event) {
|
|
188
|
-
const img = event.target
|
|
189
|
-
|
|
190
|
-
// Show a broken image placeholder
|
|
191
|
-
img.src = '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'
|
|
192
|
-
|
|
193
|
-
if (onError) {
|
|
194
|
-
onError(event)
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
</script>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script server>
|
|
2
2
|
export const value = $props.value || ''
|
|
3
|
-
export const onChange = $props.onChange || (() => {})
|
|
4
3
|
export const placeholder = $props.placeholder || 'Enter your email'
|
|
5
4
|
export const disabled = $props.disabled || false
|
|
6
5
|
export const readonly = $props.readonly || false
|
|
@@ -11,31 +10,21 @@ export const helperText = $props.helperText || ''
|
|
|
11
10
|
export const required = $props.required || false
|
|
12
11
|
export const className = $props.className || ''
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
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>'
|
|
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>'
|
|
16
14
|
</script>
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
value
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
})
|
|
34
|
-
@endcomponent
|
|
35
|
-
|
|
36
|
-
<script>
|
|
37
|
-
// Add additional email validation logic if needed
|
|
38
|
-
function validateEmail(email) {
|
|
39
|
-
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
|
|
40
|
-
}
|
|
41
|
-
</script>
|
|
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,6 +1,5 @@
|
|
|
1
1
|
<script server>
|
|
2
2
|
export const value = $props.value || ''
|
|
3
|
-
export const onChange = $props.onChange || (() => {})
|
|
4
3
|
export const placeholder = $props.placeholder || '0'
|
|
5
4
|
export const disabled = $props.disabled || false
|
|
6
5
|
export const readonly = $props.readonly || false
|
|
@@ -16,26 +15,16 @@ export const showControls = $props.showControls ?? true
|
|
|
16
15
|
export const className = $props.className || ''
|
|
17
16
|
export const inputClassName = $props.inputClassName || ''
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
const sizeClasses = {
|
|
21
|
-
sm: 'px-3 py-1.5 text-sm h-8',
|
|
22
|
-
md: 'px-4 py-2 text-base h-10',
|
|
23
|
-
lg: 'px-5 py-3 text-lg h-12',
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Base and state classes
|
|
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' }
|
|
27
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'
|
|
28
|
-
|
|
29
20
|
const stateClasses = error
|
|
30
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'
|
|
31
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'
|
|
32
|
-
|
|
33
23
|
const disabledClasses = disabled
|
|
34
24
|
? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
|
|
35
25
|
: readonly
|
|
36
26
|
? 'bg-gray-50 dark:bg-gray-800 cursor-default'
|
|
37
27
|
: 'bg-white dark:bg-blue-gray-700'
|
|
38
|
-
|
|
39
28
|
const paddingClasses = showControls ? 'pr-10' : ''
|
|
40
29
|
|
|
41
30
|
export const inputClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses} ${paddingClasses} ${inputClassName}`.trim()
|
|
@@ -43,6 +32,37 @@ export const labelClasses = `block text-sm font-medium leading-6 mb-2 ${error ?
|
|
|
43
32
|
export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
|
|
44
33
|
</script>
|
|
45
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
|
+
const inputValue = state({{ value }})
|
|
41
|
+
|
|
42
|
+
function onInput(event) {
|
|
43
|
+
inputValue.set(event.target.value)
|
|
44
|
+
emit('input', inputValue())
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function increment() {
|
|
48
|
+
const current = Number.parseFloat(inputValue()) || 0
|
|
49
|
+
const next = current + stepSize
|
|
50
|
+
if (maxVal !== undefined && next > maxVal) return
|
|
51
|
+
inputValue.set(String(next))
|
|
52
|
+
emit('input', String(next))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function decrement() {
|
|
56
|
+
const current = Number.parseFloat(inputValue()) || 0
|
|
57
|
+
const next = current - stepSize
|
|
58
|
+
if (minVal !== undefined && next < minVal) return
|
|
59
|
+
inputValue.set(String(next))
|
|
60
|
+
emit('input', String(next))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
defineExpose({ value: inputValue, increment, decrement })
|
|
64
|
+
</script>
|
|
65
|
+
|
|
46
66
|
<div class="w-full {{ className }}">
|
|
47
67
|
@if(label)
|
|
48
68
|
<label for="number-input" class="{{ labelClasses }}">
|
|
@@ -58,25 +78,24 @@ export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red
|
|
|
58
78
|
type="number"
|
|
59
79
|
id="number-input"
|
|
60
80
|
class="{{ inputClasses }}"
|
|
61
|
-
value="
|
|
81
|
+
:value="inputValue()"
|
|
62
82
|
placeholder="{{ placeholder }}"
|
|
63
|
-
disabled
|
|
64
|
-
readonly
|
|
65
|
-
required
|
|
83
|
+
@if(disabled)disabled@endif
|
|
84
|
+
@if(readonly)readonly@endif
|
|
85
|
+
@if(required)required@endif
|
|
66
86
|
@if(min !== undefined)min="{{ min }}"@endif
|
|
67
87
|
@if(max !== undefined)max="{{ max }}"@endif
|
|
68
88
|
step="{{ step }}"
|
|
69
|
-
|
|
70
|
-
aria-invalid="{{ error ? 'true' : 'false' }}"
|
|
89
|
+
@input="onInput($event)"
|
|
90
|
+
:aria-invalid="{{ error ? 'true' : 'false' }}"
|
|
71
91
|
@if(helperText)aria-describedby="helper-text"@endif
|
|
72
92
|
/>
|
|
73
93
|
|
|
74
94
|
@if(showControls && !disabled && !readonly)
|
|
75
|
-
<!-- Increment/Decrement controls -->
|
|
76
95
|
<div class="absolute inset-y-0 right-0 flex flex-col">
|
|
77
96
|
<button
|
|
78
97
|
type="button"
|
|
79
|
-
@click="
|
|
98
|
+
@click="increment()"
|
|
80
99
|
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"
|
|
81
100
|
aria-label="Increment"
|
|
82
101
|
>
|
|
@@ -86,7 +105,7 @@ export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red
|
|
|
86
105
|
</button>
|
|
87
106
|
<button
|
|
88
107
|
type="button"
|
|
89
|
-
@click="
|
|
108
|
+
@click="decrement()"
|
|
90
109
|
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"
|
|
91
110
|
aria-label="Decrement"
|
|
92
111
|
>
|
|
@@ -104,35 +123,3 @@ export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red
|
|
|
104
123
|
</p>
|
|
105
124
|
@endif
|
|
106
125
|
</div>
|
|
107
|
-
|
|
108
|
-
<script>
|
|
109
|
-
function handleInput(event) {
|
|
110
|
-
const newValue = event.target.value
|
|
111
|
-
|
|
112
|
-
if (onChange) {
|
|
113
|
-
onChange(newValue)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function handleIncrement() {
|
|
118
|
-
const currentValue = parseFloat(value) || 0
|
|
119
|
-
const newValue = currentValue + step
|
|
120
|
-
|
|
121
|
-
if (max !== undefined && newValue > max) return
|
|
122
|
-
|
|
123
|
-
if (onChange) {
|
|
124
|
-
onChange(String(newValue))
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function handleDecrement() {
|
|
129
|
-
const currentValue = parseFloat(value) || 0
|
|
130
|
-
const newValue = currentValue - step
|
|
131
|
-
|
|
132
|
-
if (min !== undefined && newValue < min) return
|
|
133
|
-
|
|
134
|
-
if (onChange) {
|
|
135
|
-
onChange(String(newValue))
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
</script>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script server>
|
|
2
2
|
export const value = $props.value || ''
|
|
3
|
-
export const onChange = $props.onChange || (() => {})
|
|
4
3
|
export const placeholder = $props.placeholder || 'Enter your password'
|
|
5
4
|
export const disabled = $props.disabled || false
|
|
6
5
|
export const readonly = $props.readonly || false
|
|
@@ -13,30 +12,13 @@ export const showStrength = $props.showStrength || false
|
|
|
13
12
|
export const className = $props.className || ''
|
|
14
13
|
export const inputClassName = $props.inputClassName || ''
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
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' }
|
|
18
17
|
|
|
19
|
-
// Size classes
|
|
20
|
-
const sizeClasses = {
|
|
21
|
-
sm: 'px-3 py-1.5 text-sm h-8',
|
|
22
|
-
md: 'px-4 py-2 text-base h-10',
|
|
23
|
-
lg: 'px-5 py-3 text-lg h-12',
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Icon size classes
|
|
27
|
-
const iconSizeClasses = {
|
|
28
|
-
sm: 'h-4 w-4',
|
|
29
|
-
md: 'h-5 w-5',
|
|
30
|
-
lg: 'h-6 w-6',
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Base and state classes
|
|
34
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'
|
|
35
|
-
|
|
36
19
|
const stateClasses = error
|
|
37
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'
|
|
38
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'
|
|
39
|
-
|
|
40
22
|
const disabledClasses = disabled
|
|
41
23
|
? 'opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800'
|
|
42
24
|
: readonly
|
|
@@ -47,22 +29,50 @@ export const inputClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses}
|
|
|
47
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()
|
|
48
30
|
export const helperClasses = `mt-2 text-sm ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
|
|
49
31
|
export const iconClasses = iconSizeClasses[size]
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<script client>
|
|
35
|
+
const emit = defineEmits()
|
|
36
|
+
const showPassword = state(false)
|
|
37
|
+
const inputValue = state({{ value }})
|
|
38
|
+
|
|
39
|
+
function onInput(event) {
|
|
40
|
+
inputValue.set(event.target.value)
|
|
41
|
+
emit('input', inputValue())
|
|
42
|
+
}
|
|
50
43
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
if (
|
|
59
|
-
if (
|
|
60
|
-
|
|
44
|
+
function toggleVisibility() {
|
|
45
|
+
showPassword.set(!showPassword())
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function calcStrength(pw) {
|
|
49
|
+
if (!pw) return 0
|
|
50
|
+
let s = 0
|
|
51
|
+
if (pw.length >= 8) s += 25
|
|
52
|
+
if (pw.length >= 12) s += 25
|
|
53
|
+
if (/[a-z]/.test(pw) && /[A-Z]/.test(pw)) s += 25
|
|
54
|
+
if (/\d/.test(pw)) s += 15
|
|
55
|
+
if (/[^a-zA-Z\d]/.test(pw)) s += 10
|
|
56
|
+
return Math.min(s, 100)
|
|
61
57
|
}
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
const strength = derived(() => calcStrength(inputValue()))
|
|
60
|
+
const strengthText = derived(() => {
|
|
61
|
+
const s = strength()
|
|
62
|
+
return s < 40 ? 'Weak' : s < 70 ? 'Medium' : 'Strong'
|
|
63
|
+
})
|
|
64
|
+
const strengthColor = derived(() => {
|
|
65
|
+
const s = strength()
|
|
66
|
+
return s < 40 ? 'bg-red-500' : s < 70 ? 'bg-yellow-500' : 'bg-green-500'
|
|
67
|
+
})
|
|
68
|
+
const strengthLabelColor = derived(() => {
|
|
69
|
+
const s = strength()
|
|
70
|
+
return s < 40 ? 'text-red-600' : s < 70 ? 'text-yellow-600' : 'text-green-600'
|
|
71
|
+
})
|
|
72
|
+
const inputType = derived(() => showPassword() ? 'text' : 'password')
|
|
73
|
+
const toggleAriaLabel = derived(() => showPassword() ? 'Hide password' : 'Show password')
|
|
74
|
+
|
|
75
|
+
defineExpose({ value: inputValue })
|
|
66
76
|
</script>
|
|
67
77
|
|
|
68
78
|
<div class="w-full {{ className }}">
|
|
@@ -77,56 +87,48 @@ export const strengthText = strength < 40 ? 'Weak' : strength < 70 ? 'Medium' :
|
|
|
77
87
|
|
|
78
88
|
<div class="relative">
|
|
79
89
|
<input
|
|
80
|
-
type="
|
|
90
|
+
:type="inputType()"
|
|
81
91
|
id="password-input"
|
|
82
92
|
class="{{ inputClasses }}"
|
|
83
|
-
value="
|
|
93
|
+
:value="inputValue()"
|
|
84
94
|
placeholder="{{ placeholder }}"
|
|
85
|
-
disabled
|
|
86
|
-
readonly
|
|
87
|
-
required
|
|
88
|
-
|
|
89
|
-
aria-invalid="{{ error ? 'true' : 'false' }}"
|
|
95
|
+
@if(disabled)disabled@endif
|
|
96
|
+
@if(readonly)readonly@endif
|
|
97
|
+
@if(required)required@endif
|
|
98
|
+
@input="onInput($event)"
|
|
99
|
+
:aria-invalid="{{ error ? 'true' : 'false' }}"
|
|
90
100
|
@if(helperText)aria-describedby="helper-text"@endif
|
|
91
101
|
/>
|
|
92
102
|
|
|
93
|
-
<!-- Toggle visibility button -->
|
|
94
103
|
<div class="absolute inset-y-0 right-0 flex items-center pr-3">
|
|
95
104
|
<button
|
|
96
105
|
type="button"
|
|
97
106
|
@click="toggleVisibility()"
|
|
98
107
|
class="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 focus:outline-none"
|
|
99
|
-
aria-label="
|
|
108
|
+
:aria-label="toggleAriaLabel()"
|
|
100
109
|
>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
<svg class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
109
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
110
|
-
<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" />
|
|
111
|
-
</svg>
|
|
112
|
-
@endif
|
|
110
|
+
<svg :show="showPassword()" class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
111
|
+
<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" />
|
|
112
|
+
</svg>
|
|
113
|
+
<svg :show="!showPassword()" class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
114
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
115
|
+
<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" />
|
|
116
|
+
</svg>
|
|
113
117
|
</button>
|
|
114
118
|
</div>
|
|
115
119
|
</div>
|
|
116
120
|
|
|
117
|
-
@if(showStrength
|
|
118
|
-
|
|
119
|
-
<div class="mt-2">
|
|
121
|
+
@if(showStrength)
|
|
122
|
+
<div :show="inputValue().length > 0" class="mt-2">
|
|
120
123
|
<div class="flex items-center justify-between mb-1">
|
|
121
124
|
<span class="text-xs text-gray-600 dark:text-gray-400">Password strength</span>
|
|
122
|
-
<span class="text-xs font-medium
|
|
123
|
-
{{ strengthText }}
|
|
124
|
-
</span>
|
|
125
|
+
<span class="text-xs font-medium" :class="strengthLabelColor()" x-text="strengthText()"></span>
|
|
125
126
|
</div>
|
|
126
127
|
<div class="w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
|
|
127
128
|
<div
|
|
128
|
-
class="
|
|
129
|
-
|
|
129
|
+
class="h-full transition-all duration-300 rounded-full"
|
|
130
|
+
:class="strengthColor()"
|
|
131
|
+
:style="`width: ${strength()}%`"
|
|
130
132
|
></div>
|
|
131
133
|
</div>
|
|
132
134
|
</div>
|
|
@@ -138,18 +140,3 @@ export const strengthText = strength < 40 ? 'Weak' : strength < 70 ? 'Medium' :
|
|
|
138
140
|
</p>
|
|
139
141
|
@endif
|
|
140
142
|
</div>
|
|
141
|
-
|
|
142
|
-
<script>
|
|
143
|
-
function handleInput(event) {
|
|
144
|
-
const newValue = event.target.value
|
|
145
|
-
|
|
146
|
-
if (onChange) {
|
|
147
|
-
onChange(newValue)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function toggleVisibility() {
|
|
152
|
-
showPassword = !showPassword
|
|
153
|
-
// Re-render would be handled by the framework
|
|
154
|
-
}
|
|
155
|
-
</script>
|