@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,88 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const src = $props.src || ''
|
|
3
|
+
export const alt = $props.alt || ''
|
|
4
|
+
export const initials = $props.initials || ''
|
|
5
|
+
export const size = $props.size || 'md'
|
|
6
|
+
export const shape = $props.shape || 'circle'
|
|
7
|
+
export const status = $props.status || ''
|
|
8
|
+
export const className = $props.className || ''
|
|
9
|
+
export const onError = $props.onError
|
|
10
|
+
|
|
11
|
+
let imageError = false
|
|
12
|
+
|
|
13
|
+
// Size classes
|
|
14
|
+
const sizeClasses = {
|
|
15
|
+
xs: 'w-6 h-6 text-xs',
|
|
16
|
+
sm: 'w-8 h-8 text-sm',
|
|
17
|
+
md: 'w-10 h-10 text-base',
|
|
18
|
+
lg: 'w-12 h-12 text-lg',
|
|
19
|
+
xl: 'w-16 h-16 text-xl',
|
|
20
|
+
'2xl': 'w-20 h-20 text-2xl',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Shape classes
|
|
24
|
+
const shapeClasses = {
|
|
25
|
+
circle: 'rounded-full',
|
|
26
|
+
square: 'rounded-md',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Status indicator size classes
|
|
30
|
+
const statusSizeClasses = {
|
|
31
|
+
xs: 'w-1.5 h-1.5 border',
|
|
32
|
+
sm: 'w-2 h-2 border',
|
|
33
|
+
md: 'w-2.5 h-2.5 border-2',
|
|
34
|
+
lg: 'w-3 h-3 border-2',
|
|
35
|
+
xl: 'w-3.5 h-3.5 border-2',
|
|
36
|
+
'2xl': 'w-4 h-4 border-2',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Status color classes
|
|
40
|
+
const statusColorClasses = {
|
|
41
|
+
online: 'bg-green-500 dark:bg-green-400',
|
|
42
|
+
offline: 'bg-gray-400 dark:bg-gray-600',
|
|
43
|
+
away: 'bg-yellow-500 dark:bg-yellow-400',
|
|
44
|
+
busy: 'bg-red-500 dark:bg-red-400',
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const containerClasses = `relative inline-block ${className}`.trim()
|
|
48
|
+
export const avatarClasses = `flex items-center justify-center overflow-hidden bg-gray-200 dark:bg-gray-700 ${sizeClasses[size]} ${shapeClasses[shape]}`.trim()
|
|
49
|
+
export const imageClasses = `w-full h-full object-cover`.trim()
|
|
50
|
+
export const initialsClasses = `font-medium text-gray-600 dark:text-gray-300 uppercase select-none`.trim()
|
|
51
|
+
export const statusClasses = `absolute bottom-0 right-0 ${statusSizeClasses[size]} ${statusColorClasses[status]} ${shapeClasses[shape]} border-white dark:border-gray-900`.trim()
|
|
52
|
+
|
|
53
|
+
// Generate initials from name if not provided
|
|
54
|
+
export const displayInitials = initials || (alt ? alt.split(' ').map(n => n[0]).join('').substring(0, 2) : '?')
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<div class="{{ containerClasses }}">
|
|
58
|
+
<div class="{{ avatarClasses }}">
|
|
59
|
+
@if(src && !imageError)
|
|
60
|
+
<img
|
|
61
|
+
src="{{ src }}"
|
|
62
|
+
alt="{{ alt }}"
|
|
63
|
+
class="{{ imageClasses }}"
|
|
64
|
+
onerror="handleImageError(event)"
|
|
65
|
+
/>
|
|
66
|
+
@else
|
|
67
|
+
<span class="{{ initialsClasses }}">{{ displayInitials }}</span>
|
|
68
|
+
@endif
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
@if(status)
|
|
72
|
+
<span
|
|
73
|
+
class="{{ statusClasses }}"
|
|
74
|
+
role="status"
|
|
75
|
+
aria-label="{{ status }}"
|
|
76
|
+
></span>
|
|
77
|
+
@endif
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<script>
|
|
81
|
+
function handleImageError(event) {
|
|
82
|
+
imageError = true
|
|
83
|
+
|
|
84
|
+
if (onError) {
|
|
85
|
+
onError(event)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
</script>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const variant = $props.variant || 'default'
|
|
3
|
+
export const size = $props.size || 'md'
|
|
4
|
+
export const dot = $props.dot || false
|
|
5
|
+
export const removable = $props.removable || false
|
|
6
|
+
export const className = $props.className || ''
|
|
7
|
+
export const onRemove = $props.onRemove
|
|
8
|
+
|
|
9
|
+
// Variant classes
|
|
10
|
+
const variantClasses = {
|
|
11
|
+
default: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300',
|
|
12
|
+
primary: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200',
|
|
13
|
+
secondary: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200',
|
|
14
|
+
success: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
|
|
15
|
+
warning: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200',
|
|
16
|
+
danger: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200',
|
|
17
|
+
info: 'bg-cyan-100 text-cyan-800 dark:bg-cyan-900 dark:text-cyan-200',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Size classes
|
|
21
|
+
const sizeClasses = {
|
|
22
|
+
sm: 'px-2 py-0.5 text-xs',
|
|
23
|
+
md: 'px-2.5 py-0.5 text-sm',
|
|
24
|
+
lg: 'px-3 py-1 text-base',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Dot color classes
|
|
28
|
+
const dotColorClasses = {
|
|
29
|
+
default: 'bg-gray-500 dark:bg-gray-400',
|
|
30
|
+
primary: 'bg-blue-500 dark:bg-blue-400',
|
|
31
|
+
secondary: 'bg-purple-500 dark:bg-purple-400',
|
|
32
|
+
success: 'bg-green-500 dark:bg-green-400',
|
|
33
|
+
warning: 'bg-yellow-500 dark:bg-yellow-400',
|
|
34
|
+
danger: 'bg-red-500 dark:bg-red-400',
|
|
35
|
+
info: 'bg-cyan-500 dark:bg-cyan-400',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const badgeClasses = `inline-flex items-center gap-1.5 font-medium rounded-full ${variantClasses[variant]} ${sizeClasses[size]} ${className}`.trim()
|
|
39
|
+
export const dotClasses = `w-2 h-2 rounded-full ${dotColorClasses[variant]}`.trim()
|
|
40
|
+
export const removeButtonClasses = `ml-0.5 -mr-1 text-current hover:bg-black hover:bg-opacity-10 dark:hover:bg-white dark:hover:bg-opacity-10 rounded-full p-0.5 transition-colors duration-150`.trim()
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<span class="{{ badgeClasses }}" role="status">
|
|
44
|
+
@if(dot)
|
|
45
|
+
<span class="{{ dotClasses }}" aria-hidden="true"></span>
|
|
46
|
+
@endif
|
|
47
|
+
|
|
48
|
+
<slot />
|
|
49
|
+
|
|
50
|
+
@if(removable && onRemove)
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
onclick="handleRemove()"
|
|
54
|
+
class="{{ removeButtonClasses }}"
|
|
55
|
+
aria-label="Remove"
|
|
56
|
+
>
|
|
57
|
+
<svg class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
58
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
59
|
+
</svg>
|
|
60
|
+
</button>
|
|
61
|
+
@endif
|
|
62
|
+
</span>
|
|
63
|
+
|
|
64
|
+
<script>
|
|
65
|
+
function handleRemove() {
|
|
66
|
+
if (onRemove) {
|
|
67
|
+
onRemove()
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const items = $props.items || []
|
|
3
|
+
export const separator = $props.separator || '/'
|
|
4
|
+
export const maxItems = $props.maxItems || 0
|
|
5
|
+
export const className = $props.className || ''
|
|
6
|
+
|
|
7
|
+
// Handle collapse if maxItems is set
|
|
8
|
+
let displayItems = items
|
|
9
|
+
if (maxItems > 0 && items.length > maxItems) {
|
|
10
|
+
const firstItems = items.slice(0, 1)
|
|
11
|
+
const lastItems = items.slice(-(maxItems - 1))
|
|
12
|
+
displayItems = [...firstItems, { label: '...', href: '#', collapsed: true }, ...lastItems]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Separator element
|
|
16
|
+
const separatorElement = separator === '/' ? '/' :
|
|
17
|
+
separator === '>' ? '›' :
|
|
18
|
+
separator === 'arrow' ? `<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /></svg>` :
|
|
19
|
+
separator
|
|
20
|
+
|
|
21
|
+
export const breadcrumbClasses = `flex items-center flex-wrap gap-2 text-sm ${className}`.trim()
|
|
22
|
+
export const itemClasses = `text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 transition-colors duration-150`.trim()
|
|
23
|
+
export const activeItemClasses = `text-gray-900 dark:text-gray-100 font-medium`.trim()
|
|
24
|
+
export const separatorClasses = `text-gray-400 dark:text-gray-600 select-none`.trim()
|
|
25
|
+
export const collapsedClasses = `text-gray-400 dark:text-gray-600 cursor-default`.trim()
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<nav class="{{ breadcrumbClasses }}" aria-label="Breadcrumb">
|
|
29
|
+
<ol class="flex items-center gap-2">
|
|
30
|
+
@foreach(item in displayItems)
|
|
31
|
+
<li class="flex items-center gap-2">
|
|
32
|
+
@if(item.collapsed)
|
|
33
|
+
<!-- Collapsed indicator -->
|
|
34
|
+
<span class="{{ collapsedClasses }}">{{ item.label }}</span>
|
|
35
|
+
@else
|
|
36
|
+
@if($loop.last)
|
|
37
|
+
<!-- Last item (current page) -->
|
|
38
|
+
<span class="{{ activeItemClasses }}" aria-current="page">
|
|
39
|
+
@if(item.icon)
|
|
40
|
+
<span class="inline-flex items-center gap-1.5">
|
|
41
|
+
<span innerHTML="{!! item.icon !!}"></span>
|
|
42
|
+
<span>{{ item.label }}</span>
|
|
43
|
+
</span>
|
|
44
|
+
@else
|
|
45
|
+
{{ item.label }}
|
|
46
|
+
@endif
|
|
47
|
+
</span>
|
|
48
|
+
@else
|
|
49
|
+
<!-- Link item -->
|
|
50
|
+
<a
|
|
51
|
+
href="{{ item.href }}"
|
|
52
|
+
class="{{ itemClasses }}"
|
|
53
|
+
>
|
|
54
|
+
@if(item.icon)
|
|
55
|
+
<span class="inline-flex items-center gap-1.5">
|
|
56
|
+
<span innerHTML="{!! item.icon !!}"></span>
|
|
57
|
+
<span>{{ item.label }}</span>
|
|
58
|
+
</span>
|
|
59
|
+
@else
|
|
60
|
+
{{ item.label }}
|
|
61
|
+
@endif
|
|
62
|
+
</a>
|
|
63
|
+
@endif
|
|
64
|
+
@endif
|
|
65
|
+
|
|
66
|
+
<!-- Separator (not after last item) -->
|
|
67
|
+
@if(!$loop.last)
|
|
68
|
+
<span class="{{ separatorClasses }}" aria-hidden="true">
|
|
69
|
+
@if(separator === 'arrow')
|
|
70
|
+
{!! separatorElement !!}
|
|
71
|
+
@else
|
|
72
|
+
{{ separatorElement }}
|
|
73
|
+
@endif
|
|
74
|
+
</span>
|
|
75
|
+
@endif
|
|
76
|
+
</li>
|
|
77
|
+
@endforeach
|
|
78
|
+
</ol>
|
|
79
|
+
</nav>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export const variant = $props.variant || 'primary'
|
|
3
|
+
export const size = $props.size || 'md'
|
|
4
|
+
export const disabled = $props.disabled || false
|
|
5
|
+
export const loading = $props.loading || false
|
|
6
|
+
export const type = $props.type || 'button'
|
|
7
|
+
export const fullWidth = $props.fullWidth || false
|
|
8
|
+
export const leftIcon = $props.leftIcon || ''
|
|
9
|
+
export const rightIcon = $props.rightIcon || ''
|
|
10
|
+
export const className = $props.className || ''
|
|
11
|
+
export const onClick = $props.onClick
|
|
12
|
+
|
|
13
|
+
// Variant styles using headwind
|
|
14
|
+
const variantClasses = {
|
|
15
|
+
primary: 'bg-blue-500 hover:bg-blue-600 text-white shadow-md hover:shadow-lg dark:bg-blue-600 dark:hover:bg-blue-700',
|
|
16
|
+
secondary: 'bg-neutral-200 hover:bg-neutral-300 text-neutral-900 dark:bg-neutral-700 dark:hover:bg-neutral-600 dark:text-white',
|
|
17
|
+
outline: 'border-2 border-blue-500 text-blue-500 hover:bg-blue-50 dark:border-blue-400 dark:text-blue-400 dark:hover:bg-blue-950',
|
|
18
|
+
ghost: 'hover:bg-neutral-100 text-neutral-700 dark:hover:bg-neutral-800 dark:text-neutral-300',
|
|
19
|
+
danger: 'bg-red-500 hover:bg-red-600 text-white shadow-md hover:shadow-lg dark:bg-red-600 dark:hover:bg-red-700',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Size styles using headwind
|
|
23
|
+
const sizeClasses = {
|
|
24
|
+
xs: 'px-2.5 py-1 text-xs h-7 gap-1',
|
|
25
|
+
sm: 'px-3 py-1.5 text-sm h-8 gap-1.5',
|
|
26
|
+
md: 'px-4 py-2 text-base h-10 gap-2',
|
|
27
|
+
lg: 'px-6 py-3 text-lg h-12 gap-2.5',
|
|
28
|
+
xl: 'px-8 py-4 text-xl h-14 gap-3',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Icon size styles
|
|
32
|
+
const iconSizeClasses = {
|
|
33
|
+
xs: 'h-3 w-3',
|
|
34
|
+
sm: 'h-4 w-4',
|
|
35
|
+
md: 'h-5 w-5',
|
|
36
|
+
lg: 'h-6 w-6',
|
|
37
|
+
xl: 'h-7 w-7',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Base classes
|
|
41
|
+
const baseClasses = 'inline-flex items-center justify-center font-semibold rounded-md transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none'
|
|
42
|
+
|
|
43
|
+
// Width classes
|
|
44
|
+
const widthClasses = fullWidth ? 'w-full' : ''
|
|
45
|
+
|
|
46
|
+
// Loading state disables interactions
|
|
47
|
+
const isDisabled = disabled || loading
|
|
48
|
+
|
|
49
|
+
// Combine classes
|
|
50
|
+
export const buttonClasses = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${widthClasses} ${className}`.trim()
|
|
51
|
+
export const iconClasses = iconSizeClasses[size]
|
|
52
|
+
export const spinnerClasses = `animate-spin ${iconClasses}`.trim()
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<button
|
|
56
|
+
type="{{ type }}"
|
|
57
|
+
class="{{ buttonClasses }}"
|
|
58
|
+
@if(isDisabled)disabled@endif
|
|
59
|
+
@if(onClick)onclick="handleClick(event)"@endif
|
|
60
|
+
aria-busy="{{ loading ? 'true' : 'false' }}"
|
|
61
|
+
>
|
|
62
|
+
@if(loading)
|
|
63
|
+
<!-- Loading spinner -->
|
|
64
|
+
<svg class="{{ spinnerClasses }}" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
65
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
66
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
67
|
+
</svg>
|
|
68
|
+
@else
|
|
69
|
+
@if(leftIcon)
|
|
70
|
+
<!-- Left icon -->
|
|
71
|
+
<span class="{{ iconClasses }}" innerHTML="{!! leftIcon !!}"></span>
|
|
72
|
+
@endif
|
|
73
|
+
@endif
|
|
74
|
+
|
|
75
|
+
<slot />
|
|
76
|
+
|
|
77
|
+
@if(!loading && rightIcon)
|
|
78
|
+
<!-- Right icon -->
|
|
79
|
+
<span class="{{ iconClasses }}" innerHTML="{!! rightIcon !!}"></span>
|
|
80
|
+
@endif
|
|
81
|
+
</button>
|
|
82
|
+
|
|
83
|
+
<script>
|
|
84
|
+
function handleClick(event) {
|
|
85
|
+
if (!isDisabled && onClick) {
|
|
86
|
+
onClick(event)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
</script>
|
|
@@ -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,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>
|