@stacksjs/components 0.2.3
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/README.md +477 -0
- package/dist/Accordion-x2gjedap.stx +108 -0
- package/dist/Audio-0fk6p9tn.stx +50 -0
- package/dist/Avatar-5m0v0s4x.stx +88 -0
- package/dist/Badge-ynes82vv.stx +70 -0
- package/dist/Breadcrumb-h9n427sk.stx +79 -0
- package/dist/Button-7xtrzn14.stx +89 -0
- package/dist/Calendar-egvx1pma.stx +232 -0
- package/dist/Card-r9w7vy1a.stx +74 -0
- package/dist/Checkbox-m8v46pmx.stx +92 -0
- package/dist/Checkout-vcnnr176.stx +39 -0
- package/dist/CodeBlock-mwex82cm.stx +57 -0
- package/dist/Combobox-npb08vkp.stx +13 -0
- package/dist/ComboboxButton-vcdyq4zw.stx +13 -0
- package/dist/ComboboxInput-c9d2p02m.stx +25 -0
- package/dist/ComboboxOption-7d0h9ash.stx +52 -0
- package/dist/ComboboxOptions-4dtqc0qa.stx +12 -0
- package/dist/CommandPalette-ah00bf8v.stx +39 -0
- package/dist/CommandPaletteItem-2yx0gvtj.stx +32 -0
- package/dist/DefaultPaymentMethod-emw5fg8s.stx +62 -0
- package/dist/Dialog-1w4rz7x0.stx +19 -0
- package/dist/DialogBackdrop-qbm3y5ab.stx +9 -0
- package/dist/DialogDescription-01w5j74b.stx +11 -0
- package/dist/DialogPanel-brsszrrs.stx +16 -0
- package/dist/DialogTitle-3rxhwg21.stx +11 -0
- package/dist/Drawer-yfaz8q7f.stx +137 -0
- package/dist/Dropdown-ng7nx556.stx +24 -0
- package/dist/DropdownButton-vc4zpyrb.stx +23 -0
- package/dist/DropdownItem-z491y23c.stx +43 -0
- package/dist/DropdownItems-7ykf29sj.stx +18 -0
- package/dist/EmailInput-bfshpj92.stx +41 -0
- package/dist/Footer-xdaf02nr.stx +66 -0
- package/dist/Form-z4f7w6r1.stx +209 -0
- package/dist/Hero-9vvp7pxf.stx +25 -0
- package/dist/Image-ktk0451c.stx +197 -0
- package/dist/Installation-33bj4rm7.stx +39 -0
- package/dist/Listbox-xcejcmyc.stx +14 -0
- package/dist/ListboxButton-nx5kmw19.stx +17 -0
- package/dist/ListboxLabel-aky8a73n.stx +11 -0
- package/dist/ListboxOption-6h4hsdz5.stx +57 -0
- package/dist/ListboxOptions-2sacdtm5.stx +16 -0
- package/dist/Login-s2jkvd9y.stx +163 -0
- package/dist/Navigator-tqqdgdkw.stx +138 -0
- package/dist/Notification-sry8egea.stx +71 -0
- package/dist/NumberInput-zm93ddfw.stx +138 -0
- package/dist/Pagination-yza9v4qw.stx +183 -0
- package/dist/PasswordInput-4xnndp5y.stx +155 -0
- package/dist/PaymentMethods-jxba6kek.stx +113 -0
- package/dist/Popover-scbqatjq.stx +24 -0
- package/dist/PopoverButton-rbxhzj4k.stx +23 -0
- package/dist/PopoverPanel-9ng1rm6e.stx +23 -0
- package/dist/Portal-j111yvqp.stx +74 -0
- package/dist/Progress-gbmw3akr.stx +142 -0
- package/dist/Radio-1n9qamv1.stx +81 -0
- package/dist/RadioGroup-12dw2vhk.stx +13 -0
- package/dist/RadioGroupDescription-bjm3mdht.stx +11 -0
- package/dist/RadioGroupLabel-aky8a73n.stx +11 -0
- package/dist/RadioGroupOption-9pcxgwmd.stx +51 -0
- package/dist/SearchInput-g240e2nq.stx +63 -0
- package/dist/Select-p9f94b20.stx +89 -0
- package/dist/Sidebar-gwxw8q38.stx +288 -0
- package/dist/SidebarFooter-887kxret.stx +95 -0
- package/dist/SidebarHeader-qjeyx0hv.stx +100 -0
- package/dist/SidebarItem-4dpwtmb4.stx +118 -0
- package/dist/SidebarSection-kh92fk4m.stx +108 -0
- package/dist/Signup-3nxq5zag.stx +161 -0
- package/dist/Skeleton-g0nx1njm.stx +48 -0
- package/dist/Spinner-w4zj58xp.stx +109 -0
- package/dist/Stepper-rwc5actp.stx +17 -0
- package/dist/StepperStep-h0k657nm.stx +53 -0
- package/dist/SubscriptionCheckout-4cbxdg7w.stx +353 -0
- package/dist/Switch-kjq8qd2p.stx +74 -0
- package/dist/Table-59j9qwp0.stx +14 -0
- package/dist/TableBody-reka2gwv.stx +10 -0
- package/dist/TableCell-j7nh4sgr.stx +10 -0
- package/dist/TableHead-ecbrwpqm.stx +10 -0
- package/dist/TableHeader-jn2shwsv.stx +13 -0
- package/dist/TableRow-xz8rrxhc.stx +11 -0
- package/dist/Tabs-kb6ex6m7.stx +145 -0
- package/dist/Teleport-819cg9zm.stx +101 -0
- package/dist/TextInput-zx9kgr79.stx +146 -0
- package/dist/Textarea-5xvmrb0z.stx +119 -0
- package/dist/Tooltip-5ehqsy49.stx +76 -0
- package/dist/Transition-2j39s044.stx +174 -0
- package/dist/TwoFactorChallenge-2q4gg89m.stx +125 -0
- package/dist/Video-6aq4shh4.stx +45 -0
- package/dist/VirtualList-weh5qtm1.stx +83 -0
- package/dist/VirtualTable-59a1eskc.stx +113 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/composables/index.d.ts +4 -0
- package/dist/composables/index.js +3 -0
- package/dist/composables/useCopyCode.d.ts +19 -0
- package/dist/composables/useDarkMode.d.ts +25 -0
- package/dist/composables/useSEO.d.ts +36 -0
- package/dist/composables/utilities/index.d.ts +5 -0
- package/dist/composables/utilities/useClickOutside.d.ts +28 -0
- package/dist/composables/utilities/useFocusTrap.d.ts +34 -0
- package/dist/composables/utilities/useKeyboard.d.ts +55 -0
- package/dist/composables/utilities/useLocalStorage.d.ts +33 -0
- package/dist/composables/utilities/useMediaQuery.d.ts +64 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +19 -0
- package/dist/ui/accordion/index.d.ts +12 -0
- package/dist/ui/audio/index.d.ts +12 -0
- package/dist/ui/auth/index.d.ts +29 -0
- package/dist/ui/avatar/index.d.ts +11 -0
- package/dist/ui/badge/index.d.ts +9 -0
- package/dist/ui/breadcrumb/index.d.ts +13 -0
- package/dist/ui/button/index.d.ts +45 -0
- package/dist/ui/calendar/index.d.ts +13 -0
- package/dist/ui/card/index.d.ts +12 -0
- package/dist/ui/checkbox/index.d.ts +15 -0
- package/dist/ui/combobox/index.d.ts +35 -0
- package/dist/ui/command-palette/index.d.ts +13 -0
- package/dist/ui/dialog/index.d.ts +27 -0
- package/dist/ui/drawer/index.d.ts +8 -0
- package/dist/ui/dropdown/index.d.ts +25 -0
- package/dist/ui/form/index.d.ts +48 -0
- package/dist/ui/image/index.d.ts +21 -0
- package/dist/ui/input/index.d.ts +73 -0
- package/dist/ui/listbox/index.d.ts +34 -0
- package/dist/ui/navigator/index.d.ts +18 -0
- package/dist/ui/notification/index.d.ts +11 -0
- package/dist/ui/pagination/index.d.ts +10 -0
- package/dist/ui/payment/index.d.ts +53 -0
- package/dist/ui/popover/index.d.ts +19 -0
- package/dist/ui/portal/index.d.ts +5 -0
- package/dist/ui/progress/index.d.ts +11 -0
- package/dist/ui/radio/index.d.ts +14 -0
- package/dist/ui/radio-group/index.d.ts +26 -0
- package/dist/ui/select/index.d.ts +19 -0
- package/dist/ui/sidebar/index.d.ts +129 -0
- package/dist/ui/skeleton/index.d.ts +10 -0
- package/dist/ui/spinner/index.d.ts +8 -0
- package/dist/ui/stepper/index.d.ts +17 -0
- package/dist/ui/storage/index.d.ts +40 -0
- package/dist/ui/switch/index.d.ts +9 -0
- package/dist/ui/table/index.d.ts +29 -0
- package/dist/ui/tabs/index.d.ts +14 -0
- package/dist/ui/teleport/index.d.ts +6 -0
- package/dist/ui/textarea/index.d.ts +19 -0
- package/dist/ui/tooltip/index.d.ts +9 -0
- package/dist/ui/transition/index.d.ts +24 -0
- package/dist/ui/video/index.d.ts +15 -0
- package/dist/ui/virtual-list/index.d.ts +9 -0
- package/dist/ui/virtual-table/index.d.ts +19 -0
- package/dist/utils/accessibility.d.ts +285 -0
- package/dist/utils/animation/easing.d.ts +112 -0
- package/dist/utils/animation/helpers.d.ts +119 -0
- package/dist/utils/animation/index.d.ts +3 -0
- package/dist/utils/animation/keyframes.d.ts +334 -0
- package/dist/utils/error-handling.d.ts +182 -0
- package/dist/utils/highlighter.d.ts +27 -0
- package/dist/utils/highlighter.js +2 -0
- package/dist/utils/i18n.d.ts +245 -0
- package/dist/utils/keep-alive.d.ts +171 -0
- package/dist/utils/lazy-loading.d.ts +174 -0
- package/dist/utils/prop-validation.d.ts +106 -0
- package/dist/utils/theme.d.ts +234 -0
- package/package.json +69 -0
- package/src/components/CodeBlock.stx +57 -0
- package/src/components/Footer.stx +66 -0
- package/src/components/Hero.stx +25 -0
- package/src/components/Installation.stx +39 -0
- package/src/components/index.ts +4 -0
- package/src/composables/index.ts +4 -0
- package/src/composables/useCopyCode.ts +73 -0
- package/src/composables/useDarkMode.ts +100 -0
- package/src/composables/useSEO.ts +229 -0
- package/src/composables/utilities/index.ts +7 -0
- package/src/composables/utilities/useClickOutside.ts +72 -0
- package/src/composables/utilities/useFocusTrap.ts +159 -0
- package/src/composables/utilities/useKeyboard.ts +124 -0
- package/src/composables/utilities/useLocalStorage.ts +132 -0
- package/src/composables/utilities/useMediaQuery.ts +118 -0
- package/src/index.ts +99 -0
- package/src/stx.d.ts +4 -0
- package/src/ui/accordion/Accordion.stx +108 -0
- package/src/ui/accordion/index.ts +14 -0
- package/src/ui/audio/Audio.stx +50 -0
- package/src/ui/audio/README.md +42 -0
- package/src/ui/audio/index.ts +13 -0
- package/src/ui/auth/Login.stx +163 -0
- package/src/ui/auth/README.md +145 -0
- package/src/ui/auth/Signup.stx +161 -0
- package/src/ui/auth/TwoFactorChallenge.stx +125 -0
- package/src/ui/auth/index.ts +32 -0
- package/src/ui/avatar/Avatar.stx +88 -0
- package/src/ui/avatar/index.ts +12 -0
- package/src/ui/badge/Badge.stx +70 -0
- package/src/ui/badge/index.ts +10 -0
- package/src/ui/breadcrumb/Breadcrumb.stx +79 -0
- package/src/ui/breadcrumb/index.ts +15 -0
- package/src/ui/button/Button.stx +89 -0
- package/src/ui/button/README.md +100 -0
- package/src/ui/button/index.ts +85 -0
- package/src/ui/calendar/Calendar.stx +232 -0
- package/src/ui/calendar/README.md +254 -0
- package/src/ui/calendar/index.ts +14 -0
- package/src/ui/card/Card.stx +74 -0
- package/src/ui/card/index.ts +13 -0
- package/src/ui/checkbox/Checkbox.stx +92 -0
- package/src/ui/checkbox/index.ts +16 -0
- package/src/ui/combobox/Combobox.stx +13 -0
- package/src/ui/combobox/ComboboxButton.stx +13 -0
- package/src/ui/combobox/ComboboxInput.stx +25 -0
- package/src/ui/combobox/ComboboxOption.stx +52 -0
- package/src/ui/combobox/ComboboxOptions.stx +12 -0
- package/src/ui/combobox/README.md +154 -0
- package/src/ui/combobox/index.ts +40 -0
- package/src/ui/command-palette/CommandPalette.stx +39 -0
- package/src/ui/command-palette/CommandPaletteItem.stx +32 -0
- package/src/ui/command-palette/README.md +42 -0
- package/src/ui/command-palette/index.ts +15 -0
- package/src/ui/dialog/Dialog.stx +19 -0
- package/src/ui/dialog/DialogBackdrop.stx +9 -0
- package/src/ui/dialog/DialogDescription.stx +11 -0
- package/src/ui/dialog/DialogPanel.stx +16 -0
- package/src/ui/dialog/DialogTitle.stx +11 -0
- package/src/ui/dialog/README.md +133 -0
- package/src/ui/dialog/index.ts +32 -0
- package/src/ui/drawer/Drawer.stx +137 -0
- package/src/ui/drawer/README.md +110 -0
- package/src/ui/drawer/index.ts +9 -0
- package/src/ui/dropdown/Dropdown.stx +24 -0
- package/src/ui/dropdown/DropdownButton.stx +23 -0
- package/src/ui/dropdown/DropdownItem.stx +43 -0
- package/src/ui/dropdown/DropdownItems.stx +18 -0
- package/src/ui/dropdown/README.md +142 -0
- package/src/ui/dropdown/index.ts +29 -0
- package/src/ui/form/Form.stx +209 -0
- package/src/ui/form/index.ts +67 -0
- package/src/ui/heatmap/Heatmap.stx +315 -0
- package/src/ui/image/Image.stx +197 -0
- package/src/ui/image/README.md +171 -0
- package/src/ui/image/index.ts +22 -0
- package/src/ui/input/EmailInput.stx +41 -0
- package/src/ui/input/NumberInput.stx +138 -0
- package/src/ui/input/PasswordInput.stx +155 -0
- package/src/ui/input/SearchInput.stx +63 -0
- package/src/ui/input/TextInput.stx +146 -0
- package/src/ui/input/index.ts +180 -0
- package/src/ui/listbox/Listbox.stx +14 -0
- package/src/ui/listbox/ListboxButton.stx +17 -0
- package/src/ui/listbox/ListboxLabel.stx +11 -0
- package/src/ui/listbox/ListboxOption.stx +57 -0
- package/src/ui/listbox/ListboxOptions.stx +16 -0
- package/src/ui/listbox/README.md +143 -0
- package/src/ui/listbox/index.ts +39 -0
- package/src/ui/navigator/Navigator.stx +138 -0
- package/src/ui/navigator/README.md +352 -0
- package/src/ui/navigator/index.ts +20 -0
- package/src/ui/notification/Notification.stx +71 -0
- package/src/ui/notification/README.md +118 -0
- package/src/ui/notification/index.ts +12 -0
- package/src/ui/pagination/Pagination.stx +183 -0
- package/src/ui/pagination/index.ts +11 -0
- package/src/ui/payment/Checkout.stx +39 -0
- package/src/ui/payment/DefaultPaymentMethod.stx +62 -0
- package/src/ui/payment/PaymentMethods.stx +113 -0
- package/src/ui/payment/README.md +427 -0
- package/src/ui/payment/SubscriptionCheckout.stx +353 -0
- package/src/ui/payment/index.ts +59 -0
- package/src/ui/popover/Popover.stx +24 -0
- package/src/ui/popover/PopoverButton.stx +23 -0
- package/src/ui/popover/PopoverPanel.stx +23 -0
- package/src/ui/popover/README.md +95 -0
- package/src/ui/popover/index.ts +22 -0
- package/src/ui/portal/Portal.stx +74 -0
- package/src/ui/portal/index.ts +6 -0
- package/src/ui/progress/Progress.stx +142 -0
- package/src/ui/progress/index.ts +12 -0
- package/src/ui/radio/Radio.stx +81 -0
- package/src/ui/radio/index.ts +15 -0
- package/src/ui/radio-group/README.md +145 -0
- package/src/ui/radio-group/RadioGroup.stx +13 -0
- package/src/ui/radio-group/RadioGroupDescription.stx +11 -0
- package/src/ui/radio-group/RadioGroupLabel.stx +11 -0
- package/src/ui/radio-group/RadioGroupOption.stx +51 -0
- package/src/ui/radio-group/index.ts +30 -0
- package/src/ui/select/README.md +204 -0
- package/src/ui/select/Select.stx +89 -0
- package/src/ui/select/index.ts +21 -0
- package/src/ui/sidebar/Sidebar.stx +288 -0
- package/src/ui/sidebar/SidebarFooter.stx +95 -0
- package/src/ui/sidebar/SidebarHeader.stx +100 -0
- package/src/ui/sidebar/SidebarItem.stx +118 -0
- package/src/ui/sidebar/SidebarSection.stx +108 -0
- package/src/ui/sidebar/index.ts +175 -0
- package/src/ui/skeleton/Skeleton.stx +48 -0
- package/src/ui/skeleton/index.ts +11 -0
- package/src/ui/spinner/Spinner.stx +109 -0
- package/src/ui/spinner/index.ts +9 -0
- package/src/ui/stepper/README.md +36 -0
- package/src/ui/stepper/Stepper.stx +17 -0
- package/src/ui/stepper/StepperStep.stx +53 -0
- package/src/ui/stepper/index.ts +19 -0
- package/src/ui/storage/README.md +193 -0
- package/src/ui/storage/index.ts +347 -0
- package/src/ui/switch/README.md +133 -0
- package/src/ui/switch/Switch.stx +74 -0
- package/src/ui/switch/index.ts +10 -0
- package/src/ui/table/README.md +39 -0
- package/src/ui/table/Table.stx +14 -0
- package/src/ui/table/TableBody.stx +10 -0
- package/src/ui/table/TableCell.stx +10 -0
- package/src/ui/table/TableHead.stx +10 -0
- package/src/ui/table/TableHeader.stx +13 -0
- package/src/ui/table/TableRow.stx +11 -0
- package/src/ui/table/index.ts +35 -0
- package/src/ui/tabs/Tabs.stx +145 -0
- package/src/ui/tabs/index.ts +16 -0
- package/src/ui/teleport/Teleport.stx +101 -0
- package/src/ui/teleport/index.ts +7 -0
- package/src/ui/textarea/Textarea.stx +119 -0
- package/src/ui/textarea/index.ts +20 -0
- package/src/ui/tooltip/Tooltip.stx +76 -0
- package/src/ui/tooltip/index.ts +10 -0
- package/src/ui/transition/README.md +30 -0
- package/src/ui/transition/Transition.stx +174 -0
- package/src/ui/transition/index.ts +25 -0
- package/src/ui/video/README.md +48 -0
- package/src/ui/video/Video.stx +45 -0
- package/src/ui/video/index.ts +16 -0
- package/src/ui/virtual-list/VirtualList.stx +83 -0
- package/src/ui/virtual-list/index.ts +10 -0
- package/src/ui/virtual-table/VirtualTable.stx +113 -0
- package/src/ui/virtual-table/index.ts +21 -0
- package/src/utils/PROP_VALIDATION.md +458 -0
- package/src/utils/THEME_SYSTEM.md +702 -0
- package/src/utils/accessibility.ts +841 -0
- package/src/utils/animation/easing.ts +368 -0
- package/src/utils/animation/helpers.ts +223 -0
- package/src/utils/animation/index.ts +5 -0
- package/src/utils/animation/keyframes.ts +230 -0
- package/src/utils/error-handling.ts +473 -0
- package/src/utils/highlighter.ts +93 -0
- package/src/utils/i18n.ts +634 -0
- package/src/utils/keep-alive.ts +818 -0
- package/src/utils/lazy-loading.ts +530 -0
- package/src/utils/prop-validation.ts +478 -0
- package/src/utils/theme.ts +626 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// Calendar Component Props
|
|
3
|
+
export const value = $props.value || new Date()
|
|
4
|
+
export const onChange = $props.onChange
|
|
5
|
+
export const minDate = $props.minDate
|
|
6
|
+
export const maxDate = $props.maxDate
|
|
7
|
+
export const disabledDates = $props.disabledDates || []
|
|
8
|
+
export const locale = $props.locale || 'en-US'
|
|
9
|
+
export const firstDayOfWeek = $props.firstDayOfWeek || 0 // 0 = Sunday, 1 = Monday
|
|
10
|
+
export const showToday = $props.showToday !== false
|
|
11
|
+
export const className = $props.className || ''
|
|
12
|
+
export const highlightToday = $props.highlightToday !== false
|
|
13
|
+
|
|
14
|
+
// State
|
|
15
|
+
let currentMonth = value ? new Date(value) : new Date()
|
|
16
|
+
let selectedDate = value ? new Date(value) : null
|
|
17
|
+
const today = new Date()
|
|
18
|
+
|
|
19
|
+
// Helper functions
|
|
20
|
+
function getDaysInMonth(date) {
|
|
21
|
+
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getFirstDayOfMonth(date) {
|
|
25
|
+
return new Date(date.getFullYear(), date.getMonth(), 1).getDay()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isSameDay(date1, date2) {
|
|
29
|
+
if (!date1 || !date2) return false
|
|
30
|
+
return date1.getFullYear() === date2.getFullYear() &&
|
|
31
|
+
date1.getMonth() === date2.getMonth() &&
|
|
32
|
+
date1.getDate() === date2.getDate()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isToday(date) {
|
|
36
|
+
return isSameDay(date, today)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isDisabled(date) {
|
|
40
|
+
if (minDate && date < minDate) return true
|
|
41
|
+
if (maxDate && date > maxDate) return true
|
|
42
|
+
return disabledDates.some(d => isSameDay(d, date))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getMonthName(date) {
|
|
46
|
+
return date.toLocaleDateString(locale, { month: 'long' })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getYear(date) {
|
|
50
|
+
return date.getFullYear()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getDayNames() {
|
|
54
|
+
const days = []
|
|
55
|
+
const baseDate = new Date(2024, 0, firstDayOfWeek) // Jan 2024 started on Monday
|
|
56
|
+
for (let i = 0; i < 7; i++) {
|
|
57
|
+
const day = new Date(baseDate)
|
|
58
|
+
day.setDate(baseDate.getDate() + i)
|
|
59
|
+
days.push(day.toLocaleDateString(locale, { weekday: 'short' }))
|
|
60
|
+
}
|
|
61
|
+
return days
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getCalendarDays() {
|
|
65
|
+
const daysInMonth = getDaysInMonth(currentMonth)
|
|
66
|
+
const firstDay = (getFirstDayOfMonth(currentMonth) - firstDayOfWeek + 7) % 7
|
|
67
|
+
const days = []
|
|
68
|
+
|
|
69
|
+
// Previous month days
|
|
70
|
+
const prevMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)
|
|
71
|
+
const daysInPrevMonth = getDaysInMonth(prevMonth)
|
|
72
|
+
for (let i = firstDay - 1; i >= 0; i--) {
|
|
73
|
+
days.push({
|
|
74
|
+
date: new Date(prevMonth.getFullYear(), prevMonth.getMonth(), daysInPrevMonth - i),
|
|
75
|
+
isCurrentMonth: false,
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Current month days
|
|
80
|
+
for (let i = 1; i <= daysInMonth; i++) {
|
|
81
|
+
days.push({
|
|
82
|
+
date: new Date(currentMonth.getFullYear(), currentMonth.getMonth(), i),
|
|
83
|
+
isCurrentMonth: true,
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Next month days to fill the grid
|
|
88
|
+
const remainingDays = 42 - days.length // 6 rows * 7 days
|
|
89
|
+
const nextMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)
|
|
90
|
+
for (let i = 1; i <= remainingDays; i++) {
|
|
91
|
+
days.push({
|
|
92
|
+
date: new Date(nextMonth.getFullYear(), nextMonth.getMonth(), i),
|
|
93
|
+
isCurrentMonth: false,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return days
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function previousMonth() {
|
|
101
|
+
currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function nextMonth() {
|
|
105
|
+
currentMonth = new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function selectDate(date) {
|
|
109
|
+
if (isDisabled(date)) return
|
|
110
|
+
selectedDate = date
|
|
111
|
+
onChange?.(date)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function goToToday() {
|
|
115
|
+
currentMonth = new Date(today)
|
|
116
|
+
selectDate(today)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Base classes
|
|
120
|
+
const baseClasses = 'bg-white dark:bg-gray-800 rounded-lg shadow-lg p-4 w-full max-w-sm'
|
|
121
|
+
const calendarClasses = `${baseClasses} ${className}`.trim()
|
|
122
|
+
|
|
123
|
+
const headerClasses = 'flex items-center justify-between mb-4'
|
|
124
|
+
const monthYearClasses = 'text-lg font-semibold text-gray-900 dark:text-gray-100'
|
|
125
|
+
const buttonClasses = 'p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors'
|
|
126
|
+
|
|
127
|
+
const weekdaysClasses = 'grid grid-cols-7 gap-1 mb-2'
|
|
128
|
+
const weekdayClasses = 'text-center text-xs font-medium text-gray-600 dark:text-gray-400 py-2'
|
|
129
|
+
|
|
130
|
+
const daysGridClasses = 'grid grid-cols-7 gap-1'
|
|
131
|
+
|
|
132
|
+
function getDayClasses(day) {
|
|
133
|
+
const base = 'flex items-center justify-center h-10 w-10 rounded-md text-sm transition-colors'
|
|
134
|
+
const isSelected = isSameDay(day.date, selectedDate)
|
|
135
|
+
const isTodayDate = isToday(day.date)
|
|
136
|
+
const disabled = isDisabled(day.date)
|
|
137
|
+
|
|
138
|
+
let classes = base
|
|
139
|
+
|
|
140
|
+
if (!day.isCurrentMonth) {
|
|
141
|
+
classes += ' text-gray-400 dark:text-gray-600'
|
|
142
|
+
} else {
|
|
143
|
+
classes += ' text-gray-900 dark:text-gray-100'
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (disabled) {
|
|
147
|
+
classes += ' opacity-50 cursor-not-allowed'
|
|
148
|
+
} else {
|
|
149
|
+
classes += ' cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700'
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (isSelected) {
|
|
153
|
+
classes += ' bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600'
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (isTodayDate && highlightToday && !isSelected) {
|
|
157
|
+
classes += ' border-2 border-blue-600 dark:border-blue-400'
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return classes
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const dayNames = getDayNames()
|
|
164
|
+
const calendarDays = getCalendarDays()
|
|
165
|
+
</script>
|
|
166
|
+
|
|
167
|
+
<div class="{{ calendarClasses }}">
|
|
168
|
+
<!-- Header -->
|
|
169
|
+
<div class="{{ headerClasses }}">
|
|
170
|
+
<button
|
|
171
|
+
type="button"
|
|
172
|
+
class="{{ buttonClasses }}"
|
|
173
|
+
onclick="previousMonth()"
|
|
174
|
+
aria-label="Previous month"
|
|
175
|
+
>
|
|
176
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
177
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
|
178
|
+
</svg>
|
|
179
|
+
</button>
|
|
180
|
+
|
|
181
|
+
<div class="{{ monthYearClasses }}">
|
|
182
|
+
{{ getMonthName(currentMonth) }} {{ getYear(currentMonth) }}
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<button
|
|
186
|
+
type="button"
|
|
187
|
+
class="{{ buttonClasses }}"
|
|
188
|
+
onclick="nextMonth()"
|
|
189
|
+
aria-label="Next month"
|
|
190
|
+
>
|
|
191
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
192
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
193
|
+
</svg>
|
|
194
|
+
</button>
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
<!-- Weekday names -->
|
|
198
|
+
<div class="{{ weekdaysClasses }}">
|
|
199
|
+
@foreach(dayNames as day)
|
|
200
|
+
<div class="{{ weekdayClasses }}">{{ day }}</div>
|
|
201
|
+
@endforeach
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<!-- Calendar grid -->
|
|
205
|
+
<div class="{{ daysGridClasses }}">
|
|
206
|
+
@foreach(calendarDays as day)
|
|
207
|
+
<button
|
|
208
|
+
type="button"
|
|
209
|
+
class="{{ getDayClasses(day) }}"
|
|
210
|
+
onclick="selectDate(day.date)"
|
|
211
|
+
aria-label="{{ day.date.toLocaleDateString(locale) }}"
|
|
212
|
+
@if(isDisabled(day.date))
|
|
213
|
+
disabled
|
|
214
|
+
@endif
|
|
215
|
+
>
|
|
216
|
+
{{ day.date.getDate() }}
|
|
217
|
+
</button>
|
|
218
|
+
@endforeach
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
@if(showToday)
|
|
222
|
+
<div class="mt-4 flex justify-center">
|
|
223
|
+
<button
|
|
224
|
+
type="button"
|
|
225
|
+
class="px-4 py-2 text-sm font-medium text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-gray-700 rounded-md transition-colors"
|
|
226
|
+
onclick="goToToday()"
|
|
227
|
+
>
|
|
228
|
+
Today
|
|
229
|
+
</button>
|
|
230
|
+
</div>
|
|
231
|
+
@endif
|
|
232
|
+
</div>
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Calendar Component
|
|
2
|
+
|
|
3
|
+
A fully-featured calendar/date picker component with support for date selection, range constraints, and localization.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ Date selection with visual feedback
|
|
8
|
+
- ✅ Month/year navigation
|
|
9
|
+
- ✅ Min/max date constraints
|
|
10
|
+
- ✅ Disabled dates support
|
|
11
|
+
- ✅ Localization support
|
|
12
|
+
- ✅ Configurable first day of week
|
|
13
|
+
- ✅ Today button for quick navigation
|
|
14
|
+
- ✅ Today highlighting
|
|
15
|
+
- ✅ Dark mode support
|
|
16
|
+
- ✅ Accessible with ARIA labels
|
|
17
|
+
- ✅ Keyboard navigation ready
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Basic Calendar
|
|
22
|
+
|
|
23
|
+
```stx
|
|
24
|
+
<script>
|
|
25
|
+
import { Calendar } from '@stacksjs/components'
|
|
26
|
+
|
|
27
|
+
let selectedDate = new Date()
|
|
28
|
+
|
|
29
|
+
function handleDateChange(date) {
|
|
30
|
+
selectedDate = date
|
|
31
|
+
console.log('Selected date:', date)
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<Calendar value={selectedDate} onChange={handleDateChange} />
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### With Date Constraints
|
|
39
|
+
|
|
40
|
+
```stx
|
|
41
|
+
<script>
|
|
42
|
+
import { Calendar } from '@stacksjs/components'
|
|
43
|
+
|
|
44
|
+
const today = new Date()
|
|
45
|
+
const minDate = new Date(today.getFullYear(), today.getMonth(), 1) // First day of month
|
|
46
|
+
const maxDate = new Date(today.getFullYear(), today.getMonth() + 1, 0) // Last day of month
|
|
47
|
+
|
|
48
|
+
let selectedDate = today
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<Calendar
|
|
52
|
+
value={selectedDate}
|
|
53
|
+
minDate={minDate}
|
|
54
|
+
maxDate={maxDate}
|
|
55
|
+
onChange={(date) => selectedDate = date}
|
|
56
|
+
/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### With Disabled Dates
|
|
60
|
+
|
|
61
|
+
```stx
|
|
62
|
+
<script>
|
|
63
|
+
import { Calendar } from '@stacksjs/components'
|
|
64
|
+
|
|
65
|
+
const disabledDates = [
|
|
66
|
+
new Date(2024, 11, 25), // Christmas
|
|
67
|
+
new Date(2024, 0, 1), // New Year's Day
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
let selectedDate = new Date()
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<Calendar
|
|
74
|
+
value={selectedDate}
|
|
75
|
+
disabledDates={disabledDates}
|
|
76
|
+
onChange={(date) => selectedDate = date}
|
|
77
|
+
/>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Localized Calendar
|
|
81
|
+
|
|
82
|
+
```stx
|
|
83
|
+
<script>
|
|
84
|
+
import { Calendar } from '@stacksjs/components'
|
|
85
|
+
|
|
86
|
+
let selectedDate = new Date()
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<!-- Spanish calendar starting on Monday -->
|
|
90
|
+
<Calendar
|
|
91
|
+
value={selectedDate}
|
|
92
|
+
locale="es-ES"
|
|
93
|
+
firstDayOfWeek={1}
|
|
94
|
+
onChange={(date) => selectedDate = date}
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Without Today Button
|
|
99
|
+
|
|
100
|
+
```stx
|
|
101
|
+
<script>
|
|
102
|
+
import { Calendar } from '@stacksjs/components'
|
|
103
|
+
|
|
104
|
+
let selectedDate = new Date()
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<Calendar
|
|
108
|
+
value={selectedDate}
|
|
109
|
+
showToday={false}
|
|
110
|
+
highlightToday={false}
|
|
111
|
+
onChange={(date) => selectedDate = date}
|
|
112
|
+
/>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Props
|
|
116
|
+
|
|
117
|
+
| Prop | Type | Default | Description |
|
|
118
|
+
|------|------|---------|-------------|
|
|
119
|
+
| `value` | `Date` | `new Date()` | Currently selected date |
|
|
120
|
+
| `onChange` | `(date: Date) => void` | - | Callback when a date is selected |
|
|
121
|
+
| `minDate` | `Date` | - | Minimum selectable date |
|
|
122
|
+
| `maxDate` | `Date` | - | Maximum selectable date |
|
|
123
|
+
| `disabledDates` | `Date[]` | `[]` | Array of dates that cannot be selected |
|
|
124
|
+
| `locale` | `string` | `'en-US'` | Locale for month/day names (e.g., 'en-US', 'es-ES', 'fr-FR') |
|
|
125
|
+
| `firstDayOfWeek` | `number` | `0` | First day of week (0 = Sunday, 1 = Monday, etc.) |
|
|
126
|
+
| `showToday` | `boolean` | `true` | Show "Today" button |
|
|
127
|
+
| `highlightToday` | `boolean` | `true` | Highlight today's date with a border |
|
|
128
|
+
| `className` | `string` | `''` | Additional CSS classes |
|
|
129
|
+
|
|
130
|
+
## Examples
|
|
131
|
+
|
|
132
|
+
### Date Range Selector (Start Date)
|
|
133
|
+
|
|
134
|
+
```stx
|
|
135
|
+
<script>
|
|
136
|
+
import { Calendar } from '@stacksjs/components'
|
|
137
|
+
|
|
138
|
+
let startDate = null
|
|
139
|
+
let endDate = null
|
|
140
|
+
|
|
141
|
+
function handleStartDateChange(date) {
|
|
142
|
+
startDate = date
|
|
143
|
+
if (endDate && date > endDate) {
|
|
144
|
+
endDate = null
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
<div>
|
|
150
|
+
<h3>Select Start Date</h3>
|
|
151
|
+
<Calendar
|
|
152
|
+
value={startDate}
|
|
153
|
+
maxDate={endDate}
|
|
154
|
+
onChange={handleStartDateChange}
|
|
155
|
+
/>
|
|
156
|
+
</div>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Meeting Scheduler (Exclude Weekends)
|
|
160
|
+
|
|
161
|
+
```stx
|
|
162
|
+
<script>
|
|
163
|
+
import { Calendar } from '@stacksjs/components'
|
|
164
|
+
|
|
165
|
+
function getNextNDays(n) {
|
|
166
|
+
const dates = []
|
|
167
|
+
const today = new Date()
|
|
168
|
+
for (let i = 0; i < n; i++) {
|
|
169
|
+
const date = new Date(today)
|
|
170
|
+
date.setDate(today.getDate() + i)
|
|
171
|
+
dates.push(date)
|
|
172
|
+
}
|
|
173
|
+
return dates
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function getWeekends(days) {
|
|
177
|
+
return days.filter(date => date.getDay() === 0 || date.getDay() === 6)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const next60Days = getNextNDays(60)
|
|
181
|
+
const weekends = getWeekends(next60Days)
|
|
182
|
+
|
|
183
|
+
let meetingDate = null
|
|
184
|
+
</script>
|
|
185
|
+
|
|
186
|
+
<Calendar
|
|
187
|
+
value={meetingDate}
|
|
188
|
+
minDate={new Date()}
|
|
189
|
+
disabledDates={weekends}
|
|
190
|
+
onChange={(date) => meetingDate = date}
|
|
191
|
+
/>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Birthday Picker
|
|
195
|
+
|
|
196
|
+
```stx
|
|
197
|
+
<script>
|
|
198
|
+
import { Calendar } from '@stacksjs/components'
|
|
199
|
+
|
|
200
|
+
const today = new Date()
|
|
201
|
+
const minDate = new Date(today.getFullYear() - 120, 0, 1) // 120 years ago
|
|
202
|
+
const maxDate = new Date(today.getFullYear() - 18, today.getMonth(), today.getDate()) // 18+ only
|
|
203
|
+
|
|
204
|
+
let birthday = null
|
|
205
|
+
</script>
|
|
206
|
+
|
|
207
|
+
<Calendar
|
|
208
|
+
value={birthday}
|
|
209
|
+
minDate={minDate}
|
|
210
|
+
maxDate={maxDate}
|
|
211
|
+
onChange={(date) => birthday = date}
|
|
212
|
+
/>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Styling
|
|
216
|
+
|
|
217
|
+
The calendar uses utility-first CSS classes and supports dark mode out of the box. You can customize the appearance by passing additional classes:
|
|
218
|
+
|
|
219
|
+
```stx
|
|
220
|
+
<Calendar
|
|
221
|
+
className="border-2 border-gray-300 shadow-xl"
|
|
222
|
+
value={selectedDate}
|
|
223
|
+
onChange={handleChange}
|
|
224
|
+
/>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Key Classes
|
|
228
|
+
|
|
229
|
+
- Container: `bg-white dark:bg-gray-800 rounded-lg shadow-lg p-4`
|
|
230
|
+
- Selected date: `bg-blue-600 text-white`
|
|
231
|
+
- Today: `border-2 border-blue-600` (when not selected)
|
|
232
|
+
- Disabled: `opacity-50 cursor-not-allowed`
|
|
233
|
+
- Hover: `hover:bg-gray-100 dark:hover:bg-gray-700`
|
|
234
|
+
|
|
235
|
+
## Accessibility
|
|
236
|
+
|
|
237
|
+
- Semantic HTML with proper ARIA labels
|
|
238
|
+
- Keyboard navigation support (ready for enhancement)
|
|
239
|
+
- Disabled dates are properly marked with `disabled` attribute
|
|
240
|
+
- Clear visual indicators for selected and current dates
|
|
241
|
+
|
|
242
|
+
## Browser Support
|
|
243
|
+
|
|
244
|
+
Works in all modern browsers that support:
|
|
245
|
+
- ES6+ JavaScript features
|
|
246
|
+
- CSS Grid
|
|
247
|
+
- CSS custom properties (for dark mode)
|
|
248
|
+
|
|
249
|
+
## Related Components
|
|
250
|
+
|
|
251
|
+
- **DatePicker**: Combines Calendar with an input field
|
|
252
|
+
- **DateRangePicker**: Select start and end dates
|
|
253
|
+
- **TimePicker**: Select time values
|
|
254
|
+
- **DateTimePicker**: Combines date and time selection
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { default as Calendar } from './Calendar.stx'
|
|
2
|
+
|
|
3
|
+
export interface CalendarProps {
|
|
4
|
+
value?: Date
|
|
5
|
+
onChange?: (date: Date) => void
|
|
6
|
+
minDate?: Date
|
|
7
|
+
maxDate?: Date
|
|
8
|
+
disabledDates?: Date[]
|
|
9
|
+
locale?: string
|
|
10
|
+
firstDayOfWeek?: number // 0 = Sunday, 1 = Monday, etc.
|
|
11
|
+
showToday?: boolean
|
|
12
|
+
highlightToday?: boolean
|
|
13
|
+
className?: string
|
|
14
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const variant = $props.variant || 'default'
|
|
3
|
+
export const padding = $props.padding || 'default'
|
|
4
|
+
export const hover = $props.hover || false
|
|
5
|
+
export const clickable = $props.clickable || false
|
|
6
|
+
export const image = $props.image || ''
|
|
7
|
+
export const imageAlt = $props.imageAlt || ''
|
|
8
|
+
export const imagePosition = $props.imagePosition || 'top'
|
|
9
|
+
export const className = $props.className || ''
|
|
10
|
+
export const onClick = $props.onClick
|
|
11
|
+
|
|
12
|
+
// Variant classes
|
|
13
|
+
const variantClasses = {
|
|
14
|
+
default: 'bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700',
|
|
15
|
+
outlined: 'bg-transparent border-2 border-gray-300 dark:border-gray-600',
|
|
16
|
+
elevated: 'bg-white dark:bg-gray-800 shadow-lg border-0',
|
|
17
|
+
flat: 'bg-gray-50 dark:bg-gray-900 border-0',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Padding classes
|
|
21
|
+
const paddingClasses = {
|
|
22
|
+
none: 'p-0',
|
|
23
|
+
sm: 'p-3',
|
|
24
|
+
default: 'p-6',
|
|
25
|
+
lg: 'p-8',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Hover effect classes
|
|
29
|
+
const hoverClasses = hover ? 'transition-all duration-200 hover:shadow-lg hover:-translate-y-0.5' : ''
|
|
30
|
+
|
|
31
|
+
// Clickable classes
|
|
32
|
+
const clickableClasses = clickable ? 'cursor-pointer' : ''
|
|
33
|
+
|
|
34
|
+
export const cardClasses = `rounded-lg overflow-hidden ${variantClasses[variant]} ${paddingClasses[image ? 'none' : padding]} ${hoverClasses} ${clickableClasses} ${className}`.trim()
|
|
35
|
+
export const imageClasses = `w-full h-48 object-cover`.trim()
|
|
36
|
+
export const contentClasses = `${image ? paddingClasses[padding] : ''}`.trim()
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
@if(onClick)
|
|
40
|
+
<div class="{{ cardClasses }}" onclick="handleClick(event)" role="button" tabindex="0" onkeydown="handleKeyDown(event)">
|
|
41
|
+
@if(image)
|
|
42
|
+
<img src="{{ image }}" alt="{{ imageAlt }}" class="{{ imageClasses }}" />
|
|
43
|
+
@endif
|
|
44
|
+
|
|
45
|
+
<div class="{{ contentClasses }}">
|
|
46
|
+
<slot />
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
@else
|
|
50
|
+
<div class="{{ cardClasses }}">
|
|
51
|
+
@if(image)
|
|
52
|
+
<img src="{{ image }}" alt="{{ imageAlt }}" class="{{ imageClasses }}" />
|
|
53
|
+
@endif
|
|
54
|
+
|
|
55
|
+
<div class="{{ contentClasses }}">
|
|
56
|
+
<slot />
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
@endif
|
|
60
|
+
|
|
61
|
+
<script>
|
|
62
|
+
function handleClick(event) {
|
|
63
|
+
if (clickable && onClick) {
|
|
64
|
+
onClick(event)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function handleKeyDown(event) {
|
|
69
|
+
if ((event.key === 'Enter' || event.key === ' ') && clickable && onClick) {
|
|
70
|
+
event.preventDefault()
|
|
71
|
+
onClick(event)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
</script>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { default as Card } from './Card.stx'
|
|
2
|
+
|
|
3
|
+
export interface CardProps {
|
|
4
|
+
variant?: 'default' | 'outlined' | 'elevated' | 'flat'
|
|
5
|
+
padding?: 'none' | 'sm' | 'default' | 'lg'
|
|
6
|
+
hover?: boolean
|
|
7
|
+
clickable?: boolean
|
|
8
|
+
image?: string
|
|
9
|
+
imageAlt?: string
|
|
10
|
+
imagePosition?: 'top' | 'bottom'
|
|
11
|
+
onClick?: (event: Event) => void
|
|
12
|
+
className?: string
|
|
13
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const checked = $props.checked || false
|
|
3
|
+
export const onChange = $props.onChange || (() => {})
|
|
4
|
+
export const disabled = $props.disabled || false
|
|
5
|
+
export const indeterminate = $props.indeterminate || false
|
|
6
|
+
export const size = $props.size || 'md'
|
|
7
|
+
export const label = $props.label || ''
|
|
8
|
+
export const description = $props.description || ''
|
|
9
|
+
export const error = $props.error || false
|
|
10
|
+
export const required = $props.required || false
|
|
11
|
+
export const value = $props.value || ''
|
|
12
|
+
export const name = $props.name || ''
|
|
13
|
+
export const className = $props.className || ''
|
|
14
|
+
|
|
15
|
+
// Size classes
|
|
16
|
+
const sizeClasses = {
|
|
17
|
+
sm: 'h-4 w-4',
|
|
18
|
+
md: 'h-5 w-5',
|
|
19
|
+
lg: 'h-6 w-6',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Base classes for checkbox
|
|
23
|
+
const baseClasses = 'rounded border-gray-300 dark:border-gray-600 text-indigo-600 dark:text-indigo-500 focus:ring-indigo-600 dark:focus:ring-indigo-500 focus:ring-2 focus:ring-offset-2 dark:focus:ring-offset-gray-900 transition-colors'
|
|
24
|
+
|
|
25
|
+
const stateClasses = error
|
|
26
|
+
? 'border-red-300 dark:border-red-600 text-red-600 dark:text-red-500 focus:ring-red-500 dark:focus:ring-red-400'
|
|
27
|
+
: ''
|
|
28
|
+
|
|
29
|
+
const disabledClasses = disabled
|
|
30
|
+
? 'opacity-50 cursor-not-allowed'
|
|
31
|
+
: 'cursor-pointer'
|
|
32
|
+
|
|
33
|
+
export const checkboxClasses = `${baseClasses} ${sizeClasses[size]} ${stateClasses} ${disabledClasses}`.trim()
|
|
34
|
+
export const labelClasses = `text-sm font-medium ${error ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-gray-100'} ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`.trim()
|
|
35
|
+
export const descriptionClasses = `text-sm ${error ? 'text-red-500 dark:text-red-400' : 'text-gray-500 dark:text-gray-400'}`.trim()
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<div class="relative flex items-start {{ className }}">
|
|
39
|
+
<div class="flex h-6 items-center">
|
|
40
|
+
<input
|
|
41
|
+
id="checkbox-{{ value || label }}"
|
|
42
|
+
type="checkbox"
|
|
43
|
+
name="{{ name }}"
|
|
44
|
+
value="{{ value }}"
|
|
45
|
+
checked="{{ checked }}"
|
|
46
|
+
disabled="{{ disabled }}"
|
|
47
|
+
required="{{ required }}"
|
|
48
|
+
class="{{ checkboxClasses }}"
|
|
49
|
+
onchange="handleChange(event)"
|
|
50
|
+
aria-invalid="{{ error ? 'true' : 'false' }}"
|
|
51
|
+
@if(description)aria-describedby="checkbox-description"@endif
|
|
52
|
+
/>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
@if(label || description)
|
|
56
|
+
<div class="ml-3 text-sm leading-6">
|
|
57
|
+
@if(label)
|
|
58
|
+
<label for="checkbox-{{ value || label }}" class="{{ labelClasses }}">
|
|
59
|
+
{{ label }}
|
|
60
|
+
@if(required)
|
|
61
|
+
<span class="text-red-500 ml-1">*</span>
|
|
62
|
+
@endif
|
|
63
|
+
</label>
|
|
64
|
+
@endif
|
|
65
|
+
@if(description)
|
|
66
|
+
<p id="checkbox-description" class="{{ descriptionClasses }}">
|
|
67
|
+
{{ description }}
|
|
68
|
+
</p>
|
|
69
|
+
@endif
|
|
70
|
+
</div>
|
|
71
|
+
@endif
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<script>
|
|
75
|
+
function handleChange(event) {
|
|
76
|
+
const isChecked = event.target.checked
|
|
77
|
+
|
|
78
|
+
if (!disabled && onChange) {
|
|
79
|
+
onChange(isChecked)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Handle indeterminate state
|
|
84
|
+
if (indeterminate && typeof window !== 'undefined') {
|
|
85
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
86
|
+
const checkbox = document.getElementById('checkbox-{{ value || label }}')
|
|
87
|
+
if (checkbox) {
|
|
88
|
+
checkbox.indeterminate = true
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
</script>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { default as Checkbox } from './Checkbox.stx'
|
|
2
|
+
|
|
3
|
+
export interface CheckboxProps {
|
|
4
|
+
checked?: boolean
|
|
5
|
+
onChange?: (checked: boolean) => void
|
|
6
|
+
disabled?: boolean
|
|
7
|
+
indeterminate?: boolean
|
|
8
|
+
size?: 'sm' | 'md' | 'lg'
|
|
9
|
+
label?: string
|
|
10
|
+
description?: string
|
|
11
|
+
error?: boolean
|
|
12
|
+
required?: boolean
|
|
13
|
+
value?: string
|
|
14
|
+
name?: string
|
|
15
|
+
className?: string
|
|
16
|
+
}
|