@rotki/ui-library 2.22.2 → 2.23.0
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/components/avatars/RuiAvatar.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/avatars/RuiAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/buttons/button/RuiButton.vue.d.ts +1 -1
- package/dist/components/buttons/button-group/RuiButtonGroup.vue.d.ts +1 -1
- package/dist/components/calendar/RuiCalendar.js.map +1 -1
- package/dist/components/calendar/RuiCalendar.vue_vue_type_script_setup_true_lang.js +3 -1
- package/dist/components/calendar/RuiCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/calendar/RuiCalendarGrid.js.map +1 -1
- package/dist/components/calendar/RuiCalendarGrid.vue_vue_type_script_setup_true_lang.js +121 -25
- package/dist/components/calendar/RuiCalendarGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/calendar/RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js +11 -4
- package/dist/components/calendar/RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePicker.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePicker.vue.d.ts +23 -1
- package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js +136 -24
- package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePickerMenu.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePickerMenu.vue.d.ts +7 -0
- package/dist/components/date-time-picker/RuiDateTimePickerMenu.vue_vue_type_script_setup_true_lang.js +70 -25
- package/dist/components/date-time-picker/RuiDateTimePickerMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/RuiTimezoneSelect.js.map +1 -1
- package/dist/components/date-time-picker/RuiTimezoneSelect.vue_vue_type_script_setup_true_lang.js +7 -3
- package/dist/components/date-time-picker/RuiTimezoneSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/segment-utils.d.ts +28 -0
- package/dist/components/date-time-picker/segment-utils.js +42 -1
- package/dist/components/date-time-picker/segment-utils.js.map +1 -1
- package/dist/components/date-time-picker/types.d.ts +2 -0
- package/dist/components/date-time-picker/types.js.map +1 -1
- package/dist/components/date-time-picker/use-date-time-selection.d.ts +6 -0
- package/dist/components/date-time-picker/use-date-time-selection.js +65 -42
- package/dist/components/date-time-picker/use-date-time-selection.js.map +1 -1
- package/dist/components/date-time-picker/use-keyboard-handler.js +4 -3
- package/dist/components/date-time-picker/use-keyboard-handler.js.map +1 -1
- package/dist/components/date-time-picker/utils.d.ts +5 -0
- package/dist/components/date-time-picker/utils.js +12 -1
- package/dist/components/date-time-picker/utils.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue.d.ts +1 -1
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js +2 -5
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue.d.ts +1 -1
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js +3 -2
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/checkbox/RuiCheckbox.vue.d.ts +1 -1
- package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue.d.ts +1 -1
- package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/radio-button/radio/RuiRadio.vue.d.ts +1 -1
- package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.vue.d.ts +1 -1
- package/dist/components/forms/select/RuiMenuSelect.vue.d.ts +1 -1
- package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiDataTable.vue.d.ts +1 -1
- package/dist/components/tables/RuiTableHead.vue.d.ts +1 -1
- package/dist/components/tables/data-table/RuiDataTableBody.vue.d.ts +1 -1
- package/dist/components/tables/data-table/RuiDataTableBody.vue_vue_type_script_setup_true_lang.js +2 -8
- package/dist/components/tables/data-table/RuiDataTableBody.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/data-table/RuiDataTableCell.vue.d.ts +1 -1
- package/dist/components/tables/data-table/RuiDataTableExpandedRow.vue.d.ts +1 -1
- package/dist/components/tables/data-table/RuiDataTableGroupRow.vue.d.ts +1 -1
- package/dist/components/tables/data-table/RuiDataTableMobileSort.vue.d.ts +1 -1
- package/dist/components/tables/data-table/RuiDataTableRow.vue.d.ts +1 -1
- package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js +1 -2
- package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/use-pagination-navigation.js.map +1 -1
- package/dist/components/tabs/tab-items/RuiTabItems.vue.d.ts +1 -1
- package/dist/components/tabs/tabs/use-tab-scroll.js +2 -1
- package/dist/components/tabs/tabs/use-tab-scroll.js.map +1 -1
- package/dist/composables/forms/auto-complete/search.d.ts +1 -2
- package/dist/composables/forms/auto-complete/search.js.map +1 -1
- package/dist/composables/forms/auto-complete/value.d.ts +1 -2
- package/dist/composables/forms/auto-complete/value.js.map +1 -1
- package/dist/i18n/keys.d.ts +10 -0
- package/dist/i18n/keys.js +15 -5
- package/dist/i18n/keys.js.map +1 -1
- package/dist/icons/icons_1.d.ts +1 -1
- package/dist/icons/icons_1.js +25 -22
- package/dist/icons/icons_1.js.map +1 -1
- package/dist/icons/icons_2.d.ts +7 -7
- package/dist/icons/icons_2.js +126 -92
- package/dist/icons/icons_2.js.map +1 -1
- package/dist/icons/icons_3.d.ts +12 -12
- package/dist/icons/icons_3.js +119 -177
- package/dist/icons/icons_3.js.map +1 -1
- package/dist/icons/icons_4.d.ts +26 -26
- package/dist/icons/icons_4.js +320 -285
- package/dist/icons/icons_4.js.map +1 -1
- package/dist/icons/icons_5.d.ts +13 -1
- package/dist/icons/icons_5.js +149 -9
- package/dist/icons/icons_5.js.map +1 -1
- package/dist/icons/index.d.ts +1 -1
- package/dist/icons/index.js +34 -22
- package/dist/icons/index.js.map +1 -1
- package/dist/index.js +7 -7
- package/dist/style.css +12 -756
- package/dist/web-types.json +75 -10
- package/package.json +32 -32
|
@@ -169,7 +169,7 @@ var RuiAvatar_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
|
|
|
169
169
|
class: normalizeClass(unref(ui).fallback()),
|
|
170
170
|
"data-id": "avatar-fallback",
|
|
171
171
|
"aria-hidden": "true"
|
|
172
|
-
}, [unref(displayMode) === "slot" ? renderSlot(_ctx.$slots, "default", {
|
|
172
|
+
}, [unref(displayMode) === "slot" ? renderSlot(_ctx.$slots, "default", {}, void 0, void 0, 0) : unref(displayMode) === "initials" ? (openBlock(), createElementBlock("span", {
|
|
173
173
|
key: 1,
|
|
174
174
|
class: normalizeClass(unref(ui).initials()),
|
|
175
175
|
"data-id": "avatar-initials"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiAvatar.vue_vue_type_script_setup_true_lang.js","names":["$attrs"],"sources":["../../../src/components/avatars/RuiAvatar.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport type { ContextColorsType } from '@/consts/colors';\nimport type { RuiIcons } from '@/icons';\nimport { get, set } from '@vueuse/shared';\nimport {\n type AvatarSize,\n type AvatarVariant,\n computeInitials,\n resolveAvatarIconPx,\n resolveAvatarSizePx,\n} from '@/components/avatars/avatar-props';\nimport { useAvatarGroup } from '@/components/avatars/use-avatar-group';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport { tv } from '@/utils/tv';\n\nexport interface Props {\n src?: string | null;\n alt?: string;\n text?: string | null;\n icon?: RuiIcons | null;\n size?: AvatarSize | number;\n variant?: AvatarVariant;\n color?: 'default' | ContextColorsType;\n loading?: 'lazy' | 'eager';\n}\n\ndefineOptions({\n name: 'RuiAvatar',\n});\n\nconst {\n src = null,\n alt = '',\n text = null,\n icon = null,\n size,\n variant,\n color = 'default',\n loading = 'lazy',\n} = defineProps<Props>();\n\nconst emit = defineEmits<{\n load: [event: Event];\n error: [event: Event];\n}>();\n\nconst slots = defineSlots<{\n default?: () => any;\n}>();\n\nconst group = useAvatarGroup();\n\nconst resolvedSize = computed<AvatarSize | number>(() => {\n if (size !== undefined)\n return size;\n if (group)\n return get(group).size;\n return 'md';\n});\n\nconst resolvedVariant = computed<AvatarVariant>(() => {\n if (variant !== undefined)\n return variant;\n if (group)\n return get(group).variant;\n return 'circular';\n});\n\nconst sizeToken = computed<AvatarSize | undefined>(() => {\n const value = get(resolvedSize);\n return typeof value === 'string' ? value : undefined;\n});\n\nconst sizePx = computed<number>(() => resolveAvatarSizePx(get(resolvedSize)));\nconst iconPx = computed<number>(() => resolveAvatarIconPx(get(resolvedSize)));\n\nconst errored = ref<boolean>(false);\n\nwatch(() => src, () => {\n set(errored, false);\n});\n\nconst initials = computed<string>(() => {\n const explicit = computeInitials(text ?? undefined);\n if (explicit)\n return explicit;\n return computeInitials(alt);\n});\n\ntype DisplayMode = 'image' | 'slot' | 'initials' | 'icon' | 'empty';\n\nconst displayMode = computed<DisplayMode>(() => {\n if (src && !get(errored))\n return 'image';\n if (slots.default)\n return 'slot';\n if (icon)\n return 'icon';\n if (get(initials))\n return 'initials';\n return 'empty';\n});\n\nconst decorative = computed<boolean>(() => alt === '' && !text);\n\nconst ariaLabel = computed<string | undefined>(() => {\n if (get(displayMode) === 'image')\n return undefined;\n if (get(decorative))\n return undefined;\n return text ?? alt ?? undefined;\n});\n\nconst rootStyle = computed<Record<string, string>>(() => {\n const px = `${get(sizePx)}px`;\n return { width: px, height: px };\n});\n\nconst avatarStyles = tv({\n slots: {\n root: 'relative inline-flex items-center justify-center shrink-0 overflow-hidden select-none font-medium align-middle',\n image: 'w-full h-full object-cover',\n fallback: 'w-full h-full flex items-center justify-center',\n initials: 'uppercase leading-none',\n },\n variants: {\n variant: {\n circular: { root: 'rounded-full' },\n rounded: { root: 'rounded-md' },\n square: { root: 'rounded-none' },\n },\n color: {\n default: { root: 'bg-rui-grey-200 text-rui-text dark:bg-rui-grey-800' },\n primary: { root: 'bg-rui-primary text-white' },\n secondary: { root: 'bg-rui-secondary text-white' },\n error: { root: 'bg-rui-error text-white' },\n warning: { root: 'bg-rui-warning text-white' },\n info: { root: 'bg-rui-info text-white' },\n success: { root: 'bg-rui-success text-white' },\n },\n size: {\n 'xs': { initials: 'text-[0.625rem]' },\n 'sm': { initials: 'text-[0.6875rem]' },\n 'md': { initials: 'text-sm' },\n 'lg': { initials: 'text-base' },\n 'xl': { initials: 'text-lg' },\n '2xl': { initials: 'text-xl' },\n },\n },\n compoundSlots: [\n { slots: ['root'], color: ['warning', 'success', 'info'], class: 'dark:text-rui-light-text' },\n ],\n defaultVariants: {\n variant: 'circular',\n color: 'default',\n size: 'md',\n },\n});\n\nconst ui = computed<ReturnType<typeof avatarStyles>>(() => avatarStyles({\n variant: get(resolvedVariant),\n color,\n size: get(sizeToken),\n}));\n\nfunction onLoad(event: Event): void {\n set(errored, false);\n emit('load', event);\n}\n\nfunction onError(event: Event): void {\n set(errored, true);\n emit('error', event);\n}\n</script>\n\n<template>\n <span\n :class=\"ui.root()\"\n :style=\"rootStyle\"\n :role=\"displayMode === 'image' ? undefined : 'img'\"\n :aria-label=\"ariaLabel\"\n :data-id=\"$attrs['data-id'] ?? 'avatar-root'\"\n :data-size=\"typeof resolvedSize === 'string' ? resolvedSize : undefined\"\n :data-variant=\"resolvedVariant\"\n :data-color=\"color\"\n >\n <img\n v-if=\"displayMode === 'image' && src\"\n :class=\"ui.image()\"\n :src=\"src\"\n :alt=\"alt\"\n :loading=\"loading\"\n :width=\"sizePx\"\n :height=\"sizePx\"\n data-id=\"avatar-image\"\n @load=\"onLoad($event)\"\n @error=\"onError($event)\"\n />\n <span\n v-else\n :class=\"ui.fallback()\"\n data-id=\"avatar-fallback\"\n aria-hidden=\"true\"\n >\n <slot v-if=\"displayMode === 'slot'\" />\n <span\n v-else-if=\"displayMode === 'initials'\"\n :class=\"ui.initials()\"\n data-id=\"avatar-initials\"\n >\n {{ initials }}\n </span>\n <RuiIcon\n v-else-if=\"displayMode === 'icon' && icon\"\n :name=\"icon\"\n :size=\"iconPx\"\n data-id=\"avatar-icon\"\n />\n <span v-else> </span>\n </span>\n </span>\n</template>\n"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"RuiAvatar.vue_vue_type_script_setup_true_lang.js","names":["$attrs"],"sources":["../../../src/components/avatars/RuiAvatar.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport type { ContextColorsType } from '@/consts/colors';\nimport type { RuiIcons } from '@/icons';\nimport { get, set } from '@vueuse/shared';\nimport {\n type AvatarSize,\n type AvatarVariant,\n computeInitials,\n resolveAvatarIconPx,\n resolveAvatarSizePx,\n} from '@/components/avatars/avatar-props';\nimport { useAvatarGroup } from '@/components/avatars/use-avatar-group';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport { tv } from '@/utils/tv';\n\nexport interface Props {\n src?: string | null;\n alt?: string;\n text?: string | null;\n icon?: RuiIcons | null;\n size?: AvatarSize | number;\n variant?: AvatarVariant;\n color?: 'default' | ContextColorsType;\n loading?: 'lazy' | 'eager';\n}\n\ndefineOptions({\n name: 'RuiAvatar',\n});\n\nconst {\n src = null,\n alt = '',\n text = null,\n icon = null,\n size,\n variant,\n color = 'default',\n loading = 'lazy',\n} = defineProps<Props>();\n\nconst emit = defineEmits<{\n load: [event: Event];\n error: [event: Event];\n}>();\n\nconst slots = defineSlots<{\n default?: () => any;\n}>();\n\nconst group = useAvatarGroup();\n\nconst resolvedSize = computed<AvatarSize | number>(() => {\n if (size !== undefined)\n return size;\n if (group)\n return get(group).size;\n return 'md';\n});\n\nconst resolvedVariant = computed<AvatarVariant>(() => {\n if (variant !== undefined)\n return variant;\n if (group)\n return get(group).variant;\n return 'circular';\n});\n\nconst sizeToken = computed<AvatarSize | undefined>(() => {\n const value = get(resolvedSize);\n return typeof value === 'string' ? value : undefined;\n});\n\nconst sizePx = computed<number>(() => resolveAvatarSizePx(get(resolvedSize)));\nconst iconPx = computed<number>(() => resolveAvatarIconPx(get(resolvedSize)));\n\nconst errored = ref<boolean>(false);\n\nwatch(() => src, () => {\n set(errored, false);\n});\n\nconst initials = computed<string>(() => {\n const explicit = computeInitials(text ?? undefined);\n if (explicit)\n return explicit;\n return computeInitials(alt);\n});\n\ntype DisplayMode = 'image' | 'slot' | 'initials' | 'icon' | 'empty';\n\nconst displayMode = computed<DisplayMode>(() => {\n if (src && !get(errored))\n return 'image';\n if (slots.default)\n return 'slot';\n if (icon)\n return 'icon';\n if (get(initials))\n return 'initials';\n return 'empty';\n});\n\nconst decorative = computed<boolean>(() => alt === '' && !text);\n\nconst ariaLabel = computed<string | undefined>(() => {\n if (get(displayMode) === 'image')\n return undefined;\n if (get(decorative))\n return undefined;\n return text ?? alt ?? undefined;\n});\n\nconst rootStyle = computed<Record<string, string>>(() => {\n const px = `${get(sizePx)}px`;\n return { width: px, height: px };\n});\n\nconst avatarStyles = tv({\n slots: {\n root: 'relative inline-flex items-center justify-center shrink-0 overflow-hidden select-none font-medium align-middle',\n image: 'w-full h-full object-cover',\n fallback: 'w-full h-full flex items-center justify-center',\n initials: 'uppercase leading-none',\n },\n variants: {\n variant: {\n circular: { root: 'rounded-full' },\n rounded: { root: 'rounded-md' },\n square: { root: 'rounded-none' },\n },\n color: {\n default: { root: 'bg-rui-grey-200 text-rui-text dark:bg-rui-grey-800' },\n primary: { root: 'bg-rui-primary text-white' },\n secondary: { root: 'bg-rui-secondary text-white' },\n error: { root: 'bg-rui-error text-white' },\n warning: { root: 'bg-rui-warning text-white' },\n info: { root: 'bg-rui-info text-white' },\n success: { root: 'bg-rui-success text-white' },\n },\n size: {\n 'xs': { initials: 'text-[0.625rem]' },\n 'sm': { initials: 'text-[0.6875rem]' },\n 'md': { initials: 'text-sm' },\n 'lg': { initials: 'text-base' },\n 'xl': { initials: 'text-lg' },\n '2xl': { initials: 'text-xl' },\n },\n },\n compoundSlots: [\n { slots: ['root'], color: ['warning', 'success', 'info'], class: 'dark:text-rui-light-text' },\n ],\n defaultVariants: {\n variant: 'circular',\n color: 'default',\n size: 'md',\n },\n});\n\nconst ui = computed<ReturnType<typeof avatarStyles>>(() => avatarStyles({\n variant: get(resolvedVariant),\n color,\n size: get(sizeToken),\n}));\n\nfunction onLoad(event: Event): void {\n set(errored, false);\n emit('load', event);\n}\n\nfunction onError(event: Event): void {\n set(errored, true);\n emit('error', event);\n}\n</script>\n\n<template>\n <span\n :class=\"ui.root()\"\n :style=\"rootStyle\"\n :role=\"displayMode === 'image' ? undefined : 'img'\"\n :aria-label=\"ariaLabel\"\n :data-id=\"$attrs['data-id'] ?? 'avatar-root'\"\n :data-size=\"typeof resolvedSize === 'string' ? resolvedSize : undefined\"\n :data-variant=\"resolvedVariant\"\n :data-color=\"color\"\n >\n <img\n v-if=\"displayMode === 'image' && src\"\n :class=\"ui.image()\"\n :src=\"src\"\n :alt=\"alt\"\n :loading=\"loading\"\n :width=\"sizePx\"\n :height=\"sizePx\"\n data-id=\"avatar-image\"\n @load=\"onLoad($event)\"\n @error=\"onError($event)\"\n />\n <span\n v-else\n :class=\"ui.fallback()\"\n data-id=\"avatar-fallback\"\n aria-hidden=\"true\"\n >\n <slot v-if=\"displayMode === 'slot'\" />\n <span\n v-else-if=\"displayMode === 'initials'\"\n :class=\"ui.initials()\"\n data-id=\"avatar-initials\"\n >\n {{ initials }}\n </span>\n <RuiIcon\n v-else-if=\"displayMode === 'icon' && icon\"\n :name=\"icon\"\n :size=\"iconPx\"\n data-id=\"avatar-icon\"\n />\n <span v-else> </span>\n </span>\n </span>\n</template>\n"],"mappings":"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"}
|
|
@@ -30,7 +30,7 @@ declare const __VLS_export: <T = undefined>(__VLS_props: NonNullable<Awaited<typ
|
|
|
30
30
|
};
|
|
31
31
|
emit: (evt: "update:model-value", value?: T | undefined) => void;
|
|
32
32
|
}>) => import("vue").VNode & {
|
|
33
|
-
__ctx?: Awaited<typeof __VLS_setup
|
|
33
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
34
34
|
};
|
|
35
35
|
declare const _default: typeof __VLS_export;
|
|
36
36
|
export default _default;
|
|
@@ -23,7 +23,7 @@ declare const __VLS_export: <T = undefined>(__VLS_props: NonNullable<Awaited<typ
|
|
|
23
23
|
slots: {};
|
|
24
24
|
emit: (event: "update:modelValue", value: T | T[] | undefined) => void;
|
|
25
25
|
}>) => import("vue").VNode & {
|
|
26
|
-
__ctx?: Awaited<typeof __VLS_setup
|
|
26
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
27
27
|
};
|
|
28
28
|
declare const _default: typeof __VLS_export;
|
|
29
29
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiCalendar.js","names":[],"sources":["../../../src/components/calendar/RuiCalendar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { get, isDefined, set } from '@vueuse/core';\nimport { computed, provide, ref, watch } from 'vue';\nimport {\n CalendarStateSymbol,\n type MonthYearSelection,\n type RuiCalendarState,\n} from '@/components/calendar/state';\nimport { useRotkiTheme } from '@/composables/theme';\nimport { tv } from '@/utils/tv';\nimport RuiCalendarGrid from './RuiCalendarGrid.vue';\nimport RuiCalendarHeader from './RuiCalendarHeader.vue';\n\nexport interface CalendarProps {\n maxDate?: Date | number;\n minDate?: Date | number;\n allowEmpty?: boolean;\n borderless?: boolean;\n}\n\ndefineOptions({\n name: 'RuiCalendar',\n inheritAttrs: false,\n});\n\nconst model = defineModel<Date | undefined>({ required: false });\nconst isMenuOpen = defineModel<boolean>('menu-open', { required: false, default: false });\n\nconst { maxDate, minDate, allowEmpty = false, borderless = false } = defineProps<CalendarProps>();\n\nconst emit = defineEmits<{\n 'update:pages': [{ title: string }[]];\n}>();\n\nconst currentDate = ref<Date>(isDefined(model) ? new Date(get(model)) : new Date());\nconst selectedDate = ref<Date | undefined>(isDefined(model) ? new Date(get(model)) : undefined);\nconst viewMonth = ref<number>(get(currentDate).getMonth());\nconst viewYear = ref<number>(get(currentDate).getFullYear());\n\nconst { isDark } = useRotkiTheme();\n\nconst calendarStyles = tv({\n base: 'w-[18.75rem] bg-white dark:bg-rui-grey-900 overflow-hidden',\n variants: {\n bordered: {\n true: 'rounded-md shadow-sm border border-rui-grey-200 dark:border-rui-grey-800',\n },\n },\n});\n\nconst ui = computed<string>(() => calendarStyles({ bordered: !borderless }));\n\nconst monthTitle = computed<string>(() => {\n const date = new Date(get(viewYear), get(viewMonth));\n return date.toLocaleString('default', { month: 'long', year: 'numeric' });\n});\n\nprovide(CalendarStateSymbol.valueOf(), {\n viewMonth,\n viewYear,\n selectedDate,\n maxDate: computed(() => (maxDate ? new Date(maxDate) : undefined)),\n minDate: computed(() => (minDate ? new Date(minDate) : undefined)),\n isDark,\n allowEmpty,\n} satisfies RuiCalendarState);\n\nfunction moveMonth(delta: number): void {\n let newMonth = get(viewMonth) + delta;\n let newYear = get(viewYear);\n\n if (newMonth > 11) {\n newMonth = 0;\n newYear++;\n }\n else if (newMonth < 0) {\n newMonth = 11;\n newYear--;\n }\n\n set(viewMonth, newMonth);\n set(viewYear, newYear);\n
|
|
1
|
+
{"version":3,"file":"RuiCalendar.js","names":[],"sources":["../../../src/components/calendar/RuiCalendar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { get, isDefined, set } from '@vueuse/core';\nimport { computed, provide, ref, watch } from 'vue';\nimport {\n CalendarStateSymbol,\n type MonthYearSelection,\n type RuiCalendarState,\n} from '@/components/calendar/state';\nimport { useRotkiTheme } from '@/composables/theme';\nimport { tv } from '@/utils/tv';\nimport RuiCalendarGrid from './RuiCalendarGrid.vue';\nimport RuiCalendarHeader from './RuiCalendarHeader.vue';\n\nexport interface CalendarProps {\n maxDate?: Date | number;\n minDate?: Date | number;\n allowEmpty?: boolean;\n borderless?: boolean;\n}\n\ndefineOptions({\n name: 'RuiCalendar',\n inheritAttrs: false,\n});\n\nconst model = defineModel<Date | undefined>({ required: false });\nconst isMenuOpen = defineModel<boolean>('menu-open', { required: false, default: false });\n\nconst { maxDate, minDate, allowEmpty = false, borderless = false } = defineProps<CalendarProps>();\n\nconst emit = defineEmits<{\n 'update:pages': [{ title: string }[]];\n}>();\n\nconst currentDate = ref<Date>(isDefined(model) ? new Date(get(model)) : new Date());\nconst selectedDate = ref<Date | undefined>(isDefined(model) ? new Date(get(model)) : undefined);\nconst viewMonth = ref<number>(get(currentDate).getMonth());\nconst viewYear = ref<number>(get(currentDate).getFullYear());\n\nconst { isDark } = useRotkiTheme();\n\nconst calendarStyles = tv({\n base: 'w-[18.75rem] bg-white dark:bg-rui-grey-900 overflow-hidden',\n variants: {\n bordered: {\n true: 'rounded-md shadow-sm border border-rui-grey-200 dark:border-rui-grey-800',\n },\n },\n});\n\nconst ui = computed<string>(() => calendarStyles({ bordered: !borderless }));\n\nconst monthTitle = computed<string>(() => {\n const date = new Date(get(viewYear), get(viewMonth));\n return date.toLocaleString('default', { month: 'long', year: 'numeric' });\n});\n\nprovide(CalendarStateSymbol.valueOf(), {\n viewMonth,\n viewYear,\n selectedDate,\n maxDate: computed(() => (maxDate ? new Date(maxDate) : undefined)),\n minDate: computed(() => (minDate ? new Date(minDate) : undefined)),\n isDark,\n allowEmpty,\n} satisfies RuiCalendarState);\n\n// The view also turns from the grid's keyboard navigation, so the page title\n// follows the view instead of being emitted by whoever changed it.\nwatch(monthTitle, (title) => {\n emit('update:pages', [{ title }]);\n});\n\nfunction moveMonth(delta: number): void {\n let newMonth = get(viewMonth) + delta;\n let newYear = get(viewYear);\n\n if (newMonth > 11) {\n newMonth = 0;\n newYear++;\n }\n else if (newMonth < 0) {\n newMonth = 11;\n newYear--;\n }\n\n set(viewMonth, newMonth);\n set(viewYear, newYear);\n}\n\nfunction move(date: Date | number): void {\n const newDate = new Date(date);\n set(viewMonth, newDate.getMonth());\n set(viewYear, newDate.getFullYear());\n}\n\nfunction selectMonth(selection: MonthYearSelection): void {\n set(viewMonth, selection.month);\n set(viewYear, selection.year);\n\n // If there's a selected date, update it with the new year/month\n if (isDefined(selectedDate)) {\n const current = get(selectedDate);\n const currentDay = current.getDate();\n // Handle day overflow (e.g., Jan 31 -> Feb 28) by checking BEFORE setting month\n const daysInNewMonth = new Date(selection.year, selection.month + 1, 0).getDate();\n const adjustedDay = Math.min(currentDay, daysInNewMonth);\n\n const newDate = new Date(current);\n newDate.setFullYear(selection.year);\n newDate.setDate(1); // Set to 1st to avoid overflow when changing month\n newDate.setMonth(selection.month);\n newDate.setDate(adjustedDay);\n set(selectedDate, newDate);\n }\n}\n\nwatch(selectedDate, (date) => {\n if (!date && !allowEmpty)\n return;\n\n if (date) {\n set(model, new Date(date));\n\n if (get(viewMonth) !== date.getMonth()) {\n set(viewMonth, date.getMonth());\n }\n\n if (get(viewYear) !== date.getFullYear()) {\n set(viewYear, date.getFullYear());\n }\n }\n else {\n set(model, undefined);\n }\n});\n\nwatch(model, (newValue) => {\n if (newValue === undefined) {\n set(selectedDate, undefined);\n return;\n }\n\n const date = new Date(newValue);\n\n if (isDefined(selectedDate) && get(selectedDate).getTime() !== date.getTime()) {\n set(selectedDate, new Date(newValue));\n set(viewMonth, date.getMonth());\n set(viewYear, date.getFullYear());\n }\n});\n\ndefineExpose({\n move,\n});\n</script>\n\n<template>\n <div :class=\"ui\">\n <RuiCalendarHeader\n v-model:menu-open=\"isMenuOpen\"\n :title=\"monthTitle\"\n :view-month=\"viewMonth\"\n :view-year=\"viewYear\"\n @prev-month=\"moveMonth(-1)\"\n @next-month=\"moveMonth(1)\"\n @select-month=\"selectMonth($event)\"\n />\n\n <RuiCalendarGrid\n v-model=\"selectedDate\"\n :view-month=\"viewMonth\"\n :view-year=\"viewYear\"\n />\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -61,6 +61,9 @@ var RuiCalendar_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
|
|
|
61
61
|
isDark,
|
|
62
62
|
allowEmpty: __props.allowEmpty
|
|
63
63
|
});
|
|
64
|
+
watch(monthTitle, (title) => {
|
|
65
|
+
emit("update:pages", [{ title }]);
|
|
66
|
+
});
|
|
64
67
|
function moveMonth(delta) {
|
|
65
68
|
let newMonth = get(viewMonth) + delta;
|
|
66
69
|
let newYear = get(viewYear);
|
|
@@ -73,7 +76,6 @@ var RuiCalendar_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
|
|
|
73
76
|
}
|
|
74
77
|
set(viewMonth, newMonth);
|
|
75
78
|
set(viewYear, newYear);
|
|
76
|
-
emit("update:pages", [{ title: get(monthTitle) }]);
|
|
77
79
|
}
|
|
78
80
|
function move(date) {
|
|
79
81
|
const newDate = new Date(date);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiCalendar.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/calendar/RuiCalendar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { get, isDefined, set } from '@vueuse/core';\nimport { computed, provide, ref, watch } from 'vue';\nimport {\n CalendarStateSymbol,\n type MonthYearSelection,\n type RuiCalendarState,\n} from '@/components/calendar/state';\nimport { useRotkiTheme } from '@/composables/theme';\nimport { tv } from '@/utils/tv';\nimport RuiCalendarGrid from './RuiCalendarGrid.vue';\nimport RuiCalendarHeader from './RuiCalendarHeader.vue';\n\nexport interface CalendarProps {\n maxDate?: Date | number;\n minDate?: Date | number;\n allowEmpty?: boolean;\n borderless?: boolean;\n}\n\ndefineOptions({\n name: 'RuiCalendar',\n inheritAttrs: false,\n});\n\nconst model = defineModel<Date | undefined>({ required: false });\nconst isMenuOpen = defineModel<boolean>('menu-open', { required: false, default: false });\n\nconst { maxDate, minDate, allowEmpty = false, borderless = false } = defineProps<CalendarProps>();\n\nconst emit = defineEmits<{\n 'update:pages': [{ title: string }[]];\n}>();\n\nconst currentDate = ref<Date>(isDefined(model) ? new Date(get(model)) : new Date());\nconst selectedDate = ref<Date | undefined>(isDefined(model) ? new Date(get(model)) : undefined);\nconst viewMonth = ref<number>(get(currentDate).getMonth());\nconst viewYear = ref<number>(get(currentDate).getFullYear());\n\nconst { isDark } = useRotkiTheme();\n\nconst calendarStyles = tv({\n base: 'w-[18.75rem] bg-white dark:bg-rui-grey-900 overflow-hidden',\n variants: {\n bordered: {\n true: 'rounded-md shadow-sm border border-rui-grey-200 dark:border-rui-grey-800',\n },\n },\n});\n\nconst ui = computed<string>(() => calendarStyles({ bordered: !borderless }));\n\nconst monthTitle = computed<string>(() => {\n const date = new Date(get(viewYear), get(viewMonth));\n return date.toLocaleString('default', { month: 'long', year: 'numeric' });\n});\n\nprovide(CalendarStateSymbol.valueOf(), {\n viewMonth,\n viewYear,\n selectedDate,\n maxDate: computed(() => (maxDate ? new Date(maxDate) : undefined)),\n minDate: computed(() => (minDate ? new Date(minDate) : undefined)),\n isDark,\n allowEmpty,\n} satisfies RuiCalendarState);\n\nfunction moveMonth(delta: number): void {\n let newMonth = get(viewMonth) + delta;\n let newYear = get(viewYear);\n\n if (newMonth > 11) {\n newMonth = 0;\n newYear++;\n }\n else if (newMonth < 0) {\n newMonth = 11;\n newYear--;\n }\n\n set(viewMonth, newMonth);\n set(viewYear, newYear);\n
|
|
1
|
+
{"version":3,"file":"RuiCalendar.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/calendar/RuiCalendar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { get, isDefined, set } from '@vueuse/core';\nimport { computed, provide, ref, watch } from 'vue';\nimport {\n CalendarStateSymbol,\n type MonthYearSelection,\n type RuiCalendarState,\n} from '@/components/calendar/state';\nimport { useRotkiTheme } from '@/composables/theme';\nimport { tv } from '@/utils/tv';\nimport RuiCalendarGrid from './RuiCalendarGrid.vue';\nimport RuiCalendarHeader from './RuiCalendarHeader.vue';\n\nexport interface CalendarProps {\n maxDate?: Date | number;\n minDate?: Date | number;\n allowEmpty?: boolean;\n borderless?: boolean;\n}\n\ndefineOptions({\n name: 'RuiCalendar',\n inheritAttrs: false,\n});\n\nconst model = defineModel<Date | undefined>({ required: false });\nconst isMenuOpen = defineModel<boolean>('menu-open', { required: false, default: false });\n\nconst { maxDate, minDate, allowEmpty = false, borderless = false } = defineProps<CalendarProps>();\n\nconst emit = defineEmits<{\n 'update:pages': [{ title: string }[]];\n}>();\n\nconst currentDate = ref<Date>(isDefined(model) ? new Date(get(model)) : new Date());\nconst selectedDate = ref<Date | undefined>(isDefined(model) ? new Date(get(model)) : undefined);\nconst viewMonth = ref<number>(get(currentDate).getMonth());\nconst viewYear = ref<number>(get(currentDate).getFullYear());\n\nconst { isDark } = useRotkiTheme();\n\nconst calendarStyles = tv({\n base: 'w-[18.75rem] bg-white dark:bg-rui-grey-900 overflow-hidden',\n variants: {\n bordered: {\n true: 'rounded-md shadow-sm border border-rui-grey-200 dark:border-rui-grey-800',\n },\n },\n});\n\nconst ui = computed<string>(() => calendarStyles({ bordered: !borderless }));\n\nconst monthTitle = computed<string>(() => {\n const date = new Date(get(viewYear), get(viewMonth));\n return date.toLocaleString('default', { month: 'long', year: 'numeric' });\n});\n\nprovide(CalendarStateSymbol.valueOf(), {\n viewMonth,\n viewYear,\n selectedDate,\n maxDate: computed(() => (maxDate ? new Date(maxDate) : undefined)),\n minDate: computed(() => (minDate ? new Date(minDate) : undefined)),\n isDark,\n allowEmpty,\n} satisfies RuiCalendarState);\n\n// The view also turns from the grid's keyboard navigation, so the page title\n// follows the view instead of being emitted by whoever changed it.\nwatch(monthTitle, (title) => {\n emit('update:pages', [{ title }]);\n});\n\nfunction moveMonth(delta: number): void {\n let newMonth = get(viewMonth) + delta;\n let newYear = get(viewYear);\n\n if (newMonth > 11) {\n newMonth = 0;\n newYear++;\n }\n else if (newMonth < 0) {\n newMonth = 11;\n newYear--;\n }\n\n set(viewMonth, newMonth);\n set(viewYear, newYear);\n}\n\nfunction move(date: Date | number): void {\n const newDate = new Date(date);\n set(viewMonth, newDate.getMonth());\n set(viewYear, newDate.getFullYear());\n}\n\nfunction selectMonth(selection: MonthYearSelection): void {\n set(viewMonth, selection.month);\n set(viewYear, selection.year);\n\n // If there's a selected date, update it with the new year/month\n if (isDefined(selectedDate)) {\n const current = get(selectedDate);\n const currentDay = current.getDate();\n // Handle day overflow (e.g., Jan 31 -> Feb 28) by checking BEFORE setting month\n const daysInNewMonth = new Date(selection.year, selection.month + 1, 0).getDate();\n const adjustedDay = Math.min(currentDay, daysInNewMonth);\n\n const newDate = new Date(current);\n newDate.setFullYear(selection.year);\n newDate.setDate(1); // Set to 1st to avoid overflow when changing month\n newDate.setMonth(selection.month);\n newDate.setDate(adjustedDay);\n set(selectedDate, newDate);\n }\n}\n\nwatch(selectedDate, (date) => {\n if (!date && !allowEmpty)\n return;\n\n if (date) {\n set(model, new Date(date));\n\n if (get(viewMonth) !== date.getMonth()) {\n set(viewMonth, date.getMonth());\n }\n\n if (get(viewYear) !== date.getFullYear()) {\n set(viewYear, date.getFullYear());\n }\n }\n else {\n set(model, undefined);\n }\n});\n\nwatch(model, (newValue) => {\n if (newValue === undefined) {\n set(selectedDate, undefined);\n return;\n }\n\n const date = new Date(newValue);\n\n if (isDefined(selectedDate) && get(selectedDate).getTime() !== date.getTime()) {\n set(selectedDate, new Date(newValue));\n set(viewMonth, date.getMonth());\n set(viewYear, date.getFullYear());\n }\n});\n\ndefineExpose({\n move,\n});\n</script>\n\n<template>\n <div :class=\"ui\">\n <RuiCalendarHeader\n v-model:menu-open=\"isMenuOpen\"\n :title=\"monthTitle\"\n :view-month=\"viewMonth\"\n :view-year=\"viewYear\"\n @prev-month=\"moveMonth(-1)\"\n @next-month=\"moveMonth(1)\"\n @select-month=\"selectMonth($event)\"\n />\n\n <RuiCalendarGrid\n v-model=\"selectedDate\"\n :view-month=\"viewMonth\"\n :view-year=\"viewYear\"\n />\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiCalendarGrid.js","names":[],"sources":["../../../src/components/calendar/RuiCalendarGrid.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { get, isDefined, set } from '@vueuse/core';\nimport { inject, ref, watch } from 'vue';\nimport {\n CalendarStateSymbol,\n getDaysOfWeek,\n type RuiCalendarState,\n} from '@/components/calendar/state';\nimport { tv } from '@/utils/tv';\n\ndefineOptions({\n name: 'RuiCalendarGrid',\n inheritAttrs: false,\n});\n\nconst model = defineModel<Date | undefined>();\n\nconst { viewMonth, viewYear } = defineProps<{\n viewMonth: number;\n viewYear: number;\n}>();\n\nconst calendarState = inject<RuiCalendarState>(CalendarStateSymbol) as RuiCalendarState;\n\n// Helper functions to reduce duplication\nfunction createDateKey(date: Date): string {\n return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;\n}\n\n// Pre-calculated constants\nconst daysOfWeek = getDaysOfWeek();\nconst today = new Date();\nconst todayKey = createDateKey(today);\n\nconst dayButton = tv({\n base: 'h-9 w-full flex items-center justify-center text-sm rounded-full mx-auto max-w-[2.25rem] transition-colors duration-150 ease-in-out border-none outline-none cursor-pointer focus:ring-2 focus:ring-rui-primary focus:ring-opacity-50',\n variants: {\n selected: {\n true: 'bg-rui-primary text-white hover:bg-rui-primary/90 dark:hover:bg-rui-primary/90',\n },\n currentMonth: {\n true: 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700',\n false: 'text-gray-400 dark:text-gray-600',\n },\n inRange: {\n false: 'opacity-50 cursor-not-allowed hover:bg-transparent',\n },\n today: {\n true: `relative after:content-[''] after:absolute after:size-1 after:rounded-full after:bottom-1 after:left-1/2 after:-translate-x-1/2 after:bg-rui-primary`,\n },\n },\n compoundVariants: [\n {\n selected: true,\n class: 'text-white dark:text-white',\n },\n ],\n});\n\n// Pre-calculated arrays for performance - using refs for better control\nconst calendarDays = ref<\n Array<{\n date: Date;\n isCurrentMonth: boolean;\n isInRange: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n dayNumber: number;\n }>\n>([]);\n\nfunction createDayData(\n date: Date,\n isCurrentMonth: boolean,\n modelValue: Date | null | undefined,\n maxDateValue: Date | null,\n minDateValue: Date | null,\n): {\n date: Date;\n isCurrentMonth: boolean;\n isInRange: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n dayNumber: number;\n} {\n const key = createDateKey(date);\n return {\n date,\n isCurrentMonth,\n isInRange: isDateInRangeCheck(date, maxDateValue, minDateValue),\n isSelected: modelValue ? isDateSelectedCheck(date, modelValue) : false,\n isToday: key === todayKey,\n key,\n dayNumber: date.getDate(),\n };\n}\n\n// Pre-calculation functions using VueUse get/set\nfunction calculateCalendarDays(): void {\n const firstDayOfMonth = new Date(viewYear, viewMonth, 1);\n const lastDayOfMonth = new Date(viewYear, viewMonth + 1, 0);\n const daysInMonth = lastDayOfMonth.getDate();\n const startingDayOfWeek = firstDayOfMonth.getDay();\n\n const { maxDate, minDate } = calendarState;\n const modelValue = get(model);\n const maxDateValue = isDefined(maxDate) ? get(maxDate) : null;\n const minDateValue = isDefined(minDate) ? get(minDate) : null;\n\n const result = [];\n\n // Previous month days\n if (startingDayOfWeek > 0) {\n const prevMonthLastDay = new Date(viewYear, viewMonth, 0).getDate();\n for (let i = startingDayOfWeek - 1; i >= 0; i--) {\n const date = new Date(viewYear, viewMonth - 1, prevMonthLastDay - i, 12, 0, 0, 0);\n result.push(createDayData(date, false, modelValue, maxDateValue, minDateValue));\n }\n }\n\n // Current month days\n for (let i = 1; i <= daysInMonth; i++) {\n const date = new Date(viewYear, viewMonth, i, 12, 0, 0, 0);\n result.push(createDayData(date, true, modelValue, maxDateValue, minDateValue));\n }\n\n // Next month days\n const totalDaysSoFar = result.length;\n const daysToAdd = 42 - totalDaysSoFar;\n for (let i = 1; i <= daysToAdd; i++) {\n const date = new Date(viewYear, viewMonth + 1, i, 12, 0, 0, 0);\n result.push(createDayData(date, false, modelValue, maxDateValue, minDateValue));\n }\n\n set(calendarDays, result);\n}\n\nfunction isDateInRangeCheck(\n date: Date,\n maxDateValue: Date | null,\n minDateValue: Date | null,\n): boolean {\n if (maxDateValue && date > maxDateValue)\n return false;\n if (minDateValue && date < minDateValue)\n return false;\n return true;\n}\n\nfunction isDateSelectedCheck(date: Date, modelValue: Date): boolean {\n return (\n date.getFullYear() === modelValue.getFullYear() &&\n date.getMonth() === modelValue.getMonth() &&\n date.getDate() === modelValue.getDate()\n );\n}\n\nfunction selectDate(dayData: { date: Date; isInRange: boolean; isSelected: boolean }): void {\n if (!dayData.isInRange)\n return;\n\n let updateModel: Date | undefined;\n\n if (dayData.isSelected && calendarState.allowEmpty) {\n updateModel = undefined;\n }\n else if (isDefined(model)) {\n const modelValue = get(model);\n const newDate = new Date(dayData.date);\n newDate.setHours(modelValue.getHours());\n newDate.setMinutes(modelValue.getMinutes());\n newDate.setSeconds(modelValue.getSeconds());\n updateModel = newDate;\n }\n else {\n updateModel = new Date(dayData.date);\n }\n set(model, updateModel);\n}\n\n// Initial calculation\ncalculateCalendarDays();\n\n// Watchers for recalculation only when needed\nwatch([() => viewMonth, () => viewYear], () => {\n calculateCalendarDays();\n});\n\nwatch(model, () => {\n calculateCalendarDays();\n});\n\nwatch(\n [() => calendarState.maxDate, () => calendarState.minDate],\n () => {\n calculateCalendarDays();\n },\n);\n</script>\n\n<template>\n <div class=\"w-full p-2 pt-0\">\n <div class=\"grid grid-cols-7 pb-2\">\n <span\n v-for=\"day in daysOfWeek\"\n :key=\"day\"\n class=\"py-2 text-xs font-medium text-center text-gray-500 dark:text-gray-400\"\n >\n {{ day }}\n </span>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0.5\">\n <button\n v-for=\"dayData in get(calendarDays)\"\n :key=\"dayData.key\"\n type=\"button\"\n :data-id=\"dayData.key\"\n :data-current-month=\"dayData.isCurrentMonth || undefined\"\n :data-selected=\"dayData.isSelected || undefined\"\n :data-today=\"(dayData.isToday && !dayData.isSelected) || undefined\"\n :class=\"dayButton({\n selected: dayData.isSelected,\n currentMonth: !dayData.isSelected && dayData.isCurrentMonth,\n inRange: dayData.isInRange,\n today: dayData.isToday && !dayData.isSelected,\n })\"\n :disabled=\"!dayData.isInRange\"\n @click.stop=\"selectDate(dayData)\"\n >\n {{ dayData.dayNumber }}\n </button>\n </div>\n </div>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"RuiCalendarGrid.js","names":[],"sources":["../../../src/components/calendar/RuiCalendarGrid.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { get, isDefined, set } from '@vueuse/core';\nimport { computed, inject, nextTick, ref, useTemplateRef, watch } from 'vue';\nimport {\n CalendarStateSymbol,\n getDaysOfWeek,\n type RuiCalendarState,\n} from '@/components/calendar/state';\nimport { tv } from '@/utils/tv';\n\ndefineOptions({\n name: 'RuiCalendarGrid',\n inheritAttrs: false,\n});\n\nconst model = defineModel<Date | undefined>();\n\nconst { viewMonth, viewYear } = defineProps<{\n viewMonth: number;\n viewYear: number;\n}>();\n\nconst calendarState = inject<RuiCalendarState>(CalendarStateSymbol) as RuiCalendarState;\n\n// Helper functions to reduce duplication\nfunction createDateKey(date: Date): string {\n return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;\n}\n\n// Pre-calculated constants\nconst daysOfWeek = getDaysOfWeek();\nconst today = new Date();\nconst todayKey = createDateKey(today);\n\nconst dayButton = tv({\n base: 'h-9 w-full flex items-center justify-center text-sm rounded-full mx-auto max-w-[2.25rem] transition-colors duration-150 ease-in-out border-none outline-none cursor-pointer focus:ring-2 focus:ring-rui-primary focus:ring-opacity-50',\n variants: {\n selected: {\n true: 'bg-rui-primary text-white hover:bg-rui-primary/90 dark:hover:bg-rui-primary/90',\n },\n currentMonth: {\n true: 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700',\n false: 'text-gray-400 dark:text-gray-600',\n },\n inRange: {\n false: 'opacity-50 cursor-not-allowed hover:bg-transparent',\n },\n today: {\n true: `relative after:content-[''] after:absolute after:size-1 after:rounded-full after:bottom-1 after:left-1/2 after:-translate-x-1/2 after:bg-rui-primary`,\n },\n },\n compoundVariants: [\n {\n selected: true,\n class: 'text-white dark:text-white',\n },\n ],\n});\n\n// Pre-calculated arrays for performance - using refs for better control\nconst calendarDays = ref<\n Array<{\n date: Date;\n isCurrentMonth: boolean;\n isInRange: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n dayNumber: number;\n }>\n>([]);\n\nfunction createDayData(\n date: Date,\n isCurrentMonth: boolean,\n modelValue: Date | null | undefined,\n maxDateValue: Date | null,\n minDateValue: Date | null,\n): {\n date: Date;\n isCurrentMonth: boolean;\n isInRange: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n dayNumber: number;\n} {\n const key = createDateKey(date);\n return {\n date,\n isCurrentMonth,\n isInRange: isDateInRangeCheck(date, maxDateValue, minDateValue),\n isSelected: modelValue ? isDateSelectedCheck(date, modelValue) : false,\n isToday: key === todayKey,\n key,\n dayNumber: date.getDate(),\n };\n}\n\n// Pre-calculation functions using VueUse get/set\nfunction calculateCalendarDays(): void {\n const firstDayOfMonth = new Date(viewYear, viewMonth, 1);\n const lastDayOfMonth = new Date(viewYear, viewMonth + 1, 0);\n const daysInMonth = lastDayOfMonth.getDate();\n const startingDayOfWeek = firstDayOfMonth.getDay();\n\n const { maxDate, minDate } = calendarState;\n const modelValue = get(model);\n const maxDateValue = isDefined(maxDate) ? get(maxDate) : null;\n const minDateValue = isDefined(minDate) ? get(minDate) : null;\n\n const result = [];\n\n // Previous month days\n if (startingDayOfWeek > 0) {\n const prevMonthLastDay = new Date(viewYear, viewMonth, 0).getDate();\n for (let i = startingDayOfWeek - 1; i >= 0; i--) {\n const date = new Date(viewYear, viewMonth - 1, prevMonthLastDay - i, 12, 0, 0, 0);\n result.push(createDayData(date, false, modelValue, maxDateValue, minDateValue));\n }\n }\n\n // Current month days\n for (let i = 1; i <= daysInMonth; i++) {\n const date = new Date(viewYear, viewMonth, i, 12, 0, 0, 0);\n result.push(createDayData(date, true, modelValue, maxDateValue, minDateValue));\n }\n\n // Next month days\n const totalDaysSoFar = result.length;\n const daysToAdd = 42 - totalDaysSoFar;\n for (let i = 1; i <= daysToAdd; i++) {\n const date = new Date(viewYear, viewMonth + 1, i, 12, 0, 0, 0);\n result.push(createDayData(date, false, modelValue, maxDateValue, minDateValue));\n }\n\n set(calendarDays, result);\n}\n\nfunction isDateInRangeCheck(\n date: Date,\n maxDateValue: Date | null,\n minDateValue: Date | null,\n): boolean {\n if (maxDateValue && date > maxDateValue)\n return false;\n if (minDateValue && date < minDateValue)\n return false;\n return true;\n}\n\nfunction isDateSelectedCheck(date: Date, modelValue: Date): boolean {\n return (\n date.getFullYear() === modelValue.getFullYear() &&\n date.getMonth() === modelValue.getMonth() &&\n date.getDate() === modelValue.getDate()\n );\n}\n\nfunction selectDate(dayData: { date: Date; isInRange: boolean; isSelected: boolean }): void {\n if (!dayData.isInRange)\n return;\n\n let updateModel: Date | undefined;\n\n if (dayData.isSelected && calendarState.allowEmpty) {\n updateModel = undefined;\n }\n else if (isDefined(model)) {\n const modelValue = get(model);\n const newDate = new Date(dayData.date);\n newDate.setHours(modelValue.getHours());\n newDate.setMinutes(modelValue.getMinutes());\n newDate.setSeconds(modelValue.getSeconds());\n updateModel = newDate;\n }\n else {\n updateModel = new Date(dayData.date);\n }\n set(model, updateModel);\n}\n\n// --- keyboard navigation -------------------------------------------------\n// The grid is a roving tabindex: one day is tabbable and the arrows move it,\n// so crossing the calendar costs one Tab instead of 42.\n\nconst gridRef = useTemplateRef<HTMLDivElement>('grid');\nconst activeKey = ref<string>('');\n\nconst dayLabelFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'full' });\n\nconst KEY_DAY_OFFSETS: Record<string, number> = {\n ArrowDown: 7,\n ArrowLeft: -1,\n ArrowRight: 1,\n ArrowUp: -7,\n};\n\nconst weeks = computed<Array<typeof calendarDays.value>>(() => {\n const days = get(calendarDays);\n const result = [];\n for (let i = 0; i < days.length; i += 7)\n result.push(days.slice(i, i + 7));\n return result;\n});\n\nfunction preferredActiveKey(): string {\n const days = get(calendarDays);\n const selected = days.find(day => day.isSelected && day.isInRange);\n const todayInView = days.find(day => day.isToday && day.isCurrentMonth && day.isInRange);\n const firstSelectable = days.find(day => day.isCurrentMonth && day.isInRange);\n return (selected ?? todayInView ?? firstSelectable ?? days[0])?.key ?? '';\n}\n\nfunction syncActiveKey(): void {\n const days = get(calendarDays);\n if (!days.some(day => day.key === get(activeKey)))\n set(activeKey, preferredActiveKey());\n}\n\nfunction keyToDate(key: string): Date {\n const [year, month, day] = key.split('-').map(Number);\n return new Date(year ?? 0, (month ?? 1) - 1, day ?? 1, 12, 0, 0, 0);\n}\n\nfunction focusKey(key: string): void {\n set(activeKey, key);\n nextTick(() => {\n get(gridRef)?.querySelector<HTMLButtonElement>(`[data-id=\"${key}\"]`)?.focus();\n });\n}\n\nfunction moveTo(target: Date): void {\n const { maxDate, minDate } = calendarState;\n const maxDateValue = isDefined(maxDate) ? get(maxDate) : null;\n const minDateValue = isDefined(minDate) ? get(minDate) : null;\n\n // min/max bound a contiguous range, so a blocked step means the edge is here\n if (!isDateInRangeCheck(target, maxDateValue, minDateValue))\n return;\n\n const key = createDateKey(target);\n\n // Landing on another month turns the page, even when that day happens to be\n // rendered as a spill day in the current window — otherwise the header and\n // the focused day disagree about which month is being edited.\n if (target.getMonth() === viewMonth && target.getFullYear() === viewYear) {\n focusKey(key);\n return;\n }\n\n set(calendarState.viewMonth, target.getMonth());\n set(calendarState.viewYear, target.getFullYear());\n nextTick(() => {\n focusKey(key);\n });\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n const active = get(activeKey);\n if (!active)\n return;\n\n const current = keyToDate(active);\n const dayOffset = KEY_DAY_OFFSETS[event.key];\n\n if (dayOffset !== undefined) {\n event.preventDefault();\n moveTo(new Date(current.getFullYear(), current.getMonth(), current.getDate() + dayOffset, 12, 0, 0, 0));\n }\n else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n const offset = event.key === 'Home' ? -current.getDay() : 6 - current.getDay();\n moveTo(new Date(current.getFullYear(), current.getMonth(), current.getDate() + offset, 12, 0, 0, 0));\n }\n else if (event.key === 'PageUp' || event.key === 'PageDown') {\n event.preventDefault();\n const delta = event.key === 'PageUp' ? -1 : 1;\n moveTo(new Date(current.getFullYear(), current.getMonth() + delta, current.getDate(), 12, 0, 0, 0));\n }\n}\n\n// Initial calculation\ncalculateCalendarDays();\nsyncActiveKey();\n\n// Watchers for recalculation only when needed\nwatch([() => viewMonth, () => viewYear], () => {\n calculateCalendarDays();\n syncActiveKey();\n});\n\nwatch(model, () => {\n calculateCalendarDays();\n syncActiveKey();\n});\n\nwatch(\n [() => calendarState.maxDate, () => calendarState.minDate],\n () => {\n calculateCalendarDays();\n syncActiveKey();\n },\n);\n</script>\n\n<template>\n <div class=\"w-full p-2 pt-0\">\n <div\n ref=\"grid\"\n role=\"grid\"\n class=\"grid grid-cols-7 gap-0.5\"\n @keydown=\"onKeydown($event)\"\n >\n <div\n role=\"row\"\n class=\"contents\"\n >\n <span\n v-for=\"day in daysOfWeek\"\n :key=\"day\"\n role=\"columnheader\"\n class=\"py-2 pb-4 text-xs font-medium text-center text-gray-500 dark:text-gray-400\"\n >\n {{ day }}\n </span>\n </div>\n\n <div\n v-for=\"(week, index) in weeks\"\n :key=\"index\"\n role=\"row\"\n class=\"contents\"\n >\n <button\n v-for=\"dayData in week\"\n :key=\"dayData.key\"\n type=\"button\"\n role=\"gridcell\"\n :data-id=\"dayData.key\"\n :data-current-month=\"dayData.isCurrentMonth || undefined\"\n :data-selected=\"dayData.isSelected || undefined\"\n :data-today=\"(dayData.isToday && !dayData.isSelected) || undefined\"\n :class=\"dayButton({\n selected: dayData.isSelected,\n currentMonth: !dayData.isSelected && dayData.isCurrentMonth,\n inRange: dayData.isInRange,\n today: dayData.isToday && !dayData.isSelected,\n })\"\n :disabled=\"!dayData.isInRange\"\n :tabindex=\"dayData.key === activeKey ? 0 : -1\"\n :aria-label=\"dayLabelFormatter.format(dayData.date)\"\n :aria-selected=\"dayData.isSelected\"\n :aria-current=\"dayData.isToday ? 'date' : undefined\"\n @click.stop=\"selectDate(dayData)\"\n @focus=\"activeKey = dayData.key\"\n >\n {{ dayData.dayNumber }}\n </button>\n </div>\n </div>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { tv } from "../../utils/tv.js";
|
|
2
2
|
import { CalendarStateSymbol, getDaysOfWeek } from "./state.js";
|
|
3
|
-
import { Fragment, createElementBlock, createElementVNode, defineComponent, inject, mergeModels, normalizeClass, openBlock, ref, renderList, toDisplayString, unref, useModel, watch, withModifiers } from "vue";
|
|
3
|
+
import { Fragment, computed, createElementBlock, createElementVNode, defineComponent, inject, mergeModels, nextTick, normalizeClass, openBlock, ref, renderList, toDisplayString, unref, useModel, useTemplateRef, watch, withModifiers } from "vue";
|
|
4
4
|
import { get, isDefined, set } from "@vueuse/core";
|
|
5
5
|
//#region src/components/calendar/RuiCalendarGrid.vue?vue&type=script&setup=true&lang.ts
|
|
6
6
|
var _hoisted_1 = { class: "w-full p-2 pt-0" };
|
|
7
|
-
var _hoisted_2 = {
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
var _hoisted_2 = {
|
|
8
|
+
role: "row",
|
|
9
|
+
class: "contents"
|
|
10
|
+
};
|
|
11
|
+
var _hoisted_3 = [
|
|
10
12
|
"data-id",
|
|
11
13
|
"data-current-month",
|
|
12
14
|
"data-selected",
|
|
13
15
|
"data-today",
|
|
14
16
|
"disabled",
|
|
15
|
-
"
|
|
17
|
+
"tabindex",
|
|
18
|
+
"aria-label",
|
|
19
|
+
"aria-selected",
|
|
20
|
+
"aria-current",
|
|
21
|
+
"onClick",
|
|
22
|
+
"onFocus"
|
|
16
23
|
];
|
|
17
24
|
var RuiCalendarGrid_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
18
25
|
name: "RuiCalendarGrid",
|
|
@@ -112,40 +119,129 @@ var RuiCalendarGrid_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
112
119
|
} else updateModel = new Date(dayData.date);
|
|
113
120
|
set(model, updateModel);
|
|
114
121
|
}
|
|
122
|
+
const gridRef = useTemplateRef("grid");
|
|
123
|
+
const activeKey = ref("");
|
|
124
|
+
const dayLabelFormatter = new Intl.DateTimeFormat("en-US", { dateStyle: "full" });
|
|
125
|
+
const KEY_DAY_OFFSETS = {
|
|
126
|
+
ArrowDown: 7,
|
|
127
|
+
ArrowLeft: -1,
|
|
128
|
+
ArrowRight: 1,
|
|
129
|
+
ArrowUp: -7
|
|
130
|
+
};
|
|
131
|
+
const weeks = computed(() => {
|
|
132
|
+
const days = get(calendarDays);
|
|
133
|
+
const result = [];
|
|
134
|
+
for (let i = 0; i < days.length; i += 7) result.push(days.slice(i, i + 7));
|
|
135
|
+
return result;
|
|
136
|
+
});
|
|
137
|
+
function preferredActiveKey() {
|
|
138
|
+
const days = get(calendarDays);
|
|
139
|
+
const selected = days.find((day) => day.isSelected && day.isInRange);
|
|
140
|
+
const todayInView = days.find((day) => day.isToday && day.isCurrentMonth && day.isInRange);
|
|
141
|
+
const firstSelectable = days.find((day) => day.isCurrentMonth && day.isInRange);
|
|
142
|
+
return (selected ?? todayInView ?? firstSelectable ?? days[0])?.key ?? "";
|
|
143
|
+
}
|
|
144
|
+
function syncActiveKey() {
|
|
145
|
+
if (!get(calendarDays).some((day) => day.key === get(activeKey))) set(activeKey, preferredActiveKey());
|
|
146
|
+
}
|
|
147
|
+
function keyToDate(key) {
|
|
148
|
+
const [year, month, day] = key.split("-").map(Number);
|
|
149
|
+
return new Date(year ?? 0, (month ?? 1) - 1, day ?? 1, 12, 0, 0, 0);
|
|
150
|
+
}
|
|
151
|
+
function focusKey(key) {
|
|
152
|
+
set(activeKey, key);
|
|
153
|
+
nextTick(() => {
|
|
154
|
+
get(gridRef)?.querySelector(`[data-id="${key}"]`)?.focus();
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function moveTo(target) {
|
|
158
|
+
const { maxDate, minDate } = calendarState;
|
|
159
|
+
if (!isDateInRangeCheck(target, isDefined(maxDate) ? get(maxDate) : null, isDefined(minDate) ? get(minDate) : null)) return;
|
|
160
|
+
const key = createDateKey(target);
|
|
161
|
+
if (target.getMonth() === __props.viewMonth && target.getFullYear() === __props.viewYear) {
|
|
162
|
+
focusKey(key);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
set(calendarState.viewMonth, target.getMonth());
|
|
166
|
+
set(calendarState.viewYear, target.getFullYear());
|
|
167
|
+
nextTick(() => {
|
|
168
|
+
focusKey(key);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function onKeydown(event) {
|
|
172
|
+
const active = get(activeKey);
|
|
173
|
+
if (!active) return;
|
|
174
|
+
const current = keyToDate(active);
|
|
175
|
+
const dayOffset = KEY_DAY_OFFSETS[event.key];
|
|
176
|
+
if (dayOffset !== void 0) {
|
|
177
|
+
event.preventDefault();
|
|
178
|
+
moveTo(new Date(current.getFullYear(), current.getMonth(), current.getDate() + dayOffset, 12, 0, 0, 0));
|
|
179
|
+
} else if (event.key === "Home" || event.key === "End") {
|
|
180
|
+
event.preventDefault();
|
|
181
|
+
const offset = event.key === "Home" ? -current.getDay() : 6 - current.getDay();
|
|
182
|
+
moveTo(new Date(current.getFullYear(), current.getMonth(), current.getDate() + offset, 12, 0, 0, 0));
|
|
183
|
+
} else if (event.key === "PageUp" || event.key === "PageDown") {
|
|
184
|
+
event.preventDefault();
|
|
185
|
+
const delta = event.key === "PageUp" ? -1 : 1;
|
|
186
|
+
moveTo(new Date(current.getFullYear(), current.getMonth() + delta, current.getDate(), 12, 0, 0, 0));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
115
189
|
calculateCalendarDays();
|
|
190
|
+
syncActiveKey();
|
|
116
191
|
watch([() => __props.viewMonth, () => __props.viewYear], () => {
|
|
117
192
|
calculateCalendarDays();
|
|
193
|
+
syncActiveKey();
|
|
118
194
|
});
|
|
119
195
|
watch(model, () => {
|
|
120
196
|
calculateCalendarDays();
|
|
197
|
+
syncActiveKey();
|
|
121
198
|
});
|
|
122
199
|
watch([() => calendarState.maxDate, () => calendarState.minDate], () => {
|
|
123
200
|
calculateCalendarDays();
|
|
201
|
+
syncActiveKey();
|
|
124
202
|
});
|
|
125
203
|
return (_ctx, _cache) => {
|
|
126
|
-
return openBlock(), createElementBlock("div", _hoisted_1, [createElementVNode("div",
|
|
204
|
+
return openBlock(), createElementBlock("div", _hoisted_1, [createElementVNode("div", {
|
|
205
|
+
ref: "grid",
|
|
206
|
+
role: "grid",
|
|
207
|
+
class: "grid grid-cols-7 gap-0.5",
|
|
208
|
+
onKeydown: _cache[0] || (_cache[0] = ($event) => onKeydown($event))
|
|
209
|
+
}, [createElementVNode("div", _hoisted_2, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(daysOfWeek), (day) => {
|
|
127
210
|
return openBlock(), createElementBlock("span", {
|
|
128
211
|
key: day,
|
|
129
|
-
|
|
212
|
+
role: "columnheader",
|
|
213
|
+
class: "py-2 pb-4 text-xs font-medium text-center text-gray-500 dark:text-gray-400"
|
|
130
214
|
}, toDisplayString(day), 1);
|
|
131
|
-
}), 128))]),
|
|
132
|
-
return openBlock(), createElementBlock("
|
|
133
|
-
key:
|
|
134
|
-
|
|
135
|
-
"
|
|
136
|
-
|
|
137
|
-
"
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
215
|
+
}), 128))]), (openBlock(true), createElementBlock(Fragment, null, renderList(weeks.value, (week, index) => {
|
|
216
|
+
return openBlock(), createElementBlock("div", {
|
|
217
|
+
key: index,
|
|
218
|
+
role: "row",
|
|
219
|
+
class: "contents"
|
|
220
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(week, (dayData) => {
|
|
221
|
+
return openBlock(), createElementBlock("button", {
|
|
222
|
+
key: dayData.key,
|
|
223
|
+
type: "button",
|
|
224
|
+
role: "gridcell",
|
|
225
|
+
"data-id": dayData.key,
|
|
226
|
+
"data-current-month": dayData.isCurrentMonth || void 0,
|
|
227
|
+
"data-selected": dayData.isSelected || void 0,
|
|
228
|
+
"data-today": dayData.isToday && !dayData.isSelected || void 0,
|
|
229
|
+
class: normalizeClass(unref(dayButton)({
|
|
230
|
+
selected: dayData.isSelected,
|
|
231
|
+
currentMonth: !dayData.isSelected && dayData.isCurrentMonth,
|
|
232
|
+
inRange: dayData.isInRange,
|
|
233
|
+
today: dayData.isToday && !dayData.isSelected
|
|
234
|
+
})),
|
|
235
|
+
disabled: !dayData.isInRange,
|
|
236
|
+
tabindex: dayData.key === activeKey.value ? 0 : -1,
|
|
237
|
+
"aria-label": unref(dayLabelFormatter).format(dayData.date),
|
|
238
|
+
"aria-selected": dayData.isSelected,
|
|
239
|
+
"aria-current": dayData.isToday ? "date" : void 0,
|
|
240
|
+
onClick: withModifiers(($event) => selectDate(dayData), ["stop"]),
|
|
241
|
+
onFocus: ($event) => activeKey.value = dayData.key
|
|
242
|
+
}, toDisplayString(dayData.dayNumber), 43, _hoisted_3);
|
|
243
|
+
}), 128))]);
|
|
244
|
+
}), 128))], 544)]);
|
|
149
245
|
};
|
|
150
246
|
}
|
|
151
247
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiCalendarGrid.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/calendar/RuiCalendarGrid.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { get, isDefined, set } from '@vueuse/core';\nimport { inject, ref, watch } from 'vue';\nimport {\n CalendarStateSymbol,\n getDaysOfWeek,\n type RuiCalendarState,\n} from '@/components/calendar/state';\nimport { tv } from '@/utils/tv';\n\ndefineOptions({\n name: 'RuiCalendarGrid',\n inheritAttrs: false,\n});\n\nconst model = defineModel<Date | undefined>();\n\nconst { viewMonth, viewYear } = defineProps<{\n viewMonth: number;\n viewYear: number;\n}>();\n\nconst calendarState = inject<RuiCalendarState>(CalendarStateSymbol) as RuiCalendarState;\n\n// Helper functions to reduce duplication\nfunction createDateKey(date: Date): string {\n return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;\n}\n\n// Pre-calculated constants\nconst daysOfWeek = getDaysOfWeek();\nconst today = new Date();\nconst todayKey = createDateKey(today);\n\nconst dayButton = tv({\n base: 'h-9 w-full flex items-center justify-center text-sm rounded-full mx-auto max-w-[2.25rem] transition-colors duration-150 ease-in-out border-none outline-none cursor-pointer focus:ring-2 focus:ring-rui-primary focus:ring-opacity-50',\n variants: {\n selected: {\n true: 'bg-rui-primary text-white hover:bg-rui-primary/90 dark:hover:bg-rui-primary/90',\n },\n currentMonth: {\n true: 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700',\n false: 'text-gray-400 dark:text-gray-600',\n },\n inRange: {\n false: 'opacity-50 cursor-not-allowed hover:bg-transparent',\n },\n today: {\n true: `relative after:content-[''] after:absolute after:size-1 after:rounded-full after:bottom-1 after:left-1/2 after:-translate-x-1/2 after:bg-rui-primary`,\n },\n },\n compoundVariants: [\n {\n selected: true,\n class: 'text-white dark:text-white',\n },\n ],\n});\n\n// Pre-calculated arrays for performance - using refs for better control\nconst calendarDays = ref<\n Array<{\n date: Date;\n isCurrentMonth: boolean;\n isInRange: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n dayNumber: number;\n }>\n>([]);\n\nfunction createDayData(\n date: Date,\n isCurrentMonth: boolean,\n modelValue: Date | null | undefined,\n maxDateValue: Date | null,\n minDateValue: Date | null,\n): {\n date: Date;\n isCurrentMonth: boolean;\n isInRange: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n dayNumber: number;\n} {\n const key = createDateKey(date);\n return {\n date,\n isCurrentMonth,\n isInRange: isDateInRangeCheck(date, maxDateValue, minDateValue),\n isSelected: modelValue ? isDateSelectedCheck(date, modelValue) : false,\n isToday: key === todayKey,\n key,\n dayNumber: date.getDate(),\n };\n}\n\n// Pre-calculation functions using VueUse get/set\nfunction calculateCalendarDays(): void {\n const firstDayOfMonth = new Date(viewYear, viewMonth, 1);\n const lastDayOfMonth = new Date(viewYear, viewMonth + 1, 0);\n const daysInMonth = lastDayOfMonth.getDate();\n const startingDayOfWeek = firstDayOfMonth.getDay();\n\n const { maxDate, minDate } = calendarState;\n const modelValue = get(model);\n const maxDateValue = isDefined(maxDate) ? get(maxDate) : null;\n const minDateValue = isDefined(minDate) ? get(minDate) : null;\n\n const result = [];\n\n // Previous month days\n if (startingDayOfWeek > 0) {\n const prevMonthLastDay = new Date(viewYear, viewMonth, 0).getDate();\n for (let i = startingDayOfWeek - 1; i >= 0; i--) {\n const date = new Date(viewYear, viewMonth - 1, prevMonthLastDay - i, 12, 0, 0, 0);\n result.push(createDayData(date, false, modelValue, maxDateValue, minDateValue));\n }\n }\n\n // Current month days\n for (let i = 1; i <= daysInMonth; i++) {\n const date = new Date(viewYear, viewMonth, i, 12, 0, 0, 0);\n result.push(createDayData(date, true, modelValue, maxDateValue, minDateValue));\n }\n\n // Next month days\n const totalDaysSoFar = result.length;\n const daysToAdd = 42 - totalDaysSoFar;\n for (let i = 1; i <= daysToAdd; i++) {\n const date = new Date(viewYear, viewMonth + 1, i, 12, 0, 0, 0);\n result.push(createDayData(date, false, modelValue, maxDateValue, minDateValue));\n }\n\n set(calendarDays, result);\n}\n\nfunction isDateInRangeCheck(\n date: Date,\n maxDateValue: Date | null,\n minDateValue: Date | null,\n): boolean {\n if (maxDateValue && date > maxDateValue)\n return false;\n if (minDateValue && date < minDateValue)\n return false;\n return true;\n}\n\nfunction isDateSelectedCheck(date: Date, modelValue: Date): boolean {\n return (\n date.getFullYear() === modelValue.getFullYear() &&\n date.getMonth() === modelValue.getMonth() &&\n date.getDate() === modelValue.getDate()\n );\n}\n\nfunction selectDate(dayData: { date: Date; isInRange: boolean; isSelected: boolean }): void {\n if (!dayData.isInRange)\n return;\n\n let updateModel: Date | undefined;\n\n if (dayData.isSelected && calendarState.allowEmpty) {\n updateModel = undefined;\n }\n else if (isDefined(model)) {\n const modelValue = get(model);\n const newDate = new Date(dayData.date);\n newDate.setHours(modelValue.getHours());\n newDate.setMinutes(modelValue.getMinutes());\n newDate.setSeconds(modelValue.getSeconds());\n updateModel = newDate;\n }\n else {\n updateModel = new Date(dayData.date);\n }\n set(model, updateModel);\n}\n\n// Initial calculation\ncalculateCalendarDays();\n\n// Watchers for recalculation only when needed\nwatch([() => viewMonth, () => viewYear], () => {\n calculateCalendarDays();\n});\n\nwatch(model, () => {\n calculateCalendarDays();\n});\n\nwatch(\n [() => calendarState.maxDate, () => calendarState.minDate],\n () => {\n calculateCalendarDays();\n },\n);\n</script>\n\n<template>\n <div class=\"w-full p-2 pt-0\">\n <div class=\"grid grid-cols-7 pb-2\">\n <span\n v-for=\"day in daysOfWeek\"\n :key=\"day\"\n class=\"py-2 text-xs font-medium text-center text-gray-500 dark:text-gray-400\"\n >\n {{ day }}\n </span>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0.5\">\n <button\n v-for=\"dayData in get(calendarDays)\"\n :key=\"dayData.key\"\n type=\"button\"\n :data-id=\"dayData.key\"\n :data-current-month=\"dayData.isCurrentMonth || undefined\"\n :data-selected=\"dayData.isSelected || undefined\"\n :data-today=\"(dayData.isToday && !dayData.isSelected) || undefined\"\n :class=\"dayButton({\n selected: dayData.isSelected,\n currentMonth: !dayData.isSelected && dayData.isCurrentMonth,\n inRange: dayData.isInRange,\n today: dayData.isToday && !dayData.isSelected,\n })\"\n :disabled=\"!dayData.isInRange\"\n @click.stop=\"selectDate(dayData)\"\n >\n {{ dayData.dayNumber }}\n </button>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAeA,MAAM,QAAQ,SAA6B,SAAA,YAAC;EAO5C,MAAM,gBAAgB,OAAyB,mBAAmB;EAGlE,SAAS,cAAc,MAAoB;GACzC,OAAO,GAAG,KAAK,YAAY,EAAE,IAAI,KAAK,SAAS,IAAI,EAAA,CAAG,SAAS,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,GAAG,GAAG;EAChI;EAGA,MAAM,aAAa,cAAc;EAEjC,MAAM,WAAW,8BAAc,IADb,KACa,CAAK;EAEpC,MAAM,YAAY,GAAG;GACnB,MAAM;GACN,UAAU;IACR,UAAU,EACR,MAAM,iFACR;IACA,cAAc;KACZ,MAAM;KACN,OAAO;IACT;IACA,SAAS,EACP,OAAO,qDACT;IACA,OAAO,EACL,MAAM,uJACR;GACF;GACA,kBAAkB,CAChB;IACE,UAAU;IACV,OAAO;GACT,CACF;EACF,CAAC;EAGD,MAAM,eAAe,IAUnB,CAAC,CAAC;EAEJ,SAAS,cACP,MACA,gBACA,YACA,cACA,cASA;GACA,MAAM,MAAM,cAAc,IAAI;GAC9B,OAAO;IACL;IACA;IACA,WAAW,mBAAmB,MAAM,cAAc,YAAY;IAC9D,YAAY,aAAa,oBAAoB,MAAM,UAAU,IAAI;IACjE,SAAS,QAAQ;IACjB;IACA,WAAW,KAAK,QAAQ;GAC1B;EACF;EAGA,SAAS,wBAA8B;GACrC,MAAM,kBAAkB,IAAI,KAAK,QAAA,UAAU,QAAA,WAAW,CAAC;GAEvD,MAAM,cAAc,IADO,KAAK,QAAA,UAAU,QAAA,YAAY,GAAG,CACrC,CAAA,CAAe,QAAQ;GAC3C,MAAM,oBAAoB,gBAAgB,OAAO;GAEjD,MAAM,EAAE,SAAS,YAAY;GAC7B,MAAM,aAAa,IAAI,KAAK;GAC5B,MAAM,eAAe,UAAU,OAAO,IAAI,IAAI,OAAO,IAAI;GACzD,MAAM,eAAe,UAAU,OAAO,IAAI,IAAI,OAAO,IAAI;GAEzD,MAAM,SAAS,CAAC;GAGhB,IAAI,oBAAoB,GAAG;IACzB,MAAM,mBAAmB,IAAI,KAAK,QAAA,UAAU,QAAA,WAAW,CAAC,CAAC,CAAC,QAAQ;IAClE,KAAK,IAAI,IAAI,oBAAoB,GAAG,KAAK,GAAG,KAAK;KAC/C,MAAM,OAAO,IAAI,KAAK,QAAA,UAAU,QAAA,YAAY,GAAG,mBAAmB,GAAG,IAAI,GAAG,GAAG,CAAC;KAChF,OAAO,KAAK,cAAc,MAAM,OAAO,YAAY,cAAc,YAAY,CAAC;IAChF;GACF;GAGA,KAAK,IAAI,IAAI,GAAG,KAAK,aAAa,KAAK;IACrC,MAAM,OAAO,IAAI,KAAK,QAAA,UAAU,QAAA,WAAW,GAAG,IAAI,GAAG,GAAG,CAAC;IACzD,OAAO,KAAK,cAAc,MAAM,MAAM,YAAY,cAAc,YAAY,CAAC;GAC/E;GAIA,MAAM,YAAY,KADK,OAAO;GAE9B,KAAK,IAAI,IAAI,GAAG,KAAK,WAAW,KAAK;IACnC,MAAM,OAAO,IAAI,KAAK,QAAA,UAAU,QAAA,YAAY,GAAG,GAAG,IAAI,GAAG,GAAG,CAAC;IAC7D,OAAO,KAAK,cAAc,MAAM,OAAO,YAAY,cAAc,YAAY,CAAC;GAChF;GAEA,IAAI,cAAc,MAAM;EAC1B;EAEA,SAAS,mBACP,MACA,cACA,cACS;GACT,IAAI,gBAAgB,OAAO,cACzB,OAAO;GACT,IAAI,gBAAgB,OAAO,cACzB,OAAO;GACT,OAAO;EACT;EAEA,SAAS,oBAAoB,MAAY,YAA2B;GAClE,OACE,KAAK,YAAY,MAAM,WAAW,YAAY,KAC9C,KAAK,SAAS,MAAM,WAAW,SAAS,KACxC,KAAK,QAAQ,MAAM,WAAW,QAAQ;EAE1C;EAEA,SAAS,WAAW,SAAwE;GAC1F,IAAI,CAAC,QAAQ,WACX;GAEF,IAAI;GAEJ,IAAI,QAAQ,cAAc,cAAc,YACtC,cAAc,KAAA;QAEX,IAAI,UAAU,KAAK,GAAG;IACzB,MAAM,aAAa,IAAI,KAAK;IAC5B,MAAM,UAAU,IAAI,KAAK,QAAQ,IAAI;IACrC,QAAQ,SAAS,WAAW,SAAS,CAAC;IACtC,QAAQ,WAAW,WAAW,WAAW,CAAC;IAC1C,QAAQ,WAAW,WAAW,WAAW,CAAC;IAC1C,cAAc;GAChB,OAEE,cAAc,IAAI,KAAK,QAAQ,IAAI;GAErC,IAAI,OAAO,WAAW;EACxB;EAGA,sBAAsB;EAGtB,MAAM,OAAO,QAAA,iBAAiB,QAAA,QAAQ,SAAS;GAC7C,sBAAsB;EACxB,CAAC;EAED,MAAM,aAAa;GACjB,sBAAsB;EACxB,CAAC;EAED,MACE,OAAO,cAAc,eAAe,cAAc,OAAO,SACnD;GACJ,sBAAsB;EACxB,CACF;;uBAIE,mBAgCM,OAhCN,YAgCM,CA/BJ,mBAQM,OARN,YAQM,EAAA,UAAA,IAAA,GAPJ,mBAMO,UAAA,MAAA,WALS,MAAA,UAAA,IAAP,QAAG;wBADZ,mBAMO,QAAA;KAJJ,KAAK;KACN,OAAM;uBAEH,GAAG,GAAA,CAAA;gBAIV,mBAoBM,OApBN,YAoBM,EAAA,UAAA,IAAA,GAnBJ,mBAkBS,UAAA,MAAA,WAjBW,MAAA,GAAA,CAAG,CAAC,aAAA,KAAY,IAA3B,YAAO;wBADhB,mBAkBS,UAAA;KAhBN,KAAK,QAAQ;KACd,MAAK;KACJ,WAAS,QAAQ;KACjB,sBAAoB,QAAQ,kBAAkB,KAAA;KAC9C,iBAAe,QAAQ,cAAc,KAAA;KACrC,cAAa,QAAQ,WAAO,CAAK,QAAQ,cAAe,KAAA;KACxD,OAAK,eAAE,MAAA,SAAA,CAAS,CAAA;gBAAuB,QAAQ;qBAAqC,QAAQ,cAAc,QAAQ;eAAmC,QAAQ;aAA4B,QAAQ,WAAO,CAAK,QAAQ;;KAMrN,UAAQ,CAAG,QAAQ;KACnB,SAAK,eAAA,WAAO,WAAW,OAAO,GAAA,CAAA,MAAA,CAAA;uBAE5B,QAAQ,SAAS,GAAA,IAAA,UAAA"}
|
|
1
|
+
{"version":3,"file":"RuiCalendarGrid.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/calendar/RuiCalendarGrid.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { get, isDefined, set } from '@vueuse/core';\nimport { computed, inject, nextTick, ref, useTemplateRef, watch } from 'vue';\nimport {\n CalendarStateSymbol,\n getDaysOfWeek,\n type RuiCalendarState,\n} from '@/components/calendar/state';\nimport { tv } from '@/utils/tv';\n\ndefineOptions({\n name: 'RuiCalendarGrid',\n inheritAttrs: false,\n});\n\nconst model = defineModel<Date | undefined>();\n\nconst { viewMonth, viewYear } = defineProps<{\n viewMonth: number;\n viewYear: number;\n}>();\n\nconst calendarState = inject<RuiCalendarState>(CalendarStateSymbol) as RuiCalendarState;\n\n// Helper functions to reduce duplication\nfunction createDateKey(date: Date): string {\n return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;\n}\n\n// Pre-calculated constants\nconst daysOfWeek = getDaysOfWeek();\nconst today = new Date();\nconst todayKey = createDateKey(today);\n\nconst dayButton = tv({\n base: 'h-9 w-full flex items-center justify-center text-sm rounded-full mx-auto max-w-[2.25rem] transition-colors duration-150 ease-in-out border-none outline-none cursor-pointer focus:ring-2 focus:ring-rui-primary focus:ring-opacity-50',\n variants: {\n selected: {\n true: 'bg-rui-primary text-white hover:bg-rui-primary/90 dark:hover:bg-rui-primary/90',\n },\n currentMonth: {\n true: 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700',\n false: 'text-gray-400 dark:text-gray-600',\n },\n inRange: {\n false: 'opacity-50 cursor-not-allowed hover:bg-transparent',\n },\n today: {\n true: `relative after:content-[''] after:absolute after:size-1 after:rounded-full after:bottom-1 after:left-1/2 after:-translate-x-1/2 after:bg-rui-primary`,\n },\n },\n compoundVariants: [\n {\n selected: true,\n class: 'text-white dark:text-white',\n },\n ],\n});\n\n// Pre-calculated arrays for performance - using refs for better control\nconst calendarDays = ref<\n Array<{\n date: Date;\n isCurrentMonth: boolean;\n isInRange: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n dayNumber: number;\n }>\n>([]);\n\nfunction createDayData(\n date: Date,\n isCurrentMonth: boolean,\n modelValue: Date | null | undefined,\n maxDateValue: Date | null,\n minDateValue: Date | null,\n): {\n date: Date;\n isCurrentMonth: boolean;\n isInRange: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n dayNumber: number;\n} {\n const key = createDateKey(date);\n return {\n date,\n isCurrentMonth,\n isInRange: isDateInRangeCheck(date, maxDateValue, minDateValue),\n isSelected: modelValue ? isDateSelectedCheck(date, modelValue) : false,\n isToday: key === todayKey,\n key,\n dayNumber: date.getDate(),\n };\n}\n\n// Pre-calculation functions using VueUse get/set\nfunction calculateCalendarDays(): void {\n const firstDayOfMonth = new Date(viewYear, viewMonth, 1);\n const lastDayOfMonth = new Date(viewYear, viewMonth + 1, 0);\n const daysInMonth = lastDayOfMonth.getDate();\n const startingDayOfWeek = firstDayOfMonth.getDay();\n\n const { maxDate, minDate } = calendarState;\n const modelValue = get(model);\n const maxDateValue = isDefined(maxDate) ? get(maxDate) : null;\n const minDateValue = isDefined(minDate) ? get(minDate) : null;\n\n const result = [];\n\n // Previous month days\n if (startingDayOfWeek > 0) {\n const prevMonthLastDay = new Date(viewYear, viewMonth, 0).getDate();\n for (let i = startingDayOfWeek - 1; i >= 0; i--) {\n const date = new Date(viewYear, viewMonth - 1, prevMonthLastDay - i, 12, 0, 0, 0);\n result.push(createDayData(date, false, modelValue, maxDateValue, minDateValue));\n }\n }\n\n // Current month days\n for (let i = 1; i <= daysInMonth; i++) {\n const date = new Date(viewYear, viewMonth, i, 12, 0, 0, 0);\n result.push(createDayData(date, true, modelValue, maxDateValue, minDateValue));\n }\n\n // Next month days\n const totalDaysSoFar = result.length;\n const daysToAdd = 42 - totalDaysSoFar;\n for (let i = 1; i <= daysToAdd; i++) {\n const date = new Date(viewYear, viewMonth + 1, i, 12, 0, 0, 0);\n result.push(createDayData(date, false, modelValue, maxDateValue, minDateValue));\n }\n\n set(calendarDays, result);\n}\n\nfunction isDateInRangeCheck(\n date: Date,\n maxDateValue: Date | null,\n minDateValue: Date | null,\n): boolean {\n if (maxDateValue && date > maxDateValue)\n return false;\n if (minDateValue && date < minDateValue)\n return false;\n return true;\n}\n\nfunction isDateSelectedCheck(date: Date, modelValue: Date): boolean {\n return (\n date.getFullYear() === modelValue.getFullYear() &&\n date.getMonth() === modelValue.getMonth() &&\n date.getDate() === modelValue.getDate()\n );\n}\n\nfunction selectDate(dayData: { date: Date; isInRange: boolean; isSelected: boolean }): void {\n if (!dayData.isInRange)\n return;\n\n let updateModel: Date | undefined;\n\n if (dayData.isSelected && calendarState.allowEmpty) {\n updateModel = undefined;\n }\n else if (isDefined(model)) {\n const modelValue = get(model);\n const newDate = new Date(dayData.date);\n newDate.setHours(modelValue.getHours());\n newDate.setMinutes(modelValue.getMinutes());\n newDate.setSeconds(modelValue.getSeconds());\n updateModel = newDate;\n }\n else {\n updateModel = new Date(dayData.date);\n }\n set(model, updateModel);\n}\n\n// --- keyboard navigation -------------------------------------------------\n// The grid is a roving tabindex: one day is tabbable and the arrows move it,\n// so crossing the calendar costs one Tab instead of 42.\n\nconst gridRef = useTemplateRef<HTMLDivElement>('grid');\nconst activeKey = ref<string>('');\n\nconst dayLabelFormatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'full' });\n\nconst KEY_DAY_OFFSETS: Record<string, number> = {\n ArrowDown: 7,\n ArrowLeft: -1,\n ArrowRight: 1,\n ArrowUp: -7,\n};\n\nconst weeks = computed<Array<typeof calendarDays.value>>(() => {\n const days = get(calendarDays);\n const result = [];\n for (let i = 0; i < days.length; i += 7)\n result.push(days.slice(i, i + 7));\n return result;\n});\n\nfunction preferredActiveKey(): string {\n const days = get(calendarDays);\n const selected = days.find(day => day.isSelected && day.isInRange);\n const todayInView = days.find(day => day.isToday && day.isCurrentMonth && day.isInRange);\n const firstSelectable = days.find(day => day.isCurrentMonth && day.isInRange);\n return (selected ?? todayInView ?? firstSelectable ?? days[0])?.key ?? '';\n}\n\nfunction syncActiveKey(): void {\n const days = get(calendarDays);\n if (!days.some(day => day.key === get(activeKey)))\n set(activeKey, preferredActiveKey());\n}\n\nfunction keyToDate(key: string): Date {\n const [year, month, day] = key.split('-').map(Number);\n return new Date(year ?? 0, (month ?? 1) - 1, day ?? 1, 12, 0, 0, 0);\n}\n\nfunction focusKey(key: string): void {\n set(activeKey, key);\n nextTick(() => {\n get(gridRef)?.querySelector<HTMLButtonElement>(`[data-id=\"${key}\"]`)?.focus();\n });\n}\n\nfunction moveTo(target: Date): void {\n const { maxDate, minDate } = calendarState;\n const maxDateValue = isDefined(maxDate) ? get(maxDate) : null;\n const minDateValue = isDefined(minDate) ? get(minDate) : null;\n\n // min/max bound a contiguous range, so a blocked step means the edge is here\n if (!isDateInRangeCheck(target, maxDateValue, minDateValue))\n return;\n\n const key = createDateKey(target);\n\n // Landing on another month turns the page, even when that day happens to be\n // rendered as a spill day in the current window — otherwise the header and\n // the focused day disagree about which month is being edited.\n if (target.getMonth() === viewMonth && target.getFullYear() === viewYear) {\n focusKey(key);\n return;\n }\n\n set(calendarState.viewMonth, target.getMonth());\n set(calendarState.viewYear, target.getFullYear());\n nextTick(() => {\n focusKey(key);\n });\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n const active = get(activeKey);\n if (!active)\n return;\n\n const current = keyToDate(active);\n const dayOffset = KEY_DAY_OFFSETS[event.key];\n\n if (dayOffset !== undefined) {\n event.preventDefault();\n moveTo(new Date(current.getFullYear(), current.getMonth(), current.getDate() + dayOffset, 12, 0, 0, 0));\n }\n else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n const offset = event.key === 'Home' ? -current.getDay() : 6 - current.getDay();\n moveTo(new Date(current.getFullYear(), current.getMonth(), current.getDate() + offset, 12, 0, 0, 0));\n }\n else if (event.key === 'PageUp' || event.key === 'PageDown') {\n event.preventDefault();\n const delta = event.key === 'PageUp' ? -1 : 1;\n moveTo(new Date(current.getFullYear(), current.getMonth() + delta, current.getDate(), 12, 0, 0, 0));\n }\n}\n\n// Initial calculation\ncalculateCalendarDays();\nsyncActiveKey();\n\n// Watchers for recalculation only when needed\nwatch([() => viewMonth, () => viewYear], () => {\n calculateCalendarDays();\n syncActiveKey();\n});\n\nwatch(model, () => {\n calculateCalendarDays();\n syncActiveKey();\n});\n\nwatch(\n [() => calendarState.maxDate, () => calendarState.minDate],\n () => {\n calculateCalendarDays();\n syncActiveKey();\n },\n);\n</script>\n\n<template>\n <div class=\"w-full p-2 pt-0\">\n <div\n ref=\"grid\"\n role=\"grid\"\n class=\"grid grid-cols-7 gap-0.5\"\n @keydown=\"onKeydown($event)\"\n >\n <div\n role=\"row\"\n class=\"contents\"\n >\n <span\n v-for=\"day in daysOfWeek\"\n :key=\"day\"\n role=\"columnheader\"\n class=\"py-2 pb-4 text-xs font-medium text-center text-gray-500 dark:text-gray-400\"\n >\n {{ day }}\n </span>\n </div>\n\n <div\n v-for=\"(week, index) in weeks\"\n :key=\"index\"\n role=\"row\"\n class=\"contents\"\n >\n <button\n v-for=\"dayData in week\"\n :key=\"dayData.key\"\n type=\"button\"\n role=\"gridcell\"\n :data-id=\"dayData.key\"\n :data-current-month=\"dayData.isCurrentMonth || undefined\"\n :data-selected=\"dayData.isSelected || undefined\"\n :data-today=\"(dayData.isToday && !dayData.isSelected) || undefined\"\n :class=\"dayButton({\n selected: dayData.isSelected,\n currentMonth: !dayData.isSelected && dayData.isCurrentMonth,\n inRange: dayData.isInRange,\n today: dayData.isToday && !dayData.isSelected,\n })\"\n :disabled=\"!dayData.isInRange\"\n :tabindex=\"dayData.key === activeKey ? 0 : -1\"\n :aria-label=\"dayLabelFormatter.format(dayData.date)\"\n :aria-selected=\"dayData.isSelected\"\n :aria-current=\"dayData.isToday ? 'date' : undefined\"\n @click.stop=\"selectDate(dayData)\"\n @focus=\"activeKey = dayData.key\"\n >\n {{ dayData.dayNumber }}\n </button>\n </div>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -104,11 +104,12 @@ var RuiCalendarMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
104
104
|
set(yearsInRange, new Array(range.length).fill(true));
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
|
-
|
|
107
|
+
const result = range.map((year) => {
|
|
108
108
|
const date = new Date(year, currentMonth);
|
|
109
109
|
if (isDefined(maxDate) && date > get(maxDate)) return false;
|
|
110
110
|
return !isDefined(minDate) || date >= get(minDate);
|
|
111
|
-
})
|
|
111
|
+
});
|
|
112
|
+
set(yearsInRange, result);
|
|
112
113
|
}
|
|
113
114
|
function toggleSelection() {
|
|
114
115
|
set(viewMode, get(viewMode) === "months" ? "years" : "months");
|
|
@@ -137,7 +138,10 @@ var RuiCalendarMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
137
138
|
month: __props.viewMonth,
|
|
138
139
|
year: nextYear
|
|
139
140
|
});
|
|
140
|
-
} else
|
|
141
|
+
} else {
|
|
142
|
+
const lastYearInRange = get(startYear) + 11;
|
|
143
|
+
set(startYear, lastYearInRange + 1);
|
|
144
|
+
}
|
|
141
145
|
}
|
|
142
146
|
function handlePrev() {
|
|
143
147
|
if (get(viewMode) === "months") {
|
|
@@ -146,7 +150,10 @@ var RuiCalendarMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
146
150
|
month: __props.viewMonth,
|
|
147
151
|
year: previousYear
|
|
148
152
|
});
|
|
149
|
-
} else
|
|
153
|
+
} else {
|
|
154
|
+
const firstYearInRange = get(startYear);
|
|
155
|
+
set(startYear, firstYearInRange - 12);
|
|
156
|
+
}
|
|
150
157
|
}
|
|
151
158
|
calculateYearRange();
|
|
152
159
|
calculateTitle();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/calendar/RuiCalendarMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { get, isDefined, set } from '@vueuse/core';\nimport { ref, watch } from 'vue';\nimport {\n CalendarStateSymbol,\n getShortMonthNames,\n type MonthYearSelection,\n type RuiCalendarState,\n} from '@/components/calendar/state';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiMenu from '@/components/overlays/menu/RuiMenu.vue';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport { tv } from '@/utils/tv';\nimport RuiButton from '../buttons/button/RuiButton.vue';\n\ndefineOptions({\n name: 'RuiCalendarMenu',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<boolean>();\n\nconst { viewMonth, viewYear } = defineProps<{\n anchorEl: HTMLElement | undefined;\n viewMonth: number;\n viewYear: number;\n}>();\n\nconst emit = defineEmits<{\n select: [MonthYearSelection];\n}>();\n\nconst MENU_OPTIONS: FloatingOptions = { placement: Placement.bottomStart };\n\n// Core reactive state - minimal\nconst viewMode = ref<'months' | 'years'>('months');\nconst startYear = ref<number>(viewYear - 6);\n\n// Pre-calculated constants\nconst months = getShortMonthNames();\nconst calendarState = inject<RuiCalendarState>(CalendarStateSymbol) as RuiCalendarState;\n\nconst navButtonClass = 'p-1 rounded-full text-gray-500 hover:text-rui-primary hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-600 dark:hover:text-rui-primary';\n\nconst cellButton = tv({\n base: 'h-9 w-full flex items-center justify-center text-sm font-medium rounded-md transition-colors duration-150 ease-in-out border-none outline-none cursor-pointer focus:ring-2 focus:ring-rui-primary focus:ring-opacity-50',\n variants: {\n selected: {\n true: 'bg-rui-primary text-white hover:bg-rui-primary-darker active:bg-rui-primary-darker dark:bg-rui-primary dark:text-white dark:hover:bg-rui-primary-darker',\n },\n inRange: {\n true: 'text-gray-700 bg-transparent hover:bg-gray-100 active:bg-gray-200 dark:text-gray-300 dark:hover:bg-gray-700 dark:active:bg-gray-600',\n false: 'opacity-50 cursor-not-allowed bg-transparent hover:bg-transparent active:bg-transparent dark:hover:bg-transparent dark:active:bg-transparent pointer-events-none text-gray-400 dark:text-gray-600',\n },\n selectedOutOfRange: {\n true: 'bg-gray-300 text-gray-500 dark:bg-gray-600 dark:text-gray-400',\n },\n },\n});\n\n// Pre-calculated arrays for performance - using refs for better control\nconst yearRange = ref<number[]>([]);\nconst monthsInRange = ref<boolean[]>([]);\nconst yearsInRange = ref<boolean[]>([]);\nconst title = ref<string>('');\nconst canGoToNext = ref<boolean>(true);\nconst canGoToPrev = ref<boolean>(true);\n\n// Pre-calculation functions using VueUse get/set\nfunction calculateYearRange(): void {\n const start = get(startYear);\n set(\n yearRange,\n Array.from({ length: 12 }, (_, i) => start + i),\n );\n}\n\nfunction calculateTitle(): void {\n if (get(viewMode) === 'months') {\n set(title, viewYear.toString());\n }\n else {\n const range = get(yearRange);\n set(title, `${range[0]} - ${range.at(-1)}`);\n }\n}\n\nfunction calculateNavigation(): void {\n const { maxDate, minDate } = calendarState;\n const mode = get(viewMode);\n\n if (mode === 'months') {\n const nextYear = viewYear + 1;\n const previousYear = viewYear - 1;\n\n set(canGoToNext, !isDefined(maxDate) || nextYear <= get(maxDate).getFullYear());\n set(canGoToPrev, !isDefined(minDate) || previousYear >= get(minDate).getFullYear());\n }\n else {\n const lastYearInRange = get(startYear) + 11;\n const firstYearInRange = get(startYear);\n\n // Check if we can navigate to the next range of years\n const nextRangeStart = lastYearInRange + 1;\n set(canGoToNext, !isDefined(maxDate) || nextRangeStart <= get(maxDate).getFullYear());\n\n // Check if we can navigate to the previous range of years\n const prevRangeEnd = firstYearInRange - 1;\n set(canGoToPrev, !isDefined(minDate) || prevRangeEnd >= get(minDate).getFullYear());\n }\n}\n\nfunction calculateMonthsInRange(): void {\n const { minDate, maxDate } = calendarState;\n const currentYear = viewYear;\n const result = new Array(12).fill(true);\n\n if (isDefined(minDate) || isDefined(maxDate)) {\n for (let month = 0; month < 12; month++) {\n const date = new Date(currentYear, month);\n if (isDefined(maxDate) && date > get(maxDate)) {\n result[month] = false;\n }\n if (isDefined(minDate) && date < get(minDate)) {\n result[month] = false;\n }\n }\n }\n\n set(monthsInRange, result);\n}\n\nfunction calculateYearsInRange(): void {\n const { minDate, maxDate } = calendarState;\n const range = get(yearRange);\n const currentMonth = viewMonth;\n\n if (!isDefined(minDate) && !isDefined(maxDate)) {\n set(yearsInRange, new Array(range.length).fill(true));\n return;\n }\n\n const result = range.map((year) => {\n const date = new Date(year, currentMonth);\n if (isDefined(maxDate) && date > get(maxDate)) {\n return false;\n }\n return !isDefined(minDate) || date >= get(minDate);\n });\n\n set(yearsInRange, result);\n}\n\nfunction toggleSelection(): void {\n set(viewMode, get(viewMode) === 'months' ? 'years' : 'months');\n}\n\nfunction selectMonth(month: number): void {\n if (!get(monthsInRange)[month])\n return;\n emit('select', { month, year: viewYear });\n set(modelValue, false);\n}\n\nfunction selectYear(year: number): void {\n const yearIndex = get(yearRange).indexOf(year);\n if (yearIndex === -1 || !get(yearsInRange)[yearIndex])\n return;\n\n emit('select', { month: viewMonth, year });\n set(viewMode, 'months');\n}\n\nfunction handleNext(): void {\n if (get(viewMode) === 'months') {\n const nextYear = viewYear + 1;\n // Emit directly - navigation is already constrained by canGoToNext\n emit('select', { month: viewMonth, year: nextYear });\n }\n else {\n const lastYearInRange = get(startYear) + 11;\n set(startYear, lastYearInRange + 1);\n }\n}\n\nfunction handlePrev(): void {\n if (get(viewMode) === 'months') {\n const previousYear = viewYear - 1;\n // Emit directly - navigation is already constrained by canGoToPrev\n emit('select', { month: viewMonth, year: previousYear });\n }\n else {\n const firstYearInRange = get(startYear);\n set(startYear, firstYearInRange - 12);\n }\n}\n\n// Initial calculations\ncalculateYearRange();\ncalculateTitle();\ncalculateNavigation();\ncalculateMonthsInRange();\ncalculateYearsInRange();\n\n// Watchers for recalculation only when needed\nwatch(startYear, () => {\n calculateYearRange();\n calculateTitle();\n calculateNavigation();\n calculateYearsInRange();\n});\n\nwatch(viewMode, () => {\n calculateTitle();\n calculateNavigation();\n});\n\nwatch(\n () => viewYear,\n () => {\n calculateTitle();\n calculateNavigation();\n calculateMonthsInRange();\n calculateYearsInRange();\n },\n);\n\nwatch(\n () => viewMonth,\n () => {\n calculateYearsInRange();\n },\n);\n</script>\n\n<template>\n <RuiMenu\n v-model=\"modelValue\"\n :anchor-el=\"anchorEl\"\n :options=\"MENU_OPTIONS\"\n >\n <div class=\"w-64 shadow-lg overflow-hidden\">\n <div class=\"flex items-center justify-center p-1 font-medium text-gray-800 dark:text-gray-200 border-b border-rui-grey-200 dark:border-rui-grey-800 cursor-pointer hover:bg-rui-grey-100 dark:hover:bg-rui-grey-800\">\n <RuiButton\n type=\"button\"\n :class=\"navButtonClass\"\n icon\n :disabled=\"!get(canGoToPrev)\"\n variant=\"text\"\n @click.stop=\"handlePrev()\"\n >\n <RuiIcon\n name=\"lu-chevron-left\"\n size=\"20\"\n />\n </RuiButton>\n\n <div\n class=\"flex-1 text-center cursor-pointer\"\n @click.stop=\"toggleSelection()\"\n >\n {{ get(title) }}\n </div>\n\n <RuiButton\n type=\"button\"\n :class=\"navButtonClass\"\n icon\n :disabled=\"!get(canGoToNext)\"\n variant=\"text\"\n @click.stop=\"handleNext()\"\n >\n <RuiIcon\n name=\"lu-chevron-right\"\n size=\"20\"\n />\n </RuiButton>\n </div>\n\n <div\n v-if=\"viewMode === 'months'\"\n class=\"grid grid-cols-3 gap-1 p-2\"\n >\n <button\n v-for=\"(month, index) in months\"\n :key=\"month\"\n type=\"button\"\n :class=\"cellButton({\n selected: index === viewMonth && get(monthsInRange)[index],\n selectedOutOfRange: index === viewMonth && !get(monthsInRange)[index],\n inRange: index !== viewMonth ? get(monthsInRange)[index] : undefined,\n })\"\n :disabled=\"!get(monthsInRange)[index]\"\n @click.stop=\"selectMonth(index)\"\n >\n {{ month.substring(0, 3) }}\n </button>\n </div>\n\n <div\n v-else-if=\"viewMode === 'years'\"\n class=\"grid grid-cols-3 gap-1 p-2\"\n >\n <button\n v-for=\"(year, index) in yearRange\"\n :key=\"year\"\n type=\"button\"\n :class=\"cellButton({\n selected: year === viewYear && get(yearsInRange)[index],\n selectedOutOfRange: year === viewYear && !get(yearsInRange)[index],\n inRange: year !== viewYear ? get(yearsInRange)[index] : undefined,\n })\"\n :disabled=\"!get(yearsInRange)[index]\"\n @click.stop=\"selectYear(year)\"\n >\n {{ year }}\n </button>\n </div>\n </div>\n </RuiMenu>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/calendar/RuiCalendarMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { get, isDefined, set } from '@vueuse/core';\nimport { ref, watch } from 'vue';\nimport {\n CalendarStateSymbol,\n getShortMonthNames,\n type MonthYearSelection,\n type RuiCalendarState,\n} from '@/components/calendar/state';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiMenu from '@/components/overlays/menu/RuiMenu.vue';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport { tv } from '@/utils/tv';\nimport RuiButton from '../buttons/button/RuiButton.vue';\n\ndefineOptions({\n name: 'RuiCalendarMenu',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<boolean>();\n\nconst { viewMonth, viewYear } = defineProps<{\n anchorEl: HTMLElement | undefined;\n viewMonth: number;\n viewYear: number;\n}>();\n\nconst emit = defineEmits<{\n select: [MonthYearSelection];\n}>();\n\nconst MENU_OPTIONS: FloatingOptions = { placement: Placement.bottomStart };\n\n// Core reactive state - minimal\nconst viewMode = ref<'months' | 'years'>('months');\nconst startYear = ref<number>(viewYear - 6);\n\n// Pre-calculated constants\nconst months = getShortMonthNames();\nconst calendarState = inject<RuiCalendarState>(CalendarStateSymbol) as RuiCalendarState;\n\nconst navButtonClass = 'p-1 rounded-full text-gray-500 hover:text-rui-primary hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-600 dark:hover:text-rui-primary';\n\nconst cellButton = tv({\n base: 'h-9 w-full flex items-center justify-center text-sm font-medium rounded-md transition-colors duration-150 ease-in-out border-none outline-none cursor-pointer focus:ring-2 focus:ring-rui-primary focus:ring-opacity-50',\n variants: {\n selected: {\n true: 'bg-rui-primary text-white hover:bg-rui-primary-darker active:bg-rui-primary-darker dark:bg-rui-primary dark:text-white dark:hover:bg-rui-primary-darker',\n },\n inRange: {\n true: 'text-gray-700 bg-transparent hover:bg-gray-100 active:bg-gray-200 dark:text-gray-300 dark:hover:bg-gray-700 dark:active:bg-gray-600',\n false: 'opacity-50 cursor-not-allowed bg-transparent hover:bg-transparent active:bg-transparent dark:hover:bg-transparent dark:active:bg-transparent pointer-events-none text-gray-400 dark:text-gray-600',\n },\n selectedOutOfRange: {\n true: 'bg-gray-300 text-gray-500 dark:bg-gray-600 dark:text-gray-400',\n },\n },\n});\n\n// Pre-calculated arrays for performance - using refs for better control\nconst yearRange = ref<number[]>([]);\nconst monthsInRange = ref<boolean[]>([]);\nconst yearsInRange = ref<boolean[]>([]);\nconst title = ref<string>('');\nconst canGoToNext = ref<boolean>(true);\nconst canGoToPrev = ref<boolean>(true);\n\n// Pre-calculation functions using VueUse get/set\nfunction calculateYearRange(): void {\n const start = get(startYear);\n set(\n yearRange,\n Array.from({ length: 12 }, (_, i) => start + i),\n );\n}\n\nfunction calculateTitle(): void {\n if (get(viewMode) === 'months') {\n set(title, viewYear.toString());\n }\n else {\n const range = get(yearRange);\n set(title, `${range[0]} - ${range.at(-1)}`);\n }\n}\n\nfunction calculateNavigation(): void {\n const { maxDate, minDate } = calendarState;\n const mode = get(viewMode);\n\n if (mode === 'months') {\n const nextYear = viewYear + 1;\n const previousYear = viewYear - 1;\n\n set(canGoToNext, !isDefined(maxDate) || nextYear <= get(maxDate).getFullYear());\n set(canGoToPrev, !isDefined(minDate) || previousYear >= get(minDate).getFullYear());\n }\n else {\n const lastYearInRange = get(startYear) + 11;\n const firstYearInRange = get(startYear);\n\n // Check if we can navigate to the next range of years\n const nextRangeStart = lastYearInRange + 1;\n set(canGoToNext, !isDefined(maxDate) || nextRangeStart <= get(maxDate).getFullYear());\n\n // Check if we can navigate to the previous range of years\n const prevRangeEnd = firstYearInRange - 1;\n set(canGoToPrev, !isDefined(minDate) || prevRangeEnd >= get(minDate).getFullYear());\n }\n}\n\nfunction calculateMonthsInRange(): void {\n const { minDate, maxDate } = calendarState;\n const currentYear = viewYear;\n const result = new Array(12).fill(true);\n\n if (isDefined(minDate) || isDefined(maxDate)) {\n for (let month = 0; month < 12; month++) {\n const date = new Date(currentYear, month);\n if (isDefined(maxDate) && date > get(maxDate)) {\n result[month] = false;\n }\n if (isDefined(minDate) && date < get(minDate)) {\n result[month] = false;\n }\n }\n }\n\n set(monthsInRange, result);\n}\n\nfunction calculateYearsInRange(): void {\n const { minDate, maxDate } = calendarState;\n const range = get(yearRange);\n const currentMonth = viewMonth;\n\n if (!isDefined(minDate) && !isDefined(maxDate)) {\n set(yearsInRange, new Array(range.length).fill(true));\n return;\n }\n\n const result = range.map((year) => {\n const date = new Date(year, currentMonth);\n if (isDefined(maxDate) && date > get(maxDate)) {\n return false;\n }\n return !isDefined(minDate) || date >= get(minDate);\n });\n\n set(yearsInRange, result);\n}\n\nfunction toggleSelection(): void {\n set(viewMode, get(viewMode) === 'months' ? 'years' : 'months');\n}\n\nfunction selectMonth(month: number): void {\n if (!get(monthsInRange)[month])\n return;\n emit('select', { month, year: viewYear });\n set(modelValue, false);\n}\n\nfunction selectYear(year: number): void {\n const yearIndex = get(yearRange).indexOf(year);\n if (yearIndex === -1 || !get(yearsInRange)[yearIndex])\n return;\n\n emit('select', { month: viewMonth, year });\n set(viewMode, 'months');\n}\n\nfunction handleNext(): void {\n if (get(viewMode) === 'months') {\n const nextYear = viewYear + 1;\n // Emit directly - navigation is already constrained by canGoToNext\n emit('select', { month: viewMonth, year: nextYear });\n }\n else {\n const lastYearInRange = get(startYear) + 11;\n set(startYear, lastYearInRange + 1);\n }\n}\n\nfunction handlePrev(): void {\n if (get(viewMode) === 'months') {\n const previousYear = viewYear - 1;\n // Emit directly - navigation is already constrained by canGoToPrev\n emit('select', { month: viewMonth, year: previousYear });\n }\n else {\n const firstYearInRange = get(startYear);\n set(startYear, firstYearInRange - 12);\n }\n}\n\n// Initial calculations\ncalculateYearRange();\ncalculateTitle();\ncalculateNavigation();\ncalculateMonthsInRange();\ncalculateYearsInRange();\n\n// Watchers for recalculation only when needed\nwatch(startYear, () => {\n calculateYearRange();\n calculateTitle();\n calculateNavigation();\n calculateYearsInRange();\n});\n\nwatch(viewMode, () => {\n calculateTitle();\n calculateNavigation();\n});\n\nwatch(\n () => viewYear,\n () => {\n calculateTitle();\n calculateNavigation();\n calculateMonthsInRange();\n calculateYearsInRange();\n },\n);\n\nwatch(\n () => viewMonth,\n () => {\n calculateYearsInRange();\n },\n);\n</script>\n\n<template>\n <RuiMenu\n v-model=\"modelValue\"\n :anchor-el=\"anchorEl\"\n :options=\"MENU_OPTIONS\"\n >\n <div class=\"w-64 shadow-lg overflow-hidden\">\n <div class=\"flex items-center justify-center p-1 font-medium text-gray-800 dark:text-gray-200 border-b border-rui-grey-200 dark:border-rui-grey-800 cursor-pointer hover:bg-rui-grey-100 dark:hover:bg-rui-grey-800\">\n <RuiButton\n type=\"button\"\n :class=\"navButtonClass\"\n icon\n :disabled=\"!get(canGoToPrev)\"\n variant=\"text\"\n @click.stop=\"handlePrev()\"\n >\n <RuiIcon\n name=\"lu-chevron-left\"\n size=\"20\"\n />\n </RuiButton>\n\n <div\n class=\"flex-1 text-center cursor-pointer\"\n @click.stop=\"toggleSelection()\"\n >\n {{ get(title) }}\n </div>\n\n <RuiButton\n type=\"button\"\n :class=\"navButtonClass\"\n icon\n :disabled=\"!get(canGoToNext)\"\n variant=\"text\"\n @click.stop=\"handleNext()\"\n >\n <RuiIcon\n name=\"lu-chevron-right\"\n size=\"20\"\n />\n </RuiButton>\n </div>\n\n <div\n v-if=\"viewMode === 'months'\"\n class=\"grid grid-cols-3 gap-1 p-2\"\n >\n <button\n v-for=\"(month, index) in months\"\n :key=\"month\"\n type=\"button\"\n :class=\"cellButton({\n selected: index === viewMonth && get(monthsInRange)[index],\n selectedOutOfRange: index === viewMonth && !get(monthsInRange)[index],\n inRange: index !== viewMonth ? get(monthsInRange)[index] : undefined,\n })\"\n :disabled=\"!get(monthsInRange)[index]\"\n @click.stop=\"selectMonth(index)\"\n >\n {{ month.substring(0, 3) }}\n </button>\n </div>\n\n <div\n v-else-if=\"viewMode === 'years'\"\n class=\"grid grid-cols-3 gap-1 p-2\"\n >\n <button\n v-for=\"(year, index) in yearRange\"\n :key=\"year\"\n type=\"button\"\n :class=\"cellButton({\n selected: year === viewYear && get(yearsInRange)[index],\n selectedOutOfRange: year === viewYear && !get(yearsInRange)[index],\n inRange: year !== viewYear ? get(yearsInRange)[index] : undefined,\n })\"\n :disabled=\"!get(yearsInRange)[index]\"\n @click.stop=\"selectYear(year)\"\n >\n {{ year }}\n </button>\n </div>\n </div>\n </RuiMenu>\n</template>\n"],"mappings":"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"}
|