@stacksjs/components 0.2.35 → 0.2.54
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 +5 -5
- 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
|
@@ -5,99 +5,84 @@ export const siblingCount = $props.siblingCount || 1
|
|
|
5
5
|
export const showFirstLast = $props.showFirstLast ?? true
|
|
6
6
|
export const showPrevNext = $props.showPrevNext ?? true
|
|
7
7
|
export const className = $props.className || ''
|
|
8
|
-
export const onChange = $props.onChange
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (totalPages <= totalBlocks) {
|
|
17
|
-
// Show all pages
|
|
18
|
-
for (let i = 1; i <= totalPages; i++) {
|
|
19
|
-
pages.push(i)
|
|
20
|
-
}
|
|
21
|
-
} else {
|
|
22
|
-
const leftSiblingIndex = Math.max(currentPage - siblingCount, 1)
|
|
23
|
-
const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages)
|
|
9
|
+
export const paginationClasses = `flex items-center gap-1 ${className}`.trim()
|
|
10
|
+
export const buttonClasses = `px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white dark:disabled:hover:bg-gray-800 transition-colors duration-150`.trim()
|
|
11
|
+
export const activeButtonClasses = `px-3 py-2 text-sm font-medium text-white bg-blue-600 dark:bg-blue-500 border border-blue-600 dark:border-blue-500 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900`.trim()
|
|
12
|
+
export const ellipsisClasses = `px-3 py-2 text-sm font-medium text-gray-400 dark:text-gray-600`.trim()
|
|
13
|
+
export const iconClasses = `w-5 h-5`.trim()
|
|
14
|
+
</script>
|
|
24
15
|
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
<script client>
|
|
17
|
+
const emit = defineEmits()
|
|
18
|
+
const totalPages = {{ totalPages }}
|
|
19
|
+
const siblingCount = {{ siblingCount }}
|
|
20
|
+
const page = state({{ currentPage }})
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
pages.push(i)
|
|
33
|
-
}
|
|
34
|
-
pages.push('ellipsis-right')
|
|
35
|
-
pages.push(totalPages)
|
|
36
|
-
} else if (showLeftEllipsis && !showRightEllipsis) {
|
|
37
|
-
// Show first + ellipsis + right pages
|
|
38
|
-
pages.push(1)
|
|
39
|
-
pages.push('ellipsis-left')
|
|
40
|
-
const rightCount = 3 + 2 * siblingCount
|
|
41
|
-
for (let i = totalPages - rightCount + 1; i <= totalPages; i++) {
|
|
42
|
-
pages.push(i)
|
|
43
|
-
}
|
|
44
|
-
} else {
|
|
45
|
-
// Show first + ellipsis + middle + ellipsis + last
|
|
46
|
-
pages.push(1)
|
|
47
|
-
pages.push('ellipsis-left')
|
|
48
|
-
for (let i = leftSiblingIndex; i <= rightSiblingIndex; i++) {
|
|
49
|
-
pages.push(i)
|
|
50
|
-
}
|
|
51
|
-
pages.push('ellipsis-right')
|
|
52
|
-
pages.push(totalPages)
|
|
53
|
-
}
|
|
22
|
+
function goToPage(target) {
|
|
23
|
+
if (target >= 1 && target <= totalPages && target !== page()) {
|
|
24
|
+
page.set(target)
|
|
25
|
+
emit('change', target)
|
|
54
26
|
}
|
|
55
|
-
|
|
56
|
-
return pages
|
|
57
27
|
}
|
|
58
28
|
|
|
59
|
-
|
|
29
|
+
function goToPrevious() { goToPage(page() - 1) }
|
|
30
|
+
function goToNext() { goToPage(page() + 1) }
|
|
31
|
+
function goToFirst() { goToPage(1) }
|
|
32
|
+
function goToLast() { goToPage(totalPages) }
|
|
60
33
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
}
|
|
34
|
+
const pageNumbers = derived(() => {
|
|
35
|
+
const cur = page()
|
|
36
|
+
const pages = []
|
|
37
|
+
const totalNumbers = siblingCount * 2 + 3
|
|
38
|
+
const totalBlocks = totalNumbers + 2
|
|
68
39
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
40
|
+
if (totalPages <= totalBlocks) {
|
|
41
|
+
for (let i = 1; i <= totalPages; i++) pages.push(i)
|
|
42
|
+
return pages
|
|
43
|
+
}
|
|
72
44
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
45
|
+
const leftSibling = Math.max(cur - siblingCount, 1)
|
|
46
|
+
const rightSibling = Math.min(cur + siblingCount, totalPages)
|
|
47
|
+
const showLeftEllipsis = leftSibling > 2
|
|
48
|
+
const showRightEllipsis = rightSibling < totalPages - 1
|
|
76
49
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
50
|
+
if (!showLeftEllipsis && showRightEllipsis) {
|
|
51
|
+
const leftCount = 3 + 2 * siblingCount
|
|
52
|
+
for (let i = 1; i <= leftCount; i++) pages.push(i)
|
|
53
|
+
pages.push('ellipsis-right')
|
|
54
|
+
pages.push(totalPages)
|
|
55
|
+
}
|
|
56
|
+
else if (showLeftEllipsis && !showRightEllipsis) {
|
|
57
|
+
pages.push(1)
|
|
58
|
+
pages.push('ellipsis-left')
|
|
59
|
+
const rightCount = 3 + 2 * siblingCount
|
|
60
|
+
for (let i = totalPages - rightCount + 1; i <= totalPages; i++) pages.push(i)
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
pages.push(1)
|
|
64
|
+
pages.push('ellipsis-left')
|
|
65
|
+
for (let i = leftSibling; i <= rightSibling; i++) pages.push(i)
|
|
66
|
+
pages.push('ellipsis-right')
|
|
67
|
+
pages.push(totalPages)
|
|
68
|
+
}
|
|
69
|
+
return pages
|
|
70
|
+
})
|
|
80
71
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
72
|
+
const isFirstPage = derived(() => page() === 1)
|
|
73
|
+
const isLastPage = derived(() => page() === totalPages)
|
|
84
74
|
|
|
85
|
-
|
|
86
|
-
export const buttonClasses = `px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white dark:disabled:hover:bg-gray-800 transition-colors duration-150`.trim()
|
|
87
|
-
export const activeButtonClasses = `px-3 py-2 text-sm font-medium text-white bg-blue-600 dark:bg-blue-500 border border-blue-600 dark:border-blue-500 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900`.trim()
|
|
88
|
-
export const ellipsisClasses = `px-3 py-2 text-sm font-medium text-gray-400 dark:text-gray-600`.trim()
|
|
89
|
-
export const iconClasses = `w-5 h-5`.trim()
|
|
75
|
+
defineExpose({ page, goToPage })
|
|
90
76
|
</script>
|
|
91
77
|
|
|
92
78
|
<nav class="{{ paginationClasses }}" role="navigation" aria-label="Pagination">
|
|
93
|
-
<!-- First page button -->
|
|
94
79
|
@if(showFirstLast)
|
|
95
80
|
<button
|
|
96
81
|
type="button"
|
|
97
82
|
@click="goToFirst()"
|
|
98
83
|
class="{{ buttonClasses }}"
|
|
99
84
|
aria-label="Go to first page"
|
|
100
|
-
disabled="
|
|
85
|
+
:disabled="isFirstPage()"
|
|
101
86
|
>
|
|
102
87
|
<svg class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
103
88
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
|
|
@@ -105,14 +90,13 @@ export const iconClasses = `w-5 h-5`.trim()
|
|
|
105
90
|
</button>
|
|
106
91
|
@endif
|
|
107
92
|
|
|
108
|
-
<!-- Previous button -->
|
|
109
93
|
@if(showPrevNext)
|
|
110
94
|
<button
|
|
111
95
|
type="button"
|
|
112
96
|
@click="goToPrevious()"
|
|
113
97
|
class="{{ buttonClasses }}"
|
|
114
98
|
aria-label="Go to previous page"
|
|
115
|
-
disabled="
|
|
99
|
+
:disabled="isFirstPage()"
|
|
116
100
|
>
|
|
117
101
|
<svg class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
118
102
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
|
@@ -120,45 +104,27 @@ export const iconClasses = `w-5 h-5`.trim()
|
|
|
120
104
|
</button>
|
|
121
105
|
@endif
|
|
122
106
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
disabled
|
|
137
|
-
>
|
|
138
|
-
{{ page }}
|
|
139
|
-
</button>
|
|
140
|
-
@else
|
|
141
|
-
<!-- Other pages -->
|
|
142
|
-
<button
|
|
143
|
-
type="button"
|
|
144
|
-
@click="goToPage({{ page }})"
|
|
145
|
-
class="{{ buttonClasses }}"
|
|
146
|
-
aria-label="Go to page {{ page }}"
|
|
147
|
-
>
|
|
148
|
-
{{ page }}
|
|
149
|
-
</button>
|
|
150
|
-
@endif
|
|
151
|
-
@endif
|
|
152
|
-
@endforeach
|
|
107
|
+
<span :for="p in pageNumbers()" :key="p" class="contents">
|
|
108
|
+
<span :if="typeof p === 'string'" class="{{ ellipsisClasses }}" aria-hidden="true">...</span>
|
|
109
|
+
<button
|
|
110
|
+
:if="typeof p !== 'string'"
|
|
111
|
+
type="button"
|
|
112
|
+
:class="p === page() ? '{{ activeButtonClasses }}' : '{{ buttonClasses }}'"
|
|
113
|
+
:aria-label="`Go to page ${p}`"
|
|
114
|
+
:aria-current="p === page() ? 'page' : null"
|
|
115
|
+
:disabled="p === page()"
|
|
116
|
+
@click="goToPage(p)"
|
|
117
|
+
x-text="p"
|
|
118
|
+
></button>
|
|
119
|
+
</span>
|
|
153
120
|
|
|
154
|
-
<!-- Next button -->
|
|
155
121
|
@if(showPrevNext)
|
|
156
122
|
<button
|
|
157
123
|
type="button"
|
|
158
124
|
@click="goToNext()"
|
|
159
125
|
class="{{ buttonClasses }}"
|
|
160
126
|
aria-label="Go to next page"
|
|
161
|
-
disabled="
|
|
127
|
+
:disabled="isLastPage()"
|
|
162
128
|
>
|
|
163
129
|
<svg class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
164
130
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
@@ -166,14 +132,13 @@ export const iconClasses = `w-5 h-5`.trim()
|
|
|
166
132
|
</button>
|
|
167
133
|
@endif
|
|
168
134
|
|
|
169
|
-
<!-- Last page button -->
|
|
170
135
|
@if(showFirstLast)
|
|
171
136
|
<button
|
|
172
137
|
type="button"
|
|
173
138
|
@click="goToLast()"
|
|
174
139
|
class="{{ buttonClasses }}"
|
|
175
140
|
aria-label="Go to last page"
|
|
176
|
-
disabled="
|
|
141
|
+
:disabled="isLastPage()"
|
|
177
142
|
>
|
|
178
143
|
<svg class="{{ iconClasses }}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
179
144
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
|
|
@@ -0,0 +1,142 @@
|
|
|
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
|
+
const inputValue = state({{ value }})
|
|
38
|
+
|
|
39
|
+
function onInput(event) {
|
|
40
|
+
inputValue.set(event.target.value)
|
|
41
|
+
emit('input', inputValue())
|
|
42
|
+
}
|
|
43
|
+
|
|
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)
|
|
57
|
+
}
|
|
58
|
+
|
|
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 })
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<div class="w-full {{ className }}">
|
|
79
|
+
@if(label)
|
|
80
|
+
<label for="password-input" class="{{ labelClasses }}">
|
|
81
|
+
{{ label }}
|
|
82
|
+
@if(required)
|
|
83
|
+
<span class="text-red-500 ml-1">*</span>
|
|
84
|
+
@endif
|
|
85
|
+
</label>
|
|
86
|
+
@endif
|
|
87
|
+
|
|
88
|
+
<div class="relative">
|
|
89
|
+
<input
|
|
90
|
+
:type="inputType()"
|
|
91
|
+
id="password-input"
|
|
92
|
+
class="{{ inputClasses }}"
|
|
93
|
+
:value="inputValue()"
|
|
94
|
+
placeholder="{{ placeholder }}"
|
|
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' }}"
|
|
100
|
+
@if(helperText)aria-describedby="helper-text"@endif
|
|
101
|
+
/>
|
|
102
|
+
|
|
103
|
+
<div class="absolute inset-y-0 right-0 flex items-center pr-3">
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
@click="toggleVisibility()"
|
|
107
|
+
class="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 focus:outline-none"
|
|
108
|
+
:aria-label="toggleAriaLabel()"
|
|
109
|
+
>
|
|
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>
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
@if(showStrength)
|
|
122
|
+
<div :show="inputValue().length > 0" class="mt-2">
|
|
123
|
+
<div class="flex items-center justify-between mb-1">
|
|
124
|
+
<span class="text-xs text-gray-600 dark:text-gray-400">Password strength</span>
|
|
125
|
+
<span class="text-xs font-medium" :class="strengthLabelColor()" x-text="strengthText()"></span>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
|
|
128
|
+
<div
|
|
129
|
+
class="h-full transition-all duration-300 rounded-full"
|
|
130
|
+
:class="strengthColor()"
|
|
131
|
+
:style="`width: ${strength()}%`"
|
|
132
|
+
></div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
@endif
|
|
136
|
+
|
|
137
|
+
@if(helperText)
|
|
138
|
+
<p id="helper-text" class="{{ helperClasses }}">
|
|
139
|
+
{{ helperText }}
|
|
140
|
+
</p>
|
|
141
|
+
@endif
|
|
142
|
+
</div>
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
<script server>
|
|
2
2
|
export const paymentMethods = $props.paymentMethods || []
|
|
3
|
-
export const onDeletePaymentMethod = $props.onDeletePaymentMethod
|
|
4
|
-
export const onMakeDefault = $props.onMakeDefault
|
|
5
3
|
export const isLoading = $props.isLoading || false
|
|
4
|
+
export const showMakeDefault = $props.showMakeDefault ?? true
|
|
5
|
+
export const showDelete = $props.showDelete ?? true
|
|
6
6
|
export const className = $props.className || ''
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
const brandIcons = {
|
|
8
|
+
export const brandIcons = {
|
|
10
9
|
visa: '💳',
|
|
11
10
|
mastercard: '💳',
|
|
12
11
|
amex: '💳',
|
|
@@ -17,9 +16,22 @@ const brandIcons = {
|
|
|
17
16
|
export const containerClasses = `mt-4 ${className}`.trim()
|
|
18
17
|
</script>
|
|
19
18
|
|
|
19
|
+
<script client>
|
|
20
|
+
const emit = defineEmits()
|
|
21
|
+
|
|
22
|
+
function onMakeDefault(methodId) {
|
|
23
|
+
emit('makeDefault', methodId)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function onDelete(methodId) {
|
|
27
|
+
if (confirm('Are you sure you want to delete this payment method?')) {
|
|
28
|
+
emit('deletePaymentMethod', methodId)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
20
33
|
<div class="{{ containerClasses }}">
|
|
21
34
|
@if(isLoading)
|
|
22
|
-
<!-- Loading state -->
|
|
23
35
|
<div class="animate-pulse">
|
|
24
36
|
<div class="h-24 bg-gray-200 dark:bg-gray-700 rounded-lg"></div>
|
|
25
37
|
</div>
|
|
@@ -31,12 +43,10 @@ export const containerClasses = `mt-4 ${className}`.trim()
|
|
|
31
43
|
<div class="w-full p-3">
|
|
32
44
|
<div class="flex items-center justify-between">
|
|
33
45
|
<div class="flex items-center space-x-4">
|
|
34
|
-
<!-- Card Brand Icon -->
|
|
35
46
|
<div class="flex-shrink-0 h-10 w-10 flex items-center justify-center bg-gray-100 dark:bg-gray-700 rounded-md">
|
|
36
47
|
<span class="text-2xl">{{ brandIcons[method.brand.toLowerCase()] || brandIcons.default }}</span>
|
|
37
48
|
</div>
|
|
38
49
|
|
|
39
|
-
<!-- Card Details -->
|
|
40
50
|
<div>
|
|
41
51
|
<h2 class="text-sm text-gray-600 dark:text-gray-300">
|
|
42
52
|
{{ method.brand }} •••• {{ method.last_four }}
|
|
@@ -48,12 +58,11 @@ export const containerClasses = `mt-4 ${className}`.trim()
|
|
|
48
58
|
</div>
|
|
49
59
|
|
|
50
60
|
<div class="flex justify-end space-x-4">
|
|
51
|
-
|
|
52
|
-
@if(onMakeDefault)
|
|
61
|
+
@if(showMakeDefault)
|
|
53
62
|
<button
|
|
54
63
|
type="button"
|
|
55
64
|
class="border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-blue-gray-700 px-2 py-1 text-sm text-gray-700 dark:text-gray-300 font-semibold shadow-sm hover:bg-gray-50 dark:hover:bg-blue-gray-600 focus-visible:outline-2 focus-visible:outline-blue-600 focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
56
|
-
@click="
|
|
65
|
+
@click="onMakeDefault('{{ method.id }}')"
|
|
57
66
|
title="Make default"
|
|
58
67
|
>
|
|
59
68
|
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
@@ -62,12 +71,11 @@ export const containerClasses = `mt-4 ${className}`.trim()
|
|
|
62
71
|
</button>
|
|
63
72
|
@endif
|
|
64
73
|
|
|
65
|
-
|
|
66
|
-
@if(onDeletePaymentMethod)
|
|
74
|
+
@if(showDelete)
|
|
67
75
|
<button
|
|
68
76
|
type="button"
|
|
69
77
|
class="border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-blue-gray-700 px-2 py-1 text-sm text-red-600 dark:text-red-400 font-semibold shadow-sm hover:bg-red-50 dark:hover:bg-red-900/20 focus-visible:outline-2 focus-visible:outline-red-600 focus-visible:outline-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
70
|
-
@click="
|
|
78
|
+
@click="onDelete('{{ method.id }}')"
|
|
71
79
|
title="Delete payment method"
|
|
72
80
|
>
|
|
73
81
|
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
@@ -82,7 +90,6 @@ export const containerClasses = `mt-4 ${className}`.trim()
|
|
|
82
90
|
@endforeach
|
|
83
91
|
</ul>
|
|
84
92
|
@else
|
|
85
|
-
<!-- Empty state -->
|
|
86
93
|
<div class="text-center py-12 bg-white dark:bg-blue-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
|
|
87
94
|
<svg class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
88
95
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
|
|
@@ -95,19 +102,3 @@ export const containerClasses = `mt-4 ${className}`.trim()
|
|
|
95
102
|
@endif
|
|
96
103
|
@endif
|
|
97
104
|
</div>
|
|
98
|
-
|
|
99
|
-
<script>
|
|
100
|
-
function handleMakeDefault(methodId) {
|
|
101
|
-
if (onMakeDefault) {
|
|
102
|
-
onMakeDefault(methodId)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function handleDelete(methodId) {
|
|
107
|
-
if (onDeletePaymentMethod) {
|
|
108
|
-
if (confirm('Are you sure you want to delete this payment method?')) {
|
|
109
|
-
onDeletePaymentMethod(methodId)
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
</script>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
export const className = $props.className || ''
|
|
3
|
+
export const as = $props.as || 'div'
|
|
4
|
+
|
|
5
|
+
const baseClasses = 'relative'
|
|
6
|
+
export const popoverClasses = `${baseClasses} ${className}`.trim()
|
|
7
|
+
export const popoverInstanceId = `stx-pop-${Math.random().toString(36).slice(2, 11)}`
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<script client>
|
|
11
|
+
const emit = defineEmits()
|
|
12
|
+
const popoverInstanceId = {{ popoverInstanceId }}
|
|
13
|
+
const isOpen = state(false)
|
|
14
|
+
let rootEl = null
|
|
15
|
+
let outsideHandler = null
|
|
16
|
+
|
|
17
|
+
function open() { if (!isOpen()) { isOpen.set(true); emit('change', true) } }
|
|
18
|
+
function close() { if (isOpen()) { isOpen.set(false); emit('change', false) } }
|
|
19
|
+
function toggle() { isOpen() ? close() : open() }
|
|
20
|
+
|
|
21
|
+
function onToggle(event) { event.stopPropagation(); toggle() }
|
|
22
|
+
function onClose(event) { event.stopPropagation(); close() }
|
|
23
|
+
function onOpen(event) { event.stopPropagation(); open() }
|
|
24
|
+
|
|
25
|
+
onMount(() => {
|
|
26
|
+
rootEl = document.querySelector(`[data-popover-id="${popoverInstanceId}"]`)
|
|
27
|
+
outsideHandler = (event) => {
|
|
28
|
+
if (!isOpen()) return
|
|
29
|
+
if (rootEl && !rootEl.contains(event.target)) close()
|
|
30
|
+
}
|
|
31
|
+
document.addEventListener('click', outsideHandler, true)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
onDestroy(() => {
|
|
35
|
+
if (outsideHandler) document.removeEventListener('click', outsideHandler, true)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const dataState = derived(() => isOpen() ? 'open' : 'closed')
|
|
39
|
+
|
|
40
|
+
defineExpose({ isOpen, toggle, open, close })
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<component
|
|
44
|
+
:is="{{ as }}"
|
|
45
|
+
class="{{ popoverClasses }}"
|
|
46
|
+
data-stx-popover
|
|
47
|
+
data-popover-id="{{ popoverInstanceId }}"
|
|
48
|
+
:data-state="dataState()"
|
|
49
|
+
@stx:popover-toggle="onToggle($event)"
|
|
50
|
+
@stx:popover-open="onOpen($event)"
|
|
51
|
+
@stx:popover-close="onClose($event)"
|
|
52
|
+
>
|
|
53
|
+
<slot />
|
|
54
|
+
</component>
|
|
55
|
+
|
|
56
|
+
<style>
|
|
57
|
+
[data-stx-popover][data-state="closed"] [data-stx-popover-panel] {
|
|
58
|
+
display: none;
|
|
59
|
+
}
|
|
60
|
+
</style>
|
|
@@ -2,22 +2,28 @@
|
|
|
2
2
|
export const className = $props.className || ''
|
|
3
3
|
export const as = $props.as || 'button'
|
|
4
4
|
export const disabled = $props.disabled || false
|
|
5
|
-
export const onClick = $props.onClick
|
|
6
5
|
|
|
7
6
|
const baseClasses = 'inline-flex items-center gap-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75'
|
|
8
|
-
const buttonClasses = `${baseClasses} ${className}`.trim()
|
|
7
|
+
export const buttonClasses = `${baseClasses} ${className}`.trim()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<script client>
|
|
11
|
+
const emit = defineEmits()
|
|
12
|
+
const isDisabled = {{ disabled }}
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
if (
|
|
12
|
-
|
|
14
|
+
function onClick(event) {
|
|
15
|
+
if (isDisabled) return
|
|
16
|
+
emit('click', event)
|
|
17
|
+
event.currentTarget.dispatchEvent(new CustomEvent('stx:popover-toggle', { bubbles: true }))
|
|
13
18
|
}
|
|
14
19
|
</script>
|
|
15
20
|
|
|
16
21
|
<component
|
|
17
22
|
:is="{{ as }}"
|
|
18
23
|
class="{{ buttonClasses }}"
|
|
24
|
+
data-stx-popover-button
|
|
19
25
|
@if(disabled)disabled aria-disabled="true"@endif
|
|
20
|
-
@click="
|
|
26
|
+
@click="onClick($event)"
|
|
21
27
|
>
|
|
22
28
|
<slot />
|
|
23
29
|
</component>
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
<script server>
|
|
2
2
|
export const className = $props.className || ''
|
|
3
3
|
export const as = $props.as || 'div'
|
|
4
|
-
export const static_ = $props.static || false
|
|
5
4
|
export const position = $props.position || 'bottom'
|
|
6
5
|
|
|
7
6
|
const positionClasses = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
top: 'bottom-full mb-2',
|
|
8
|
+
bottom: 'top-full mt-2',
|
|
9
|
+
left: 'right-full mr-2',
|
|
10
|
+
right: 'left-full ml-2',
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
const baseClasses = 'absolute z-10 rounded-lg bg-white dark:bg-neutral-800 p-4 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none'
|
|
15
|
-
const panelClasses = `${baseClasses} ${positionClasses[position]} ${className}`.trim()
|
|
14
|
+
export const panelClasses = `${baseClasses} ${positionClasses[position]} ${className}`.trim()
|
|
16
15
|
</script>
|
|
17
16
|
|
|
18
17
|
<component
|
|
19
18
|
:is="{{ as }}"
|
|
20
19
|
class="{{ panelClasses }}"
|
|
20
|
+
data-stx-popover-panel
|
|
21
21
|
>
|
|
22
22
|
<slot />
|
|
23
23
|
</component>
|