@treeui/vue 0.16.0 → 0.18.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.
Files changed (143) hide show
  1. package/dist/components/TAccordion.cjs.map +1 -1
  2. package/dist/components/TAccordion.js.map +1 -1
  3. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.cjs +1 -1
  4. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  5. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.js +3 -3
  6. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  7. package/dist/components/TAccordionItem.cjs.map +1 -1
  8. package/dist/components/TAccordionItem.js.map +1 -1
  9. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.cjs +1 -1
  10. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  11. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.js +1 -1
  12. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  13. package/dist/components/TAppShell.cjs.map +1 -1
  14. package/dist/components/TAppShell.js.map +1 -1
  15. package/dist/components/TAppShell.vue_vue_type_script_setup_true_lang.cjs +1 -1
  16. package/dist/components/TAppShell.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  17. package/dist/components/TAppShell.vue_vue_type_script_setup_true_lang.js +1 -1
  18. package/dist/components/TAppShell.vue_vue_type_script_setup_true_lang.js.map +1 -1
  19. package/dist/components/TDatePicker.cjs.map +1 -1
  20. package/dist/components/TDatePicker.js.map +1 -1
  21. package/dist/components/TDatePicker.vue_vue_type_script_setup_true_lang.cjs +1 -1
  22. package/dist/components/TDatePicker.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  23. package/dist/components/TDatePicker.vue_vue_type_script_setup_true_lang.js +1 -1
  24. package/dist/components/TDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  25. package/dist/components/TDateTimePicker.cjs.map +1 -1
  26. package/dist/components/TDateTimePicker.js.map +1 -1
  27. package/dist/components/TDateTimePicker.vue_vue_type_script_setup_true_lang.cjs +1 -1
  28. package/dist/components/TDateTimePicker.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  29. package/dist/components/TDateTimePicker.vue_vue_type_script_setup_true_lang.js +1 -1
  30. package/dist/components/TDateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  31. package/dist/components/TDrawer.cjs.map +1 -1
  32. package/dist/components/TDrawer.js.map +1 -1
  33. package/dist/components/TDrawer.vue_vue_type_script_setup_true_lang.cjs +1 -1
  34. package/dist/components/TDrawer.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  35. package/dist/components/TDrawer.vue_vue_type_script_setup_true_lang.js +1 -1
  36. package/dist/components/TDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  37. package/dist/components/TDropdown.cjs.map +1 -1
  38. package/dist/components/TDropdown.js.map +1 -1
  39. package/dist/components/TDropdown.vue.d.ts +4 -0
  40. package/dist/components/TDropdown.vue.d.ts.map +1 -1
  41. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.cjs +1 -1
  42. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  43. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.js +22 -18
  44. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.js.map +1 -1
  45. package/dist/components/TFlag.cjs +2 -0
  46. package/dist/components/TFlag.cjs.map +1 -0
  47. package/dist/components/TFlag.js +7 -0
  48. package/dist/components/TFlag.js.map +1 -0
  49. package/dist/components/TFlag.vue.d.ts +20 -0
  50. package/dist/components/TFlag.vue.d.ts.map +1 -0
  51. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.cjs +2 -0
  52. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.cjs.map +1 -0
  53. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.js +58 -0
  54. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.js.map +1 -0
  55. package/dist/components/TIcon.cjs.map +1 -1
  56. package/dist/components/TIcon.js.map +1 -1
  57. package/dist/components/TIcon.vue.d.ts +1 -1
  58. package/dist/components/TIcon.vue.d.ts.map +1 -1
  59. package/dist/components/TIcon.vue_vue_type_script_setup_true_lang.cjs +1 -1
  60. package/dist/components/TIcon.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  61. package/dist/components/TIcon.vue_vue_type_script_setup_true_lang.js +18 -17
  62. package/dist/components/TIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  63. package/dist/components/TLanguageSelect.cjs +2 -0
  64. package/dist/components/TLanguageSelect.cjs.map +1 -0
  65. package/dist/components/TLanguageSelect.js +7 -0
  66. package/dist/components/TLanguageSelect.js.map +1 -0
  67. package/dist/components/TLanguageSelect.vue.d.ts +103 -0
  68. package/dist/components/TLanguageSelect.vue.d.ts.map +1 -0
  69. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.cjs +2 -0
  70. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.cjs.map +1 -0
  71. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.js +299 -0
  72. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.js.map +1 -0
  73. package/dist/components/TModal.cjs.map +1 -1
  74. package/dist/components/TModal.js.map +1 -1
  75. package/dist/components/TModal.vue_vue_type_script_setup_true_lang.cjs +1 -1
  76. package/dist/components/TModal.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  77. package/dist/components/TModal.vue_vue_type_script_setup_true_lang.js +1 -1
  78. package/dist/components/TModal.vue_vue_type_script_setup_true_lang.js.map +1 -1
  79. package/dist/components/TMultiSelect.cjs.map +1 -1
  80. package/dist/components/TMultiSelect.js.map +1 -1
  81. package/dist/components/TMultiSelect.vue_vue_type_script_setup_true_lang.cjs +1 -1
  82. package/dist/components/TMultiSelect.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  83. package/dist/components/TMultiSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  84. package/dist/components/TMultiSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  85. package/dist/components/TNavMenu.cjs.map +1 -1
  86. package/dist/components/TNavMenu.js.map +1 -1
  87. package/dist/components/TNavMenu.vue.d.ts +7 -2
  88. package/dist/components/TNavMenu.vue.d.ts.map +1 -1
  89. package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.cjs +1 -1
  90. package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  91. package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.js +50 -49
  92. package/dist/components/TNavMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  93. package/dist/components/TPopover.cjs.map +1 -1
  94. package/dist/components/TPopover.js.map +1 -1
  95. package/dist/components/TPopover.vue.d.ts +4 -2
  96. package/dist/components/TPopover.vue.d.ts.map +1 -1
  97. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.cjs +1 -1
  98. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  99. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.js +10 -5
  100. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  101. package/dist/components/TPricingCard.vue.d.ts +1 -1
  102. package/dist/components/TPricingCard.vue.d.ts.map +1 -1
  103. package/dist/components/TSidebar.cjs.map +1 -1
  104. package/dist/components/TSidebar.js.map +1 -1
  105. package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.cjs +1 -1
  106. package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  107. package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.js +1 -1
  108. package/dist/components/TSidebar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  109. package/dist/components/TSteps.cjs.map +1 -1
  110. package/dist/components/TSteps.js.map +1 -1
  111. package/dist/components/TSteps.vue_vue_type_script_setup_true_lang.cjs +1 -1
  112. package/dist/components/TSteps.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  113. package/dist/components/TSteps.vue_vue_type_script_setup_true_lang.js +1 -1
  114. package/dist/components/TSteps.vue_vue_type_script_setup_true_lang.js.map +1 -1
  115. package/dist/components/TToast.cjs.map +1 -1
  116. package/dist/components/TToast.js.map +1 -1
  117. package/dist/components/TToast.vue.d.ts.map +1 -1
  118. package/dist/components/TToast.vue_vue_type_script_setup_true_lang.cjs +1 -1
  119. package/dist/components/TToast.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  120. package/dist/components/TToast.vue_vue_type_script_setup_true_lang.js +6 -6
  121. package/dist/components/TToast.vue_vue_type_script_setup_true_lang.js.map +1 -1
  122. package/dist/components/TTreeViewItem.cjs.map +1 -1
  123. package/dist/components/TTreeViewItem.js.map +1 -1
  124. package/dist/components/TTreeViewItem.vue_vue_type_script_setup_true_lang.cjs +1 -1
  125. package/dist/components/TTreeViewItem.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  126. package/dist/components/TTreeViewItem.vue_vue_type_script_setup_true_lang.js +1 -1
  127. package/dist/components/TTreeViewItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  128. package/dist/components/accordion.cjs.map +1 -1
  129. package/dist/components/accordion.d.ts +4 -4
  130. package/dist/components/accordion.d.ts.map +1 -1
  131. package/dist/components/accordion.js.map +1 -1
  132. package/dist/components/index.d.ts +5 -1
  133. package/dist/components/index.d.ts.map +1 -1
  134. package/dist/components/index.js +77 -0
  135. package/dist/index.cjs +1 -1
  136. package/dist/index.js +81 -78
  137. package/dist/plugin.cjs +1 -1
  138. package/dist/plugin.cjs.map +1 -1
  139. package/dist/plugin.d.ts.map +1 -1
  140. package/dist/plugin.js +146 -142
  141. package/dist/plugin.js.map +1 -1
  142. package/dist/style.css +1 -1
  143. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"TDatePicker.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../src/components/TDatePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n addDays,\n addMonths,\n createId,\n isActivationKey,\n isEscapeKey,\n isSameDay,\n parseDateValue,\n startOfDay,\n startOfMonth,\n startOfWeek,\n toDateValue,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n type ComponentPublicInstance,\n computed,\n nextTick,\n onBeforeUnmount,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSize } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\ninterface CalendarCell {\n date: Date;\n dayLabel: number;\n disabled: boolean;\n inMonth: boolean;\n isFocused: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n}\n\nconst CalendarIcon = getTreeIcon('calendar');\nconst ChevronLeftIcon = getTreeIcon('chevron-left');\nconst ChevronRightIcon = getTreeIcon('chevron-right');\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n open?: boolean;\n defaultOpen?: boolean;\n size?: TSize;\n disabled?: boolean;\n invalid?: boolean;\n placeholder?: string;\n locale?: string;\n min?: string;\n max?: string;\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n id?: string;\n }>(),\n {\n modelValue: '',\n open: undefined,\n defaultOpen: false,\n size: 'md',\n disabled: false,\n invalid: false,\n placeholder: 'Select date',\n locale: 'en-US',\n min: '',\n max: '',\n weekStartsOn: 0,\n id: undefined,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n select: [value: string];\n}>();\n\nconst attrs = useAttrs();\nconst today = startOfDay(new Date());\nconst selectedDate = computed(() => parseDateValue(props.modelValue));\nconst minDate = computed(() => parseDateValue(props.min));\nconst maxDate = computed(() => parseDateValue(props.max));\nconst calendarId = props.id ?? createId('t-date-picker');\nconst captionId = `${calendarId}-caption`;\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst dayRefs = new Map<string, HTMLButtonElement>();\nconst viewMonth = ref(startOfMonth(selectedDate.value ?? today));\nconst focusedDate = ref<Date>(selectedDate.value ?? today);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, name: _name, ...rest } = attrs;\n return rest;\n});\n\nconst hiddenInputName = computed(() =>\n typeof attrs.name === 'string' ? attrs.name : undefined,\n);\n\nconst rootClasses = computed(() => [\n 't-date-picker',\n `t-date-picker--${props.size}`,\n {\n 'is-disabled': props.disabled,\n 'is-invalid': props.invalid,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst triggerText = computed(() => {\n if (!selectedDate.value) {\n return props.placeholder;\n }\n\n return new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(selectedDate.value);\n});\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n month: 'long',\n year: 'numeric',\n }).format(viewMonth.value),\n);\n\nconst weekdayLabels = computed(() => {\n const formatter = new Intl.DateTimeFormat(props.locale, { weekday: 'short' });\n const weekBase = startOfWeek(new Date(2024, 0, 7), props.weekStartsOn);\n\n return Array.from({ length: 7 }, (_, index) =>\n formatter.format(addDays(weekBase, index)),\n );\n});\n\nconst isDateDisabled = (date: Date) => {\n if (props.disabled) {\n return true;\n }\n\n if (minDate.value && date < minDate.value) {\n return true;\n }\n\n if (maxDate.value && date > maxDate.value) {\n return true;\n }\n\n return false;\n};\n\nconst syncCalendarView = (date: Date) => {\n focusedDate.value = startOfDay(date);\n viewMonth.value = startOfMonth(date);\n};\n\nwatch(\n selectedDate,\n (value) => {\n if (value) {\n syncCalendarView(value);\n return;\n }\n\n if (!isOpen.value) {\n syncCalendarView(today);\n }\n },\n { immediate: true },\n);\n\nconst cells = computed(() => {\n const startDate = startOfWeek(startOfMonth(viewMonth.value), props.weekStartsOn);\n\n return Array.from({ length: 6 }, (_, weekIndex) =>\n Array.from({ length: 7 }, (_, dayIndex) => {\n const date = addDays(startDate, weekIndex * 7 + dayIndex);\n const key = toDateValue(date);\n\n return {\n date,\n dayLabel: date.getDate(),\n disabled: isDateDisabled(date),\n inMonth: date.getMonth() === viewMonth.value.getMonth(),\n isFocused: isSameDay(date, focusedDate.value),\n isSelected: isSameDay(date, selectedDate.value),\n isToday: isSameDay(date, today),\n key,\n } satisfies CalendarCell;\n }),\n );\n});\n\nconst canMovePrev = computed(() => {\n if (!minDate.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) > startOfMonth(minDate.value);\n});\n\nconst canMoveNext = computed(() => {\n if (!maxDate.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) < startOfMonth(maxDate.value);\n});\n\nconst focusCurrentDay = () => {\n const key = toDateValue(focusedDate.value);\n nextTick(() => {\n dayRefs.get(key)?.focus();\n });\n};\n\nconst setDayRef = (\n element: Element | ComponentPublicInstance | null,\n key: string,\n) => {\n if (element instanceof HTMLButtonElement) {\n dayRefs.set(key, element);\n return;\n }\n\n dayRefs.delete(key);\n};\n\nconst openCalendar = () => {\n if (props.disabled) {\n return;\n }\n\n syncCalendarView(selectedDate.value ?? focusedDate.value ?? today);\n setValue(true);\n focusCurrentDay();\n};\n\nconst closeCalendar = (restoreFocus = false) => {\n setValue(false);\n\n if (restoreFocus) {\n nextTick(() => {\n triggerRef.value?.focus();\n });\n }\n};\n\nconst toggleCalendar = () => {\n if (isOpen.value) {\n closeCalendar();\n return;\n }\n\n openCalendar();\n};\n\nconst moveCalendar = (direction: 1 | -1) => {\n const nextMonth = addMonths(viewMonth.value, direction);\n syncCalendarView(nextMonth);\n focusCurrentDay();\n};\n\nconst moveFocus = (nextDate: Date) => {\n if (isDateDisabled(nextDate)) {\n return;\n }\n\n syncCalendarView(nextDate);\n focusCurrentDay();\n};\n\nconst selectDate = (date: Date) => {\n if (isDateDisabled(date)) {\n return;\n }\n\n const normalizedDate = startOfDay(date);\n const value = toDateValue(normalizedDate);\n\n syncCalendarView(normalizedDate);\n emit('update:modelValue', value);\n emit('select', value);\n closeCalendar(true);\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) {\n return;\n }\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeCalendar();\n return;\n }\n\n if (\n isActivationKey(event) ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowUp'\n ) {\n event.preventDefault();\n openCalendar();\n }\n};\n\nconst onDayKeydown = (event: KeyboardEvent, date: Date) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeCalendar(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectDate(date);\n return;\n }\n\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault();\n moveFocus(addDays(date, -1));\n break;\n case 'ArrowRight':\n event.preventDefault();\n moveFocus(addDays(date, 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(addDays(date, -7));\n break;\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(addDays(date, 7));\n break;\n case 'Home':\n event.preventDefault();\n moveFocus(startOfWeek(date, props.weekStartsOn));\n break;\n case 'End':\n event.preventDefault();\n moveFocus(addDays(startOfWeek(date, props.weekStartsOn), 6));\n break;\n case 'PageUp':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? -12 : -1));\n break;\n case 'PageDown':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? 12 : 1));\n break;\n default:\n break;\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) {\n return;\n }\n\n const target = event.target;\n if (!(target instanceof Node)) {\n return;\n }\n\n if (rootRef.value?.contains(target)) {\n return;\n }\n\n closeCalendar();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n return;\n }\n\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <input\n v-if=\"hiddenInputName\"\n type=\"hidden\"\n :name=\"hiddenInputName\"\n :value=\"modelValue\"\n >\n <button\n ref=\"triggerRef\"\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-date-picker__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? calendarId : undefined\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :aria-invalid=\"invalid || undefined\"\n @click=\"toggleCalendar\"\n @keydown=\"onTriggerKeydown\"\n >\n <span\n v-if=\"$slots.prefix\"\n class=\"t-date-picker__slot t-date-picker__slot--prefix\"\n >\n <slot name=\"prefix\" />\n </span>\n <span\n class=\"t-date-picker__value\"\n :data-placeholder=\"selectedDate ? undefined : true\"\n >\n {{ triggerText }}\n </span>\n <span class=\"t-date-picker__slot t-date-picker__slot--suffix\">\n <slot name=\"suffix\">\n <CalendarIcon v-bind=\"treeIconDefaults\" />\n </slot>\n </span>\n </button>\n\n <transition name=\"t-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"calendarId\"\n class=\"t-date-picker__content\"\n role=\"dialog\"\n aria-modal=\"false\"\n :aria-labelledby=\"captionId\"\n >\n <div class=\"t-date-picker__header\">\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMovePrev\"\n aria-label=\"Previous month\"\n @click=\"moveCalendar(-1)\"\n >\n <ChevronLeftIcon v-bind=\"treeIconDefaults\" />\n </button>\n <p\n :id=\"captionId\"\n class=\"t-date-picker__month\"\n >\n {{ monthLabel }}\n </p>\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMoveNext\"\n aria-label=\"Next month\"\n @click=\"moveCalendar(1)\"\n >\n <ChevronRightIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n\n <div\n class=\"t-date-picker__weekdays\"\n aria-hidden=\"true\"\n >\n <span\n v-for=\"label in weekdayLabels\"\n :key=\"label\"\n class=\"t-date-picker__weekday\"\n >\n {{ label }}\n </span>\n </div>\n\n <div\n class=\"t-date-picker__grid\"\n role=\"grid\"\n :aria-labelledby=\"captionId\"\n >\n <div\n v-for=\"(week, weekIndex) in cells\"\n :key=\"weekIndex\"\n class=\"t-date-picker__row\"\n role=\"row\"\n >\n <div\n v-for=\"cell in week\"\n :key=\"cell.key\"\n class=\"t-date-picker__cell\"\n role=\"gridcell\"\n :aria-selected=\"cell.isSelected\"\n >\n <button\n :ref=\"(element) => setDayRef(element, cell.key)\"\n type=\"button\"\n class=\"t-date-picker__day\"\n :class=\"{\n 'is-muted': !cell.inMonth,\n 'is-selected': cell.isSelected,\n 'is-today': cell.isToday,\n }\"\n :data-date=\"cell.key\"\n :disabled=\"cell.disabled\"\n :tabindex=\"cell.isFocused ? 0 : -1\"\n :aria-current=\"cell.isToday ? 'date' : undefined\"\n :aria-label=\"cell.key\"\n @click=\"selectDate(cell.date)\"\n @keydown=\"onDayKeydown($event, cell.date)\"\n >\n {{ cell.dayLabel }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"TDatePicker.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../src/components/TDatePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n addDays,\n addMonths,\n createId,\n isActivationKey,\n isEscapeKey,\n isSameDay,\n parseDateValue,\n startOfDay,\n startOfMonth,\n startOfWeek,\n toDateValue,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n type ComponentPublicInstance,\n computed,\n nextTick,\n onBeforeUnmount,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSize } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\ninterface CalendarCell {\n date: Date;\n dayLabel: number;\n disabled: boolean;\n inMonth: boolean;\n isFocused: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n}\n\nconst CalendarIcon = computed(() => getTreeIcon('calendar'));\nconst ChevronLeftIcon = computed(() => getTreeIcon('chevron-left'));\nconst ChevronRightIcon = computed(() => getTreeIcon('chevron-right'));\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n open?: boolean;\n defaultOpen?: boolean;\n size?: TSize;\n disabled?: boolean;\n invalid?: boolean;\n placeholder?: string;\n locale?: string;\n min?: string;\n max?: string;\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n id?: string;\n }>(),\n {\n modelValue: '',\n open: undefined,\n defaultOpen: false,\n size: 'md',\n disabled: false,\n invalid: false,\n placeholder: 'Select date',\n locale: 'en-US',\n min: '',\n max: '',\n weekStartsOn: 0,\n id: undefined,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n select: [value: string];\n}>();\n\nconst attrs = useAttrs();\nconst today = startOfDay(new Date());\nconst selectedDate = computed(() => parseDateValue(props.modelValue));\nconst minDate = computed(() => parseDateValue(props.min));\nconst maxDate = computed(() => parseDateValue(props.max));\nconst calendarId = props.id ?? createId('t-date-picker');\nconst captionId = `${calendarId}-caption`;\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst dayRefs = new Map<string, HTMLButtonElement>();\nconst viewMonth = ref(startOfMonth(selectedDate.value ?? today));\nconst focusedDate = ref<Date>(selectedDate.value ?? today);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, name: _name, ...rest } = attrs;\n return rest;\n});\n\nconst hiddenInputName = computed(() =>\n typeof attrs.name === 'string' ? attrs.name : undefined,\n);\n\nconst rootClasses = computed(() => [\n 't-date-picker',\n `t-date-picker--${props.size}`,\n {\n 'is-disabled': props.disabled,\n 'is-invalid': props.invalid,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst triggerText = computed(() => {\n if (!selectedDate.value) {\n return props.placeholder;\n }\n\n return new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(selectedDate.value);\n});\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n month: 'long',\n year: 'numeric',\n }).format(viewMonth.value),\n);\n\nconst weekdayLabels = computed(() => {\n const formatter = new Intl.DateTimeFormat(props.locale, { weekday: 'short' });\n const weekBase = startOfWeek(new Date(2024, 0, 7), props.weekStartsOn);\n\n return Array.from({ length: 7 }, (_, index) =>\n formatter.format(addDays(weekBase, index)),\n );\n});\n\nconst isDateDisabled = (date: Date) => {\n if (props.disabled) {\n return true;\n }\n\n if (minDate.value && date < minDate.value) {\n return true;\n }\n\n if (maxDate.value && date > maxDate.value) {\n return true;\n }\n\n return false;\n};\n\nconst syncCalendarView = (date: Date) => {\n focusedDate.value = startOfDay(date);\n viewMonth.value = startOfMonth(date);\n};\n\nwatch(\n selectedDate,\n (value) => {\n if (value) {\n syncCalendarView(value);\n return;\n }\n\n if (!isOpen.value) {\n syncCalendarView(today);\n }\n },\n { immediate: true },\n);\n\nconst cells = computed(() => {\n const startDate = startOfWeek(startOfMonth(viewMonth.value), props.weekStartsOn);\n\n return Array.from({ length: 6 }, (_, weekIndex) =>\n Array.from({ length: 7 }, (_, dayIndex) => {\n const date = addDays(startDate, weekIndex * 7 + dayIndex);\n const key = toDateValue(date);\n\n return {\n date,\n dayLabel: date.getDate(),\n disabled: isDateDisabled(date),\n inMonth: date.getMonth() === viewMonth.value.getMonth(),\n isFocused: isSameDay(date, focusedDate.value),\n isSelected: isSameDay(date, selectedDate.value),\n isToday: isSameDay(date, today),\n key,\n } satisfies CalendarCell;\n }),\n );\n});\n\nconst canMovePrev = computed(() => {\n if (!minDate.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) > startOfMonth(minDate.value);\n});\n\nconst canMoveNext = computed(() => {\n if (!maxDate.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) < startOfMonth(maxDate.value);\n});\n\nconst focusCurrentDay = () => {\n const key = toDateValue(focusedDate.value);\n nextTick(() => {\n dayRefs.get(key)?.focus();\n });\n};\n\nconst setDayRef = (\n element: Element | ComponentPublicInstance | null,\n key: string,\n) => {\n if (element instanceof HTMLButtonElement) {\n dayRefs.set(key, element);\n return;\n }\n\n dayRefs.delete(key);\n};\n\nconst openCalendar = () => {\n if (props.disabled) {\n return;\n }\n\n syncCalendarView(selectedDate.value ?? focusedDate.value ?? today);\n setValue(true);\n focusCurrentDay();\n};\n\nconst closeCalendar = (restoreFocus = false) => {\n setValue(false);\n\n if (restoreFocus) {\n nextTick(() => {\n triggerRef.value?.focus();\n });\n }\n};\n\nconst toggleCalendar = () => {\n if (isOpen.value) {\n closeCalendar();\n return;\n }\n\n openCalendar();\n};\n\nconst moveCalendar = (direction: 1 | -1) => {\n const nextMonth = addMonths(viewMonth.value, direction);\n syncCalendarView(nextMonth);\n focusCurrentDay();\n};\n\nconst moveFocus = (nextDate: Date) => {\n if (isDateDisabled(nextDate)) {\n return;\n }\n\n syncCalendarView(nextDate);\n focusCurrentDay();\n};\n\nconst selectDate = (date: Date) => {\n if (isDateDisabled(date)) {\n return;\n }\n\n const normalizedDate = startOfDay(date);\n const value = toDateValue(normalizedDate);\n\n syncCalendarView(normalizedDate);\n emit('update:modelValue', value);\n emit('select', value);\n closeCalendar(true);\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) {\n return;\n }\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeCalendar();\n return;\n }\n\n if (\n isActivationKey(event) ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowUp'\n ) {\n event.preventDefault();\n openCalendar();\n }\n};\n\nconst onDayKeydown = (event: KeyboardEvent, date: Date) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeCalendar(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectDate(date);\n return;\n }\n\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault();\n moveFocus(addDays(date, -1));\n break;\n case 'ArrowRight':\n event.preventDefault();\n moveFocus(addDays(date, 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(addDays(date, -7));\n break;\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(addDays(date, 7));\n break;\n case 'Home':\n event.preventDefault();\n moveFocus(startOfWeek(date, props.weekStartsOn));\n break;\n case 'End':\n event.preventDefault();\n moveFocus(addDays(startOfWeek(date, props.weekStartsOn), 6));\n break;\n case 'PageUp':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? -12 : -1));\n break;\n case 'PageDown':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? 12 : 1));\n break;\n default:\n break;\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) {\n return;\n }\n\n const target = event.target;\n if (!(target instanceof Node)) {\n return;\n }\n\n if (rootRef.value?.contains(target)) {\n return;\n }\n\n closeCalendar();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n return;\n }\n\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <input\n v-if=\"hiddenInputName\"\n type=\"hidden\"\n :name=\"hiddenInputName\"\n :value=\"modelValue\"\n >\n <button\n ref=\"triggerRef\"\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-date-picker__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? calendarId : undefined\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :aria-invalid=\"invalid || undefined\"\n @click=\"toggleCalendar\"\n @keydown=\"onTriggerKeydown\"\n >\n <span\n v-if=\"$slots.prefix\"\n class=\"t-date-picker__slot t-date-picker__slot--prefix\"\n >\n <slot name=\"prefix\" />\n </span>\n <span\n class=\"t-date-picker__value\"\n :data-placeholder=\"selectedDate ? undefined : true\"\n >\n {{ triggerText }}\n </span>\n <span class=\"t-date-picker__slot t-date-picker__slot--suffix\">\n <slot name=\"suffix\">\n <CalendarIcon v-bind=\"treeIconDefaults\" />\n </slot>\n </span>\n </button>\n\n <transition name=\"t-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"calendarId\"\n class=\"t-date-picker__content\"\n role=\"dialog\"\n aria-modal=\"false\"\n :aria-labelledby=\"captionId\"\n >\n <div class=\"t-date-picker__header\">\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMovePrev\"\n aria-label=\"Previous month\"\n @click=\"moveCalendar(-1)\"\n >\n <ChevronLeftIcon v-bind=\"treeIconDefaults\" />\n </button>\n <p\n :id=\"captionId\"\n class=\"t-date-picker__month\"\n >\n {{ monthLabel }}\n </p>\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMoveNext\"\n aria-label=\"Next month\"\n @click=\"moveCalendar(1)\"\n >\n <ChevronRightIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n\n <div\n class=\"t-date-picker__weekdays\"\n aria-hidden=\"true\"\n >\n <span\n v-for=\"label in weekdayLabels\"\n :key=\"label\"\n class=\"t-date-picker__weekday\"\n >\n {{ label }}\n </span>\n </div>\n\n <div\n class=\"t-date-picker__grid\"\n role=\"grid\"\n :aria-labelledby=\"captionId\"\n >\n <div\n v-for=\"(week, weekIndex) in cells\"\n :key=\"weekIndex\"\n class=\"t-date-picker__row\"\n role=\"row\"\n >\n <div\n v-for=\"cell in week\"\n :key=\"cell.key\"\n class=\"t-date-picker__cell\"\n role=\"gridcell\"\n :aria-selected=\"cell.isSelected\"\n >\n <button\n :ref=\"(element) => setDayRef(element, cell.key)\"\n type=\"button\"\n class=\"t-date-picker__day\"\n :class=\"{\n 'is-muted': !cell.inMonth,\n 'is-selected': cell.isSelected,\n 'is-today': cell.isToday,\n }\"\n :data-date=\"cell.key\"\n :disabled=\"cell.disabled\"\n :tabindex=\"cell.isFocused ? 0 : -1\"\n :aria-current=\"cell.isToday ? 'date' : undefined\"\n :aria-label=\"cell.key\"\n @click=\"selectDate(cell.date)\"\n @keydown=\"onDayKeydown($event, cell.date)\"\n >\n {{ cell.dayLabel }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2CA,IAAM,KAAe,QAAe,EAAY,UAAU,CAAC,GACrD,KAAkB,QAAe,EAAY,cAAc,CAAC,GAC5D,KAAmB,QAAe,EAAY,eAAe,CAAC,GAE9D,IAAQ,GA+BR,IAAO,GAOP,IAAQ,GAAS,GACjB,IAAQ,kBAAW,IAAI,KAAK,CAAC,GAC7B,IAAe,QAAe,EAAe,EAAM,UAAU,CAAC,GAC9D,IAAU,QAAe,EAAe,EAAM,GAAG,CAAC,GAClD,IAAU,QAAe,EAAe,EAAM,GAAG,CAAC,GAClD,IAAa,EAAM,MAAM,GAAS,eAAe,GACjD,IAAY,GAAG,EAAW,WAC1B,IAAU,EAAwB,IAAI,GACtC,IAAa,EAA8B,IAAI,GAC/C,oBAAU,IAAI,IAA+B,GAC7C,IAAY,EAAI,EAAa,EAAa,SAAS,CAAK,CAAC,GACzD,IAAc,EAAU,EAAa,SAAS,CAAK,GAEnD,KAAe,QAAe;GAClC,IAAM,EAAE,OAAO,GAAQ,OAAO,GAAQ,MAAM,GAAO,GAAG,MAAS;GAC/D,OAAO;EACT,CAAC,GAEK,IAAkB,QACtB,OAAO,EAAM,QAAS,WAAW,EAAM,OAAO,KAAA,CAChD,GAEM,KAAc,QAAe;GACjC;GACA,kBAAkB,EAAM;GACxB;IACE,eAAe,EAAM;IACrB,cAAc,EAAM;IACpB,WAAW,EAAO;GACpB;GACA,EAAM;EACR,CAAC,GAEK,KAAY,QAAe,EAAM,KAAK,GAEtC,EAAE,OAAO,GAAQ,iBAAa,EAClC,GAAM,GAAO,MAAM,GACnB,EAAM,cACL,MAAU;GAET,AADA,EAAK,eAAe,CAAK,GACzB,EAAK,eAAe,CAAK;EAC3B,CACF,GAEM,KAAc,QACb,EAAa,QAIX,IAAI,KAAK,eAAe,EAAM,QAAQ;GAC3C,KAAK;GACL,OAAO;GACP,MAAM;EACR,CAAC,CAAC,CAAC,OAAO,EAAa,KAAK,IAPnB,EAAM,WAQhB,GAEK,KAAa,QACjB,IAAI,KAAK,eAAe,EAAM,QAAQ;GACpC,OAAO;GACP,MAAM;EACR,CAAC,CAAC,CAAC,OAAO,EAAU,KAAK,CAC3B,GAEM,KAAgB,QAAe;GACnC,IAAM,IAAY,IAAI,KAAK,eAAe,EAAM,QAAQ,EAAE,SAAS,QAAQ,CAAC,GACtE,IAAW,EAAY,IAAI,KAAK,MAAM,GAAG,CAAC,GAAG,EAAM,YAAY;GAErE,OAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MACnC,EAAU,OAAO,EAAQ,GAAU,CAAK,CAAC,CAC3C;EACF,CAAC,GAEK,KAAkB,MAStB,GARI,EAAM,YAIN,EAAQ,SAAS,IAAO,EAAQ,SAIhC,EAAQ,SAAS,IAAO,EAAQ,QAOhC,KAAoB,MAAe;GAEvC,AADA,EAAY,QAAQ,EAAW,CAAI,GACnC,EAAU,QAAQ,EAAa,CAAI;EACrC;EAEA,EACE,IACC,MAAU;GACT,IAAI,GAAO;IACT,EAAiB,CAAK;IACtB;GACF;GAEA,AAAK,EAAO,SACV,EAAiB,CAAK;EAE1B,GACA,EAAE,WAAW,GAAK,CACpB;EAEA,IAAM,KAAQ,QAAe;GAC3B,IAAM,IAAY,EAAY,EAAa,EAAU,KAAK,GAAG,EAAM,YAAY;GAE/E,OAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MACnC,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,MAAa;IACzC,IAAM,IAAO,EAAQ,GAAW,IAAY,IAAI,CAAQ,GAClD,IAAM,EAAY,CAAI;IAE5B,OAAO;KACL;KACA,UAAU,EAAK,QAAQ;KACvB,UAAU,EAAe,CAAI;KAC7B,SAAS,EAAK,SAAS,MAAM,EAAU,MAAM,SAAS;KACtD,WAAW,EAAU,GAAM,EAAY,KAAK;KAC5C,YAAY,EAAU,GAAM,EAAa,KAAK;KAC9C,SAAS,EAAU,GAAM,CAAK;KAC9B;IACF;GACF,CAAC,CACH;EACF,CAAC,GAEK,KAAc,QAClB,CAAK,EAAQ,SAIN,EAAa,EAAU,KAAK,IAAI,EAAa,EAAQ,KAAK,CAClE,GAEK,KAAc,QAClB,CAAK,EAAQ,SAIN,EAAa,EAAU,KAAK,IAAI,EAAa,EAAQ,KAAK,CAClE,GAEK,UAAwB;GAC5B,IAAM,IAAM,EAAY,EAAY,KAAK;GACzC,SAAe;IACb,EAAQ,IAAI,CAAG,CAAC,EAAE,MAAM;GAC1B,CAAC;EACH,GAEM,MACJ,GACA,MACG;GACH,IAAI,aAAmB,mBAAmB;IACxC,EAAQ,IAAI,GAAK,CAAO;IACxB;GACF;GAEA,EAAQ,OAAO,CAAG;EACpB,GAEM,WAAqB;GACrB,EAAM,aAIV,EAAiB,EAAa,SAAS,EAAY,SAAS,CAAK,GACjE,GAAS,EAAI,GACb,EAAgB;EAClB,GAEM,KAAiB,IAAe,OAAU;GAG9C,AAFA,GAAS,EAAK,GAEV,KACF,SAAe;IACb,EAAW,OAAO,MAAM;GAC1B,CAAC;EAEL,GAEM,WAAuB;GAC3B,IAAI,EAAO,OAAO;IAChB,EAAc;IACd;GACF;GAEA,GAAa;EACf,GAEM,MAAgB,MAAsB;GAC1C,IAAM,IAAY,EAAU,EAAU,OAAO,CAAS;GAEtD,AADA,EAAiB,CAAS,GAC1B,EAAgB;EAClB,GAEM,KAAa,MAAmB;GAChC,EAAe,CAAQ,MAI3B,EAAiB,CAAQ,GACzB,EAAgB;EAClB,GAEM,MAAc,MAAe;GACjC,IAAI,EAAe,CAAI,GACrB;GAGF,IAAM,IAAiB,EAAW,CAAI,GAChC,IAAQ,EAAY,CAAc;GAKxC,AAHA,EAAiB,CAAc,GAC/B,EAAK,qBAAqB,CAAK,GAC/B,EAAK,UAAU,CAAK,GACpB,EAAc,EAAI;EACpB,GAEM,MAAoB,MAAyB;GAC7C,OAAM,UAIV;QAAI,EAAY,CAAK,KAAK,EAAO,OAAO;KAEtC,AADA,EAAM,eAAe,GACrB,EAAc;KACd;IACF;IAEA,CACE,EAAgB,CAAK,KACrB,EAAM,QAAQ,eACd,EAAM,QAAQ,eAEd,EAAM,eAAe,GACrB,GAAa;GARf;EAUF,GAEM,MAAgB,GAAsB,MAAe;GACzD,IAAI,EAAY,CAAK,GAAG;IAEtB,AADA,EAAM,eAAe,GACrB,EAAc,EAAI;IAClB;GACF;GAEA,IAAI,EAAgB,CAAK,GAAG;IAE1B,AADA,EAAM,eAAe,GACrB,GAAW,CAAI;IACf;GACF;GAEA,QAAQ,EAAM,KAAd;IACE,KAAK;KAEH,AADA,EAAM,eAAe,GACrB,EAAU,EAAQ,GAAM,EAAE,CAAC;KAC3B;IACF,KAAK;KAEH,AADA,EAAM,eAAe,GACrB,EAAU,EAAQ,GAAM,CAAC,CAAC;KAC1B;IACF,KAAK;KAEH,AADA,EAAM,eAAe,GACrB,EAAU,EAAQ,GAAM,EAAE,CAAC;KAC3B;IACF,KAAK;KAEH,AADA,EAAM,eAAe,GACrB,EAAU,EAAQ,GAAM,CAAC,CAAC;KAC1B;IACF,KAAK;KAEH,AADA,EAAM,eAAe,GACrB,EAAU,EAAY,GAAM,EAAM,YAAY,CAAC;KAC/C;IACF,KAAK;KAEH,AADA,EAAM,eAAe,GACrB,EAAU,EAAQ,EAAY,GAAM,EAAM,YAAY,GAAG,CAAC,CAAC;KAC3D;IACF,KAAK;KAEH,AADA,EAAM,eAAe,GACrB,EAAU,EAAU,GAAM,EAAM,WAAW,MAAM,EAAE,CAAC;KACpD;IACF,KAAK;KAEH,AADA,EAAM,eAAe,GACrB,EAAU,EAAU,GAAM,EAAM,WAAW,KAAK,CAAC,CAAC;KAClD;IACF,SACE;GACJ;EACF,GAEM,KAAyB,MAAwB;GACrD,IAAI,CAAC,EAAO,OACV;GAGF,IAAM,IAAS,EAAM;GACf,aAAkB,SAIpB,EAAQ,OAAO,SAAS,CAAM,KAIlC,EAAc;EAChB;SAEA,EAAM,IAAS,MAAU;GACvB,IAAI,GAAO;IACT,SAAS,iBAAiB,eAAe,CAAqB;IAC9D;GACF;GAEA,SAAS,oBAAoB,eAAe,CAAqB;EACnE,CAAC,GAED,SAAsB;GACpB,SAAS,oBAAoB,eAAe,CAAqB;EACnE,CAAC,mBAIC,EAuIM,OAAA;YAtIA;GAAJ,KAAI;GACH,OAAK,EAAE,GAAA,KAAW;GAClB,OAAK,GAAE,GAAA,KAAS;GAChB,cAAY,EAAA,CAAA,IAAM,SAAA;;GAGX,EAAA,SAAA,EAAA,GADR,EAKC,SAAA;;IAHC,MAAK;IACJ,MAAM,EAAA;IACN,OAAO,EAAA;;GAEV,EA8BS,UA9BT,GA8BS;aA7BH;IAAJ,KAAI;MACI,GAAA,OAAY;IACpB,MAAK;IACL,OAAM;IACL,UAAU,EAAA;IACV,iBAAe,EAAA,CAAA,IAAS,EAAA,CAAA,IAAa,KAAA;IACtC,iBAAc;IACb,iBAAe,EAAA,CAAA;IACf,gBAAc,EAAA,WAAW,KAAA;IACzB,SAAO;IACP,WAAS;;IAGFA,EAAAA,OAAO,UAAA,EAAA,GADf,EAKO,QALP,IAKO,CADL,EAAsB,EAAA,QAAA,QAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA;IAExB,EAKO,QAAA;KAJL,OAAM;KACL,oBAAkB,GAAA,SAAe,KAAA;SAE/B,GAAA,KAAW,GAAA,GAAA,EAAA;IAEhB,EAIO,QAJP,IAIO,CAHL,EAEO,EAAA,QAAA,UAAA,CAAA,SAAA,CADL,EAA0C,EAAA,EAAA,GAAA,EAAA,EAApB,EAAA,CAAA,CAAgB,CAAA,GAAA,MAAA,EAAA,CAAA,CAAA,CAAA,CAAA;;GAK5C,EA0Fa,GAAA,EA1FD,MAAK,SAAQ,GAAA;sBAyFjB,CAvFE,EAAA,CAAA,KAAM,CAAK,EAAA,YAAA,EAAA,GADnB,EAwFM,OAAA;;KAtFH,IAAI,EAAA,CAAA;KACL,OAAM;KACN,MAAK;KACL,cAAW;KACV,mBAAiB;;KAElB,EAyBM,OAzBN,IAyBM;MAxBJ,EAQS,UAAA;OAPP,MAAK;OACL,OAAM;OACL,UAAQ,CAAG,GAAA;OACZ,cAAW;OACV,SAAK,AAAA,EAAA,QAAA,MAAE,GAAY,EAAA;UAEpB,EAA6C,EAAA,EAAA,GAAA,EAAA,EAApB,EAAA,CAAA,CAAgB,CAAA,GAAA,MAAA,EAAA,CAAA,GAAA,GAAA,EAAA;MAE3C,EAKI,KAAA;OAJD,IAAI;OACL,OAAM;WAEH,GAAA,KAAU,GAAA,CAAA;MAEf,EAQS,UAAA;OAPP,MAAK;OACL,OAAM;OACL,UAAQ,CAAG,GAAA;OACZ,cAAW;OACV,SAAK,AAAA,EAAA,QAAA,MAAE,GAAY,CAAA;UAEpB,EAA8C,EAAA,EAAA,GAAA,EAAA,EAApB,EAAA,CAAA,CAAgB,CAAA,GAAA,MAAA,EAAA,CAAA,GAAA,GAAA,EAAA;;KAI9C,EAWM,OAXN,IAWM,EAAA,EAAA,EAAA,GAPJ,EAMO,GAAA,MAAA,EALW,GAAA,QAAT,YADT,EAMO,QAAA;MAJJ,KAAK;MACN,OAAM;UAEH,CAAK,GAAA,CAAA;KAIZ,EAuCM,OAAA;MAtCJ,OAAM;MACN,MAAK;MACJ,mBAAiB;iBAElB,EAiCM,GAAA,MAAA,EAhCwB,GAAA,QAApB,GAAM,YADhB,EAiCM,OAAA;MA/BH,KAAK;MACN,OAAM;MACN,MAAK;iBAEL,EA0BM,GAAA,MAAA,EAzBW,IAAR,YADT,EA0BM,OAAA;MAxBH,KAAK,EAAK;MACX,OAAM;MACN,MAAK;MACJ,iBAAe,EAAK;SAErB,EAkBS,UAAA;;MAjBN,MAAM,MAAY,GAAU,GAAS,EAAK,GAAG;MAC9C,MAAK;MACL,OAAK,EAAA,CAAC,sBAAoB;oBACe,EAAK;sBAA0C,EAAK;mBAA0C,EAAK;;MAK3I,aAAW,EAAK;MAChB,UAAU,EAAK;MACf,UAAU,EAAK,YAAS,IAAA;MACxB,gBAAc,EAAK,UAAO,SAAY,KAAA;MACtC,cAAY,EAAK;MACjB,UAAK,MAAE,GAAW,EAAK,IAAI;MAC3B,YAAO,MAAE,GAAa,GAAQ,EAAK,IAAI;UAErC,EAAK,QAAQ,GAAA,IAAA,EAAA,CAAA,GAAA,GAAA,EAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"TDateTimePicker.cjs","names":[],"sources":["../../src/components/TDateTimePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n addDays,\n addMonths,\n createId,\n isActivationKey,\n isEscapeKey,\n isSameDay,\n startOfDay,\n startOfMonth,\n startOfWeek,\n toDateValue,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n type ComponentPublicInstance,\n computed,\n nextTick,\n onBeforeUnmount,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSize } from '../types/contracts';\nimport TSpinner from './TSpinner.vue';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\ninterface CalendarCell {\n date: Date;\n dayLabel: number;\n disabled: boolean;\n inMonth: boolean;\n isFocused: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n}\n\ninterface TimeOption {\n disabled: boolean;\n label: string;\n value: number;\n}\n\nconst CalendarIcon = getTreeIcon('calendar');\nconst ChevronLeftIcon = getTreeIcon('chevron-left');\nconst ChevronRightIcon = getTreeIcon('chevron-right');\n\nconst dateOnlyPattern = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst dateTimePattern = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2}))?$/;\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n open?: boolean;\n defaultOpen?: boolean;\n size?: TSize;\n disabled?: boolean;\n loading?: boolean;\n invalid?: boolean;\n placeholder?: string;\n locale?: string;\n min?: string;\n max?: string;\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n id?: string;\n step?: number;\n }>(),\n {\n modelValue: '',\n open: undefined,\n defaultOpen: false,\n size: 'md',\n disabled: false,\n loading: false,\n invalid: false,\n placeholder: 'Select date and time',\n locale: 'en-US',\n min: '',\n max: '',\n weekStartsOn: 0,\n id: undefined,\n step: 60,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n change: [value: string];\n select: [value: string];\n}>();\n\nconst attrs = useAttrs();\n\nconst cloneDate = (value: Date) => new Date(value.getTime());\n\nconst formatPart = (value: number) => `${value}`.padStart(2, '0');\n\nconst endOfDay = (date: Date) =>\n new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);\n\nconst parseBoundaryValue = (\n value: string | null | undefined,\n boundary: 'exact' | 'start' | 'end' = 'exact',\n) => {\n if (!value) {\n return null;\n }\n\n const dateOnlyMatch = value.match(dateOnlyPattern);\n if (dateOnlyMatch) {\n const [, yearToken, monthToken, dayToken] = dateOnlyMatch;\n const year = Number.parseInt(yearToken, 10);\n const month = Number.parseInt(monthToken, 10);\n const day = Number.parseInt(dayToken, 10);\n const hour = boundary === 'end' ? 23 : 0;\n const minute = boundary === 'end' ? 59 : 0;\n const second = boundary === 'end' ? 59 : 0;\n const millisecond = boundary === 'end' ? 999 : 0;\n const date = new Date(year, month - 1, day, hour, minute, second, millisecond);\n\n if (\n Number.isNaN(date.getTime()) ||\n date.getFullYear() !== year ||\n date.getMonth() !== month - 1 ||\n date.getDate() !== day\n ) {\n return null;\n }\n\n return date;\n }\n\n const dateTimeMatch = value.match(dateTimePattern);\n if (!dateTimeMatch) {\n return null;\n }\n\n const [, yearToken, monthToken, dayToken, hourToken, minuteToken, secondToken] =\n dateTimeMatch;\n const year = Number.parseInt(yearToken, 10);\n const month = Number.parseInt(monthToken, 10);\n const day = Number.parseInt(dayToken, 10);\n const hour = Number.parseInt(hourToken, 10);\n const minute = Number.parseInt(minuteToken, 10);\n const second = secondToken ? Number.parseInt(secondToken, 10) : 0;\n const date = new Date(year, month - 1, day, hour, minute, second, 0);\n\n if (\n Number.isNaN(date.getTime()) ||\n date.getFullYear() !== year ||\n date.getMonth() !== month - 1 ||\n date.getDate() !== day ||\n date.getHours() !== hour ||\n date.getMinutes() !== minute ||\n date.getSeconds() !== second\n ) {\n return null;\n }\n\n return date;\n};\n\nconst toDateTimeValue = (date: Date) =>\n `${toDateValue(date)}T${formatPart(date.getHours())}:${formatPart(date.getMinutes())}`;\n\nconst setDatePart = (source: Date, targetDate: Date) =>\n new Date(\n targetDate.getFullYear(),\n targetDate.getMonth(),\n targetDate.getDate(),\n source.getHours(),\n source.getMinutes(),\n 0,\n 0,\n );\n\nconst setTimePart = (source: Date, hour: number, minute: number) =>\n new Date(\n source.getFullYear(),\n source.getMonth(),\n source.getDate(),\n hour,\n minute,\n 0,\n 0,\n );\n\nconst resolveMinuteStep = (step: number) => {\n if (!Number.isFinite(step) || step <= 0 || step % 60 !== 0) {\n return 1;\n }\n\n const minuteStep = step / 60;\n return minuteStep >= 60 ? 1 : minuteStep;\n};\n\nconst today = startOfDay(new Date());\nconst selectedDateTime = computed(() => parseBoundaryValue(props.modelValue, 'exact'));\nconst minDateTime = computed(() => parseBoundaryValue(props.min, 'start'));\nconst maxDateTime = computed(() => parseBoundaryValue(props.max, 'end'));\nconst pickerId = props.id ?? createId('t-date-time-picker');\nconst captionId = `${pickerId}-caption`;\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst dayRefs = new Map<string, HTMLButtonElement>();\n\nconst clampToRange = (value: Date) => {\n let nextValue = cloneDate(value);\n\n if (minDateTime.value && nextValue < minDateTime.value) {\n nextValue = cloneDate(minDateTime.value);\n }\n\n if (maxDateTime.value && nextValue > maxDateTime.value) {\n nextValue = cloneDate(maxDateTime.value);\n }\n\n return nextValue;\n};\n\nconst createDefaultDraftDateTime = () => {\n const reference = new Date();\n const stepMilliseconds = Math.max(props.step, 60) * 1000;\n const rounded = new Date(Math.ceil(reference.getTime() / stepMilliseconds) * stepMilliseconds);\n\n rounded.setSeconds(0, 0);\n return clampToRange(rounded);\n};\n\nconst initialDateTime = selectedDateTime.value ?? createDefaultDraftDateTime();\nconst draftDateTime = ref(initialDateTime);\nconst focusedDate = ref(startOfDay(initialDateTime));\nconst viewMonth = ref(startOfMonth(initialDateTime));\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, name: _name, ...rest } = attrs;\n return rest;\n});\n\nconst hiddenInputName = computed(() =>\n typeof attrs.name === 'string' ? attrs.name : undefined,\n);\n\nconst rootClasses = computed(() => [\n 't-date-picker',\n 't-date-time-picker',\n `t-date-picker--${props.size}`,\n {\n 'is-disabled': props.disabled,\n 'is-invalid': props.invalid,\n 'is-loading': props.loading,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst triggerText = computed(() => {\n if (!selectedDateTime.value) {\n return props.placeholder;\n }\n\n return new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n month: 'long',\n year: 'numeric',\n }).format(selectedDateTime.value);\n});\n\nconst draftPreview = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n month: 'long',\n year: 'numeric',\n }).format(draftDateTime.value),\n);\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n month: 'long',\n year: 'numeric',\n }).format(viewMonth.value),\n);\n\nconst weekdayLabels = computed(() => {\n const formatter = new Intl.DateTimeFormat(props.locale, { weekday: 'short' });\n const weekBase = startOfWeek(new Date(2024, 0, 7), props.weekStartsOn);\n\n return Array.from({ length: 7 }, (_, index) =>\n formatter.format(addDays(weekBase, index)),\n );\n});\n\nconst minuteStep = computed(() => resolveMinuteStep(props.step));\n\nconst minuteValues = computed(() => {\n const values = new Set<number>();\n\n for (let minute = 0; minute < 60; minute += minuteStep.value) {\n values.add(minute);\n }\n\n values.add(draftDateTime.value.getMinutes());\n\n return [...values].sort((left, right) => left - right);\n});\n\nconst isDateDisabled = (date: Date) => {\n if (props.disabled) {\n return true;\n }\n\n const dayStart = startOfDay(date);\n const dayEnd = endOfDay(date);\n\n if (minDateTime.value && dayEnd < minDateTime.value) {\n return true;\n }\n\n if (maxDateTime.value && dayStart > maxDateTime.value) {\n return true;\n }\n\n return false;\n};\n\nconst isDateTimeDisabled = (value: Date) => {\n if (props.disabled) {\n return true;\n }\n\n if (minDateTime.value && value < minDateTime.value) {\n return true;\n }\n\n if (maxDateTime.value && value > maxDateTime.value) {\n return true;\n }\n\n return false;\n};\n\nconst syncCalendarView = (date: Date) => {\n focusedDate.value = startOfDay(date);\n viewMonth.value = startOfMonth(date);\n};\n\nconst syncDraftState = (dateTime: Date) => {\n const nextValue = clampToRange(dateTime);\n draftDateTime.value = nextValue;\n syncCalendarView(nextValue);\n};\n\nwatch(\n selectedDateTime,\n (value) => {\n if (isOpen.value) {\n return;\n }\n\n syncDraftState(value ?? createDefaultDraftDateTime());\n },\n { immediate: true },\n);\n\nconst draftDate = computed(() => startOfDay(draftDateTime.value));\n\nconst cells = computed(() => {\n const startDate = startOfWeek(startOfMonth(viewMonth.value), props.weekStartsOn);\n\n return Array.from({ length: 6 }, (_, weekIndex) =>\n Array.from({ length: 7 }, (_, dayIndex) => {\n const date = addDays(startDate, weekIndex * 7 + dayIndex);\n const key = toDateValue(date);\n\n return {\n date,\n dayLabel: date.getDate(),\n disabled: isDateDisabled(date),\n inMonth: date.getMonth() === viewMonth.value.getMonth(),\n isFocused: isSameDay(date, focusedDate.value),\n isSelected: isSameDay(date, draftDate.value),\n isToday: isSameDay(date, today),\n key,\n } satisfies CalendarCell;\n }),\n );\n});\n\nconst canMovePrev = computed(() => {\n if (!minDateTime.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) > startOfMonth(minDateTime.value);\n});\n\nconst canMoveNext = computed(() => {\n if (!maxDateTime.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) < startOfMonth(maxDateTime.value);\n});\n\nconst isTimeOptionEnabled = (hour: number, minute: number) => {\n const candidate = setTimePart(draftDateTime.value, hour, minute);\n return !isDateTimeDisabled(candidate);\n};\n\nconst hourOptions = computed<TimeOption[]>(() =>\n Array.from({ length: 24 }, (_, hour) => ({\n disabled: !minuteValues.value.some((minute) => isTimeOptionEnabled(hour, minute)),\n label: formatPart(hour),\n value: hour,\n })),\n);\n\nconst minuteOptions = computed<TimeOption[]>(() =>\n minuteValues.value.map((minute) => ({\n disabled: !isTimeOptionEnabled(draftDateTime.value.getHours(), minute),\n label: formatPart(minute),\n value: minute,\n })),\n);\n\nconst focusCurrentDay = () => {\n const key = toDateValue(focusedDate.value);\n nextTick(() => {\n dayRefs.get(key)?.focus();\n });\n};\n\nconst setDayRef = (\n element: Element | ComponentPublicInstance | null,\n key: string,\n) => {\n if (element instanceof HTMLButtonElement) {\n dayRefs.set(key, element);\n return;\n }\n\n dayRefs.delete(key);\n};\n\nconst openCalendar = () => {\n if (props.disabled) {\n return;\n }\n\n syncDraftState(selectedDateTime.value ?? createDefaultDraftDateTime());\n setValue(true);\n focusCurrentDay();\n};\n\nconst closeCalendar = (restoreFocus = false) => {\n setValue(false);\n\n if (restoreFocus) {\n nextTick(() => {\n triggerRef.value?.focus();\n });\n }\n};\n\nconst toggleCalendar = () => {\n if (isOpen.value) {\n closeCalendar();\n return;\n }\n\n openCalendar();\n};\n\nconst moveCalendar = (direction: 1 | -1) => {\n const nextMonth = addMonths(viewMonth.value, direction);\n syncCalendarView(nextMonth);\n focusCurrentDay();\n};\n\nconst moveFocus = (nextDate: Date) => {\n if (isDateDisabled(nextDate)) {\n return;\n }\n\n syncCalendarView(nextDate);\n focusCurrentDay();\n};\n\nconst selectDate = (date: Date) => {\n if (isDateDisabled(date)) {\n return;\n }\n\n const candidate = clampToRange(setDatePart(draftDateTime.value, date));\n draftDateTime.value = candidate;\n syncCalendarView(candidate);\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) {\n return;\n }\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeCalendar();\n return;\n }\n\n if (\n isActivationKey(event) ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowUp'\n ) {\n event.preventDefault();\n openCalendar();\n }\n};\n\nconst onDayKeydown = (event: KeyboardEvent, date: Date) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeCalendar(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectDate(date);\n return;\n }\n\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault();\n moveFocus(addDays(date, -1));\n break;\n case 'ArrowRight':\n event.preventDefault();\n moveFocus(addDays(date, 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(addDays(date, -7));\n break;\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(addDays(date, 7));\n break;\n case 'Home':\n event.preventDefault();\n moveFocus(startOfWeek(date, props.weekStartsOn));\n break;\n case 'End':\n event.preventDefault();\n moveFocus(addDays(startOfWeek(date, props.weekStartsOn), 6));\n break;\n case 'PageUp':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? -12 : -1));\n break;\n case 'PageDown':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? 12 : 1));\n break;\n default:\n break;\n }\n};\n\nconst updateHour = (event: Event) => {\n const nextHour = Number.parseInt((event.target as HTMLSelectElement).value, 10);\n const currentMinute = draftDateTime.value.getMinutes();\n\n if (Number.isNaN(nextHour)) {\n return;\n }\n\n const availableMinutes = minuteValues.value.filter((minute) =>\n isTimeOptionEnabled(nextHour, minute),\n );\n\n if (!availableMinutes.length) {\n return;\n }\n\n const nextMinute = availableMinutes.includes(currentMinute)\n ? currentMinute\n : availableMinutes[0];\n\n draftDateTime.value = setTimePart(draftDateTime.value, nextHour, nextMinute);\n};\n\nconst updateMinute = (event: Event) => {\n const nextMinute = Number.parseInt((event.target as HTMLSelectElement).value, 10);\n const nextHour = draftDateTime.value.getHours();\n\n if (Number.isNaN(nextMinute) || !isTimeOptionEnabled(nextHour, nextMinute)) {\n return;\n }\n\n draftDateTime.value = setTimePart(draftDateTime.value, nextHour, nextMinute);\n};\n\nconst commitDraft = () => {\n const value = toDateTimeValue(draftDateTime.value);\n\n emit('update:modelValue', value);\n emit('change', value);\n emit('select', value);\n closeCalendar(true);\n};\n\nconst clearSelection = () => {\n emit('update:modelValue', '');\n emit('change', '');\n emit('select', '');\n closeCalendar(true);\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) {\n return;\n }\n\n const target = event.target;\n if (!(target instanceof Node)) {\n return;\n }\n\n if (rootRef.value?.contains(target)) {\n return;\n }\n\n closeCalendar();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n return;\n }\n\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <input\n v-if=\"hiddenInputName\"\n type=\"hidden\"\n :name=\"hiddenInputName\"\n :value=\"modelValue\"\n >\n\n <button\n ref=\"triggerRef\"\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-date-picker__trigger t-date-time-picker__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? pickerId : undefined\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :aria-invalid=\"invalid || undefined\"\n @click=\"toggleCalendar\"\n @keydown=\"onTriggerKeydown\"\n >\n <span\n v-if=\"$slots.prefix\"\n class=\"t-date-picker__slot t-date-picker__slot--prefix\"\n >\n <slot name=\"prefix\" />\n </span>\n\n <span\n class=\"t-date-picker__value\"\n :data-placeholder=\"selectedDateTime ? undefined : true\"\n >\n {{ triggerText }}\n </span>\n\n <span class=\"t-date-picker__slot t-date-picker__slot--suffix\">\n <slot name=\"suffix\">\n <TSpinner\n v-if=\"loading\"\n size=\"sm\"\n label=\"Loading\"\n />\n <CalendarIcon\n v-else\n v-bind=\"treeIconDefaults\"\n />\n </slot>\n </span>\n </button>\n\n <transition name=\"t-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"pickerId\"\n class=\"t-date-picker__content t-date-time-picker__content\"\n role=\"dialog\"\n aria-modal=\"false\"\n :aria-labelledby=\"captionId\"\n >\n <div class=\"t-date-time-picker__calendar\">\n <div class=\"t-date-picker__header\">\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMovePrev\"\n aria-label=\"Previous month\"\n @click=\"moveCalendar(-1)\"\n >\n <ChevronLeftIcon v-bind=\"treeIconDefaults\" />\n </button>\n\n <p\n :id=\"captionId\"\n class=\"t-date-picker__month\"\n >\n {{ monthLabel }}\n </p>\n\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMoveNext\"\n aria-label=\"Next month\"\n @click=\"moveCalendar(1)\"\n >\n <ChevronRightIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n\n <div\n class=\"t-date-picker__weekdays\"\n aria-hidden=\"true\"\n >\n <span\n v-for=\"label in weekdayLabels\"\n :key=\"label\"\n class=\"t-date-picker__weekday\"\n >\n {{ label }}\n </span>\n </div>\n\n <div\n class=\"t-date-picker__grid\"\n role=\"grid\"\n :aria-labelledby=\"captionId\"\n >\n <div\n v-for=\"(week, weekIndex) in cells\"\n :key=\"weekIndex\"\n class=\"t-date-picker__row\"\n role=\"row\"\n >\n <div\n v-for=\"cell in week\"\n :key=\"cell.key\"\n class=\"t-date-picker__cell\"\n role=\"gridcell\"\n :aria-selected=\"cell.isSelected\"\n >\n <button\n :ref=\"(element) => setDayRef(element, cell.key)\"\n type=\"button\"\n class=\"t-date-picker__day\"\n :class=\"{\n 'is-muted': !cell.inMonth,\n 'is-selected': cell.isSelected,\n 'is-today': cell.isToday,\n }\"\n :data-date=\"cell.key\"\n :tabindex=\"cell.isFocused ? 0 : -1\"\n :disabled=\"cell.disabled\"\n :aria-pressed=\"cell.isSelected\"\n @click=\"selectDate(cell.date)\"\n @keydown=\"onDayKeydown($event, cell.date)\"\n >\n {{ cell.dayLabel }}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"t-date-time-picker__summary\">\n <p class=\"t-date-time-picker__eyebrow\">\n Selected slot\n </p>\n <p class=\"t-date-time-picker__summary-value\">\n {{ draftPreview }}\n </p>\n </div>\n\n <div class=\"t-date-time-picker__time\">\n <label class=\"t-date-time-picker__time-field\">\n <span class=\"t-date-time-picker__time-label\">Hour</span>\n <select\n class=\"t-date-time-picker__select\"\n :value=\"draftDateTime.getHours()\"\n aria-label=\"Select hour\"\n @change=\"updateHour\"\n >\n <option\n v-for=\"option in hourOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ option.label }}\n </option>\n </select>\n </label>\n\n <label class=\"t-date-time-picker__time-field\">\n <span class=\"t-date-time-picker__time-label\">Minute</span>\n <select\n class=\"t-date-time-picker__select\"\n :value=\"draftDateTime.getMinutes()\"\n aria-label=\"Select minute\"\n @change=\"updateMinute\"\n >\n <option\n v-for=\"option in minuteOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ option.label }}\n </option>\n </select>\n </label>\n </div>\n\n <div class=\"t-date-time-picker__footer\">\n <button\n type=\"button\"\n class=\"t-button t-button--ghost t-button--sm\"\n @click=\"clearSelection\"\n >\n Clear\n </button>\n <button\n type=\"button\"\n class=\"t-button t-button--solid t-button--sm\"\n @click=\"commitDraft\"\n >\n Apply\n </button>\n </div>\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"TDateTimePicker.cjs","names":[],"sources":["../../src/components/TDateTimePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n addDays,\n addMonths,\n createId,\n isActivationKey,\n isEscapeKey,\n isSameDay,\n startOfDay,\n startOfMonth,\n startOfWeek,\n toDateValue,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n type ComponentPublicInstance,\n computed,\n nextTick,\n onBeforeUnmount,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSize } from '../types/contracts';\nimport TSpinner from './TSpinner.vue';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\ninterface CalendarCell {\n date: Date;\n dayLabel: number;\n disabled: boolean;\n inMonth: boolean;\n isFocused: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n}\n\ninterface TimeOption {\n disabled: boolean;\n label: string;\n value: number;\n}\n\nconst CalendarIcon = computed(() => getTreeIcon('calendar'));\nconst ChevronLeftIcon = computed(() => getTreeIcon('chevron-left'));\nconst ChevronRightIcon = computed(() => getTreeIcon('chevron-right'));\n\nconst dateOnlyPattern = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst dateTimePattern = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2}))?$/;\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n open?: boolean;\n defaultOpen?: boolean;\n size?: TSize;\n disabled?: boolean;\n loading?: boolean;\n invalid?: boolean;\n placeholder?: string;\n locale?: string;\n min?: string;\n max?: string;\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n id?: string;\n step?: number;\n }>(),\n {\n modelValue: '',\n open: undefined,\n defaultOpen: false,\n size: 'md',\n disabled: false,\n loading: false,\n invalid: false,\n placeholder: 'Select date and time',\n locale: 'en-US',\n min: '',\n max: '',\n weekStartsOn: 0,\n id: undefined,\n step: 60,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n change: [value: string];\n select: [value: string];\n}>();\n\nconst attrs = useAttrs();\n\nconst cloneDate = (value: Date) => new Date(value.getTime());\n\nconst formatPart = (value: number) => `${value}`.padStart(2, '0');\n\nconst endOfDay = (date: Date) =>\n new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);\n\nconst parseBoundaryValue = (\n value: string | null | undefined,\n boundary: 'exact' | 'start' | 'end' = 'exact',\n) => {\n if (!value) {\n return null;\n }\n\n const dateOnlyMatch = value.match(dateOnlyPattern);\n if (dateOnlyMatch) {\n const [, yearToken, monthToken, dayToken] = dateOnlyMatch;\n const year = Number.parseInt(yearToken, 10);\n const month = Number.parseInt(monthToken, 10);\n const day = Number.parseInt(dayToken, 10);\n const hour = boundary === 'end' ? 23 : 0;\n const minute = boundary === 'end' ? 59 : 0;\n const second = boundary === 'end' ? 59 : 0;\n const millisecond = boundary === 'end' ? 999 : 0;\n const date = new Date(year, month - 1, day, hour, minute, second, millisecond);\n\n if (\n Number.isNaN(date.getTime()) ||\n date.getFullYear() !== year ||\n date.getMonth() !== month - 1 ||\n date.getDate() !== day\n ) {\n return null;\n }\n\n return date;\n }\n\n const dateTimeMatch = value.match(dateTimePattern);\n if (!dateTimeMatch) {\n return null;\n }\n\n const [, yearToken, monthToken, dayToken, hourToken, minuteToken, secondToken] =\n dateTimeMatch;\n const year = Number.parseInt(yearToken, 10);\n const month = Number.parseInt(monthToken, 10);\n const day = Number.parseInt(dayToken, 10);\n const hour = Number.parseInt(hourToken, 10);\n const minute = Number.parseInt(minuteToken, 10);\n const second = secondToken ? Number.parseInt(secondToken, 10) : 0;\n const date = new Date(year, month - 1, day, hour, minute, second, 0);\n\n if (\n Number.isNaN(date.getTime()) ||\n date.getFullYear() !== year ||\n date.getMonth() !== month - 1 ||\n date.getDate() !== day ||\n date.getHours() !== hour ||\n date.getMinutes() !== minute ||\n date.getSeconds() !== second\n ) {\n return null;\n }\n\n return date;\n};\n\nconst toDateTimeValue = (date: Date) =>\n `${toDateValue(date)}T${formatPart(date.getHours())}:${formatPart(date.getMinutes())}`;\n\nconst setDatePart = (source: Date, targetDate: Date) =>\n new Date(\n targetDate.getFullYear(),\n targetDate.getMonth(),\n targetDate.getDate(),\n source.getHours(),\n source.getMinutes(),\n 0,\n 0,\n );\n\nconst setTimePart = (source: Date, hour: number, minute: number) =>\n new Date(\n source.getFullYear(),\n source.getMonth(),\n source.getDate(),\n hour,\n minute,\n 0,\n 0,\n );\n\nconst resolveMinuteStep = (step: number) => {\n if (!Number.isFinite(step) || step <= 0 || step % 60 !== 0) {\n return 1;\n }\n\n const minuteStep = step / 60;\n return minuteStep >= 60 ? 1 : minuteStep;\n};\n\nconst today = startOfDay(new Date());\nconst selectedDateTime = computed(() => parseBoundaryValue(props.modelValue, 'exact'));\nconst minDateTime = computed(() => parseBoundaryValue(props.min, 'start'));\nconst maxDateTime = computed(() => parseBoundaryValue(props.max, 'end'));\nconst pickerId = props.id ?? createId('t-date-time-picker');\nconst captionId = `${pickerId}-caption`;\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst dayRefs = new Map<string, HTMLButtonElement>();\n\nconst clampToRange = (value: Date) => {\n let nextValue = cloneDate(value);\n\n if (minDateTime.value && nextValue < minDateTime.value) {\n nextValue = cloneDate(minDateTime.value);\n }\n\n if (maxDateTime.value && nextValue > maxDateTime.value) {\n nextValue = cloneDate(maxDateTime.value);\n }\n\n return nextValue;\n};\n\nconst createDefaultDraftDateTime = () => {\n const reference = new Date();\n const stepMilliseconds = Math.max(props.step, 60) * 1000;\n const rounded = new Date(Math.ceil(reference.getTime() / stepMilliseconds) * stepMilliseconds);\n\n rounded.setSeconds(0, 0);\n return clampToRange(rounded);\n};\n\nconst initialDateTime = selectedDateTime.value ?? createDefaultDraftDateTime();\nconst draftDateTime = ref(initialDateTime);\nconst focusedDate = ref(startOfDay(initialDateTime));\nconst viewMonth = ref(startOfMonth(initialDateTime));\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, name: _name, ...rest } = attrs;\n return rest;\n});\n\nconst hiddenInputName = computed(() =>\n typeof attrs.name === 'string' ? attrs.name : undefined,\n);\n\nconst rootClasses = computed(() => [\n 't-date-picker',\n 't-date-time-picker',\n `t-date-picker--${props.size}`,\n {\n 'is-disabled': props.disabled,\n 'is-invalid': props.invalid,\n 'is-loading': props.loading,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst triggerText = computed(() => {\n if (!selectedDateTime.value) {\n return props.placeholder;\n }\n\n return new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n month: 'long',\n year: 'numeric',\n }).format(selectedDateTime.value);\n});\n\nconst draftPreview = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n month: 'long',\n year: 'numeric',\n }).format(draftDateTime.value),\n);\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n month: 'long',\n year: 'numeric',\n }).format(viewMonth.value),\n);\n\nconst weekdayLabels = computed(() => {\n const formatter = new Intl.DateTimeFormat(props.locale, { weekday: 'short' });\n const weekBase = startOfWeek(new Date(2024, 0, 7), props.weekStartsOn);\n\n return Array.from({ length: 7 }, (_, index) =>\n formatter.format(addDays(weekBase, index)),\n );\n});\n\nconst minuteStep = computed(() => resolveMinuteStep(props.step));\n\nconst minuteValues = computed(() => {\n const values = new Set<number>();\n\n for (let minute = 0; minute < 60; minute += minuteStep.value) {\n values.add(minute);\n }\n\n values.add(draftDateTime.value.getMinutes());\n\n return [...values].sort((left, right) => left - right);\n});\n\nconst isDateDisabled = (date: Date) => {\n if (props.disabled) {\n return true;\n }\n\n const dayStart = startOfDay(date);\n const dayEnd = endOfDay(date);\n\n if (minDateTime.value && dayEnd < minDateTime.value) {\n return true;\n }\n\n if (maxDateTime.value && dayStart > maxDateTime.value) {\n return true;\n }\n\n return false;\n};\n\nconst isDateTimeDisabled = (value: Date) => {\n if (props.disabled) {\n return true;\n }\n\n if (minDateTime.value && value < minDateTime.value) {\n return true;\n }\n\n if (maxDateTime.value && value > maxDateTime.value) {\n return true;\n }\n\n return false;\n};\n\nconst syncCalendarView = (date: Date) => {\n focusedDate.value = startOfDay(date);\n viewMonth.value = startOfMonth(date);\n};\n\nconst syncDraftState = (dateTime: Date) => {\n const nextValue = clampToRange(dateTime);\n draftDateTime.value = nextValue;\n syncCalendarView(nextValue);\n};\n\nwatch(\n selectedDateTime,\n (value) => {\n if (isOpen.value) {\n return;\n }\n\n syncDraftState(value ?? createDefaultDraftDateTime());\n },\n { immediate: true },\n);\n\nconst draftDate = computed(() => startOfDay(draftDateTime.value));\n\nconst cells = computed(() => {\n const startDate = startOfWeek(startOfMonth(viewMonth.value), props.weekStartsOn);\n\n return Array.from({ length: 6 }, (_, weekIndex) =>\n Array.from({ length: 7 }, (_, dayIndex) => {\n const date = addDays(startDate, weekIndex * 7 + dayIndex);\n const key = toDateValue(date);\n\n return {\n date,\n dayLabel: date.getDate(),\n disabled: isDateDisabled(date),\n inMonth: date.getMonth() === viewMonth.value.getMonth(),\n isFocused: isSameDay(date, focusedDate.value),\n isSelected: isSameDay(date, draftDate.value),\n isToday: isSameDay(date, today),\n key,\n } satisfies CalendarCell;\n }),\n );\n});\n\nconst canMovePrev = computed(() => {\n if (!minDateTime.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) > startOfMonth(minDateTime.value);\n});\n\nconst canMoveNext = computed(() => {\n if (!maxDateTime.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) < startOfMonth(maxDateTime.value);\n});\n\nconst isTimeOptionEnabled = (hour: number, minute: number) => {\n const candidate = setTimePart(draftDateTime.value, hour, minute);\n return !isDateTimeDisabled(candidate);\n};\n\nconst hourOptions = computed<TimeOption[]>(() =>\n Array.from({ length: 24 }, (_, hour) => ({\n disabled: !minuteValues.value.some((minute) => isTimeOptionEnabled(hour, minute)),\n label: formatPart(hour),\n value: hour,\n })),\n);\n\nconst minuteOptions = computed<TimeOption[]>(() =>\n minuteValues.value.map((minute) => ({\n disabled: !isTimeOptionEnabled(draftDateTime.value.getHours(), minute),\n label: formatPart(minute),\n value: minute,\n })),\n);\n\nconst focusCurrentDay = () => {\n const key = toDateValue(focusedDate.value);\n nextTick(() => {\n dayRefs.get(key)?.focus();\n });\n};\n\nconst setDayRef = (\n element: Element | ComponentPublicInstance | null,\n key: string,\n) => {\n if (element instanceof HTMLButtonElement) {\n dayRefs.set(key, element);\n return;\n }\n\n dayRefs.delete(key);\n};\n\nconst openCalendar = () => {\n if (props.disabled) {\n return;\n }\n\n syncDraftState(selectedDateTime.value ?? createDefaultDraftDateTime());\n setValue(true);\n focusCurrentDay();\n};\n\nconst closeCalendar = (restoreFocus = false) => {\n setValue(false);\n\n if (restoreFocus) {\n nextTick(() => {\n triggerRef.value?.focus();\n });\n }\n};\n\nconst toggleCalendar = () => {\n if (isOpen.value) {\n closeCalendar();\n return;\n }\n\n openCalendar();\n};\n\nconst moveCalendar = (direction: 1 | -1) => {\n const nextMonth = addMonths(viewMonth.value, direction);\n syncCalendarView(nextMonth);\n focusCurrentDay();\n};\n\nconst moveFocus = (nextDate: Date) => {\n if (isDateDisabled(nextDate)) {\n return;\n }\n\n syncCalendarView(nextDate);\n focusCurrentDay();\n};\n\nconst selectDate = (date: Date) => {\n if (isDateDisabled(date)) {\n return;\n }\n\n const candidate = clampToRange(setDatePart(draftDateTime.value, date));\n draftDateTime.value = candidate;\n syncCalendarView(candidate);\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) {\n return;\n }\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeCalendar();\n return;\n }\n\n if (\n isActivationKey(event) ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowUp'\n ) {\n event.preventDefault();\n openCalendar();\n }\n};\n\nconst onDayKeydown = (event: KeyboardEvent, date: Date) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeCalendar(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectDate(date);\n return;\n }\n\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault();\n moveFocus(addDays(date, -1));\n break;\n case 'ArrowRight':\n event.preventDefault();\n moveFocus(addDays(date, 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(addDays(date, -7));\n break;\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(addDays(date, 7));\n break;\n case 'Home':\n event.preventDefault();\n moveFocus(startOfWeek(date, props.weekStartsOn));\n break;\n case 'End':\n event.preventDefault();\n moveFocus(addDays(startOfWeek(date, props.weekStartsOn), 6));\n break;\n case 'PageUp':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? -12 : -1));\n break;\n case 'PageDown':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? 12 : 1));\n break;\n default:\n break;\n }\n};\n\nconst updateHour = (event: Event) => {\n const nextHour = Number.parseInt((event.target as HTMLSelectElement).value, 10);\n const currentMinute = draftDateTime.value.getMinutes();\n\n if (Number.isNaN(nextHour)) {\n return;\n }\n\n const availableMinutes = minuteValues.value.filter((minute) =>\n isTimeOptionEnabled(nextHour, minute),\n );\n\n if (!availableMinutes.length) {\n return;\n }\n\n const nextMinute = availableMinutes.includes(currentMinute)\n ? currentMinute\n : availableMinutes[0];\n\n draftDateTime.value = setTimePart(draftDateTime.value, nextHour, nextMinute);\n};\n\nconst updateMinute = (event: Event) => {\n const nextMinute = Number.parseInt((event.target as HTMLSelectElement).value, 10);\n const nextHour = draftDateTime.value.getHours();\n\n if (Number.isNaN(nextMinute) || !isTimeOptionEnabled(nextHour, nextMinute)) {\n return;\n }\n\n draftDateTime.value = setTimePart(draftDateTime.value, nextHour, nextMinute);\n};\n\nconst commitDraft = () => {\n const value = toDateTimeValue(draftDateTime.value);\n\n emit('update:modelValue', value);\n emit('change', value);\n emit('select', value);\n closeCalendar(true);\n};\n\nconst clearSelection = () => {\n emit('update:modelValue', '');\n emit('change', '');\n emit('select', '');\n closeCalendar(true);\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) {\n return;\n }\n\n const target = event.target;\n if (!(target instanceof Node)) {\n return;\n }\n\n if (rootRef.value?.contains(target)) {\n return;\n }\n\n closeCalendar();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n return;\n }\n\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <input\n v-if=\"hiddenInputName\"\n type=\"hidden\"\n :name=\"hiddenInputName\"\n :value=\"modelValue\"\n >\n\n <button\n ref=\"triggerRef\"\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-date-picker__trigger t-date-time-picker__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? pickerId : undefined\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :aria-invalid=\"invalid || undefined\"\n @click=\"toggleCalendar\"\n @keydown=\"onTriggerKeydown\"\n >\n <span\n v-if=\"$slots.prefix\"\n class=\"t-date-picker__slot t-date-picker__slot--prefix\"\n >\n <slot name=\"prefix\" />\n </span>\n\n <span\n class=\"t-date-picker__value\"\n :data-placeholder=\"selectedDateTime ? undefined : true\"\n >\n {{ triggerText }}\n </span>\n\n <span class=\"t-date-picker__slot t-date-picker__slot--suffix\">\n <slot name=\"suffix\">\n <TSpinner\n v-if=\"loading\"\n size=\"sm\"\n label=\"Loading\"\n />\n <CalendarIcon\n v-else\n v-bind=\"treeIconDefaults\"\n />\n </slot>\n </span>\n </button>\n\n <transition name=\"t-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"pickerId\"\n class=\"t-date-picker__content t-date-time-picker__content\"\n role=\"dialog\"\n aria-modal=\"false\"\n :aria-labelledby=\"captionId\"\n >\n <div class=\"t-date-time-picker__calendar\">\n <div class=\"t-date-picker__header\">\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMovePrev\"\n aria-label=\"Previous month\"\n @click=\"moveCalendar(-1)\"\n >\n <ChevronLeftIcon v-bind=\"treeIconDefaults\" />\n </button>\n\n <p\n :id=\"captionId\"\n class=\"t-date-picker__month\"\n >\n {{ monthLabel }}\n </p>\n\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMoveNext\"\n aria-label=\"Next month\"\n @click=\"moveCalendar(1)\"\n >\n <ChevronRightIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n\n <div\n class=\"t-date-picker__weekdays\"\n aria-hidden=\"true\"\n >\n <span\n v-for=\"label in weekdayLabels\"\n :key=\"label\"\n class=\"t-date-picker__weekday\"\n >\n {{ label }}\n </span>\n </div>\n\n <div\n class=\"t-date-picker__grid\"\n role=\"grid\"\n :aria-labelledby=\"captionId\"\n >\n <div\n v-for=\"(week, weekIndex) in cells\"\n :key=\"weekIndex\"\n class=\"t-date-picker__row\"\n role=\"row\"\n >\n <div\n v-for=\"cell in week\"\n :key=\"cell.key\"\n class=\"t-date-picker__cell\"\n role=\"gridcell\"\n :aria-selected=\"cell.isSelected\"\n >\n <button\n :ref=\"(element) => setDayRef(element, cell.key)\"\n type=\"button\"\n class=\"t-date-picker__day\"\n :class=\"{\n 'is-muted': !cell.inMonth,\n 'is-selected': cell.isSelected,\n 'is-today': cell.isToday,\n }\"\n :data-date=\"cell.key\"\n :tabindex=\"cell.isFocused ? 0 : -1\"\n :disabled=\"cell.disabled\"\n :aria-pressed=\"cell.isSelected\"\n @click=\"selectDate(cell.date)\"\n @keydown=\"onDayKeydown($event, cell.date)\"\n >\n {{ cell.dayLabel }}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"t-date-time-picker__summary\">\n <p class=\"t-date-time-picker__eyebrow\">\n Selected slot\n </p>\n <p class=\"t-date-time-picker__summary-value\">\n {{ draftPreview }}\n </p>\n </div>\n\n <div class=\"t-date-time-picker__time\">\n <label class=\"t-date-time-picker__time-field\">\n <span class=\"t-date-time-picker__time-label\">Hour</span>\n <select\n class=\"t-date-time-picker__select\"\n :value=\"draftDateTime.getHours()\"\n aria-label=\"Select hour\"\n @change=\"updateHour\"\n >\n <option\n v-for=\"option in hourOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ option.label }}\n </option>\n </select>\n </label>\n\n <label class=\"t-date-time-picker__time-field\">\n <span class=\"t-date-time-picker__time-label\">Minute</span>\n <select\n class=\"t-date-time-picker__select\"\n :value=\"draftDateTime.getMinutes()\"\n aria-label=\"Select minute\"\n @change=\"updateMinute\"\n >\n <option\n v-for=\"option in minuteOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ option.label }}\n </option>\n </select>\n </label>\n </div>\n\n <div class=\"t-date-time-picker__footer\">\n <button\n type=\"button\"\n class=\"t-button t-button--ghost t-button--sm\"\n @click=\"clearSelection\"\n >\n Clear\n </button>\n <button\n type=\"button\"\n class=\"t-button t-button--solid t-button--sm\"\n @click=\"commitDraft\"\n >\n Apply\n </button>\n </div>\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"TDateTimePicker.js","names":[],"sources":["../../src/components/TDateTimePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n addDays,\n addMonths,\n createId,\n isActivationKey,\n isEscapeKey,\n isSameDay,\n startOfDay,\n startOfMonth,\n startOfWeek,\n toDateValue,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n type ComponentPublicInstance,\n computed,\n nextTick,\n onBeforeUnmount,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSize } from '../types/contracts';\nimport TSpinner from './TSpinner.vue';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\ninterface CalendarCell {\n date: Date;\n dayLabel: number;\n disabled: boolean;\n inMonth: boolean;\n isFocused: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n}\n\ninterface TimeOption {\n disabled: boolean;\n label: string;\n value: number;\n}\n\nconst CalendarIcon = getTreeIcon('calendar');\nconst ChevronLeftIcon = getTreeIcon('chevron-left');\nconst ChevronRightIcon = getTreeIcon('chevron-right');\n\nconst dateOnlyPattern = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst dateTimePattern = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2}))?$/;\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n open?: boolean;\n defaultOpen?: boolean;\n size?: TSize;\n disabled?: boolean;\n loading?: boolean;\n invalid?: boolean;\n placeholder?: string;\n locale?: string;\n min?: string;\n max?: string;\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n id?: string;\n step?: number;\n }>(),\n {\n modelValue: '',\n open: undefined,\n defaultOpen: false,\n size: 'md',\n disabled: false,\n loading: false,\n invalid: false,\n placeholder: 'Select date and time',\n locale: 'en-US',\n min: '',\n max: '',\n weekStartsOn: 0,\n id: undefined,\n step: 60,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n change: [value: string];\n select: [value: string];\n}>();\n\nconst attrs = useAttrs();\n\nconst cloneDate = (value: Date) => new Date(value.getTime());\n\nconst formatPart = (value: number) => `${value}`.padStart(2, '0');\n\nconst endOfDay = (date: Date) =>\n new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);\n\nconst parseBoundaryValue = (\n value: string | null | undefined,\n boundary: 'exact' | 'start' | 'end' = 'exact',\n) => {\n if (!value) {\n return null;\n }\n\n const dateOnlyMatch = value.match(dateOnlyPattern);\n if (dateOnlyMatch) {\n const [, yearToken, monthToken, dayToken] = dateOnlyMatch;\n const year = Number.parseInt(yearToken, 10);\n const month = Number.parseInt(monthToken, 10);\n const day = Number.parseInt(dayToken, 10);\n const hour = boundary === 'end' ? 23 : 0;\n const minute = boundary === 'end' ? 59 : 0;\n const second = boundary === 'end' ? 59 : 0;\n const millisecond = boundary === 'end' ? 999 : 0;\n const date = new Date(year, month - 1, day, hour, minute, second, millisecond);\n\n if (\n Number.isNaN(date.getTime()) ||\n date.getFullYear() !== year ||\n date.getMonth() !== month - 1 ||\n date.getDate() !== day\n ) {\n return null;\n }\n\n return date;\n }\n\n const dateTimeMatch = value.match(dateTimePattern);\n if (!dateTimeMatch) {\n return null;\n }\n\n const [, yearToken, monthToken, dayToken, hourToken, minuteToken, secondToken] =\n dateTimeMatch;\n const year = Number.parseInt(yearToken, 10);\n const month = Number.parseInt(monthToken, 10);\n const day = Number.parseInt(dayToken, 10);\n const hour = Number.parseInt(hourToken, 10);\n const minute = Number.parseInt(minuteToken, 10);\n const second = secondToken ? Number.parseInt(secondToken, 10) : 0;\n const date = new Date(year, month - 1, day, hour, minute, second, 0);\n\n if (\n Number.isNaN(date.getTime()) ||\n date.getFullYear() !== year ||\n date.getMonth() !== month - 1 ||\n date.getDate() !== day ||\n date.getHours() !== hour ||\n date.getMinutes() !== minute ||\n date.getSeconds() !== second\n ) {\n return null;\n }\n\n return date;\n};\n\nconst toDateTimeValue = (date: Date) =>\n `${toDateValue(date)}T${formatPart(date.getHours())}:${formatPart(date.getMinutes())}`;\n\nconst setDatePart = (source: Date, targetDate: Date) =>\n new Date(\n targetDate.getFullYear(),\n targetDate.getMonth(),\n targetDate.getDate(),\n source.getHours(),\n source.getMinutes(),\n 0,\n 0,\n );\n\nconst setTimePart = (source: Date, hour: number, minute: number) =>\n new Date(\n source.getFullYear(),\n source.getMonth(),\n source.getDate(),\n hour,\n minute,\n 0,\n 0,\n );\n\nconst resolveMinuteStep = (step: number) => {\n if (!Number.isFinite(step) || step <= 0 || step % 60 !== 0) {\n return 1;\n }\n\n const minuteStep = step / 60;\n return minuteStep >= 60 ? 1 : minuteStep;\n};\n\nconst today = startOfDay(new Date());\nconst selectedDateTime = computed(() => parseBoundaryValue(props.modelValue, 'exact'));\nconst minDateTime = computed(() => parseBoundaryValue(props.min, 'start'));\nconst maxDateTime = computed(() => parseBoundaryValue(props.max, 'end'));\nconst pickerId = props.id ?? createId('t-date-time-picker');\nconst captionId = `${pickerId}-caption`;\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst dayRefs = new Map<string, HTMLButtonElement>();\n\nconst clampToRange = (value: Date) => {\n let nextValue = cloneDate(value);\n\n if (minDateTime.value && nextValue < minDateTime.value) {\n nextValue = cloneDate(minDateTime.value);\n }\n\n if (maxDateTime.value && nextValue > maxDateTime.value) {\n nextValue = cloneDate(maxDateTime.value);\n }\n\n return nextValue;\n};\n\nconst createDefaultDraftDateTime = () => {\n const reference = new Date();\n const stepMilliseconds = Math.max(props.step, 60) * 1000;\n const rounded = new Date(Math.ceil(reference.getTime() / stepMilliseconds) * stepMilliseconds);\n\n rounded.setSeconds(0, 0);\n return clampToRange(rounded);\n};\n\nconst initialDateTime = selectedDateTime.value ?? createDefaultDraftDateTime();\nconst draftDateTime = ref(initialDateTime);\nconst focusedDate = ref(startOfDay(initialDateTime));\nconst viewMonth = ref(startOfMonth(initialDateTime));\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, name: _name, ...rest } = attrs;\n return rest;\n});\n\nconst hiddenInputName = computed(() =>\n typeof attrs.name === 'string' ? attrs.name : undefined,\n);\n\nconst rootClasses = computed(() => [\n 't-date-picker',\n 't-date-time-picker',\n `t-date-picker--${props.size}`,\n {\n 'is-disabled': props.disabled,\n 'is-invalid': props.invalid,\n 'is-loading': props.loading,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst triggerText = computed(() => {\n if (!selectedDateTime.value) {\n return props.placeholder;\n }\n\n return new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n month: 'long',\n year: 'numeric',\n }).format(selectedDateTime.value);\n});\n\nconst draftPreview = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n month: 'long',\n year: 'numeric',\n }).format(draftDateTime.value),\n);\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n month: 'long',\n year: 'numeric',\n }).format(viewMonth.value),\n);\n\nconst weekdayLabels = computed(() => {\n const formatter = new Intl.DateTimeFormat(props.locale, { weekday: 'short' });\n const weekBase = startOfWeek(new Date(2024, 0, 7), props.weekStartsOn);\n\n return Array.from({ length: 7 }, (_, index) =>\n formatter.format(addDays(weekBase, index)),\n );\n});\n\nconst minuteStep = computed(() => resolveMinuteStep(props.step));\n\nconst minuteValues = computed(() => {\n const values = new Set<number>();\n\n for (let minute = 0; minute < 60; minute += minuteStep.value) {\n values.add(minute);\n }\n\n values.add(draftDateTime.value.getMinutes());\n\n return [...values].sort((left, right) => left - right);\n});\n\nconst isDateDisabled = (date: Date) => {\n if (props.disabled) {\n return true;\n }\n\n const dayStart = startOfDay(date);\n const dayEnd = endOfDay(date);\n\n if (minDateTime.value && dayEnd < minDateTime.value) {\n return true;\n }\n\n if (maxDateTime.value && dayStart > maxDateTime.value) {\n return true;\n }\n\n return false;\n};\n\nconst isDateTimeDisabled = (value: Date) => {\n if (props.disabled) {\n return true;\n }\n\n if (minDateTime.value && value < minDateTime.value) {\n return true;\n }\n\n if (maxDateTime.value && value > maxDateTime.value) {\n return true;\n }\n\n return false;\n};\n\nconst syncCalendarView = (date: Date) => {\n focusedDate.value = startOfDay(date);\n viewMonth.value = startOfMonth(date);\n};\n\nconst syncDraftState = (dateTime: Date) => {\n const nextValue = clampToRange(dateTime);\n draftDateTime.value = nextValue;\n syncCalendarView(nextValue);\n};\n\nwatch(\n selectedDateTime,\n (value) => {\n if (isOpen.value) {\n return;\n }\n\n syncDraftState(value ?? createDefaultDraftDateTime());\n },\n { immediate: true },\n);\n\nconst draftDate = computed(() => startOfDay(draftDateTime.value));\n\nconst cells = computed(() => {\n const startDate = startOfWeek(startOfMonth(viewMonth.value), props.weekStartsOn);\n\n return Array.from({ length: 6 }, (_, weekIndex) =>\n Array.from({ length: 7 }, (_, dayIndex) => {\n const date = addDays(startDate, weekIndex * 7 + dayIndex);\n const key = toDateValue(date);\n\n return {\n date,\n dayLabel: date.getDate(),\n disabled: isDateDisabled(date),\n inMonth: date.getMonth() === viewMonth.value.getMonth(),\n isFocused: isSameDay(date, focusedDate.value),\n isSelected: isSameDay(date, draftDate.value),\n isToday: isSameDay(date, today),\n key,\n } satisfies CalendarCell;\n }),\n );\n});\n\nconst canMovePrev = computed(() => {\n if (!minDateTime.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) > startOfMonth(minDateTime.value);\n});\n\nconst canMoveNext = computed(() => {\n if (!maxDateTime.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) < startOfMonth(maxDateTime.value);\n});\n\nconst isTimeOptionEnabled = (hour: number, minute: number) => {\n const candidate = setTimePart(draftDateTime.value, hour, minute);\n return !isDateTimeDisabled(candidate);\n};\n\nconst hourOptions = computed<TimeOption[]>(() =>\n Array.from({ length: 24 }, (_, hour) => ({\n disabled: !minuteValues.value.some((minute) => isTimeOptionEnabled(hour, minute)),\n label: formatPart(hour),\n value: hour,\n })),\n);\n\nconst minuteOptions = computed<TimeOption[]>(() =>\n minuteValues.value.map((minute) => ({\n disabled: !isTimeOptionEnabled(draftDateTime.value.getHours(), minute),\n label: formatPart(minute),\n value: minute,\n })),\n);\n\nconst focusCurrentDay = () => {\n const key = toDateValue(focusedDate.value);\n nextTick(() => {\n dayRefs.get(key)?.focus();\n });\n};\n\nconst setDayRef = (\n element: Element | ComponentPublicInstance | null,\n key: string,\n) => {\n if (element instanceof HTMLButtonElement) {\n dayRefs.set(key, element);\n return;\n }\n\n dayRefs.delete(key);\n};\n\nconst openCalendar = () => {\n if (props.disabled) {\n return;\n }\n\n syncDraftState(selectedDateTime.value ?? createDefaultDraftDateTime());\n setValue(true);\n focusCurrentDay();\n};\n\nconst closeCalendar = (restoreFocus = false) => {\n setValue(false);\n\n if (restoreFocus) {\n nextTick(() => {\n triggerRef.value?.focus();\n });\n }\n};\n\nconst toggleCalendar = () => {\n if (isOpen.value) {\n closeCalendar();\n return;\n }\n\n openCalendar();\n};\n\nconst moveCalendar = (direction: 1 | -1) => {\n const nextMonth = addMonths(viewMonth.value, direction);\n syncCalendarView(nextMonth);\n focusCurrentDay();\n};\n\nconst moveFocus = (nextDate: Date) => {\n if (isDateDisabled(nextDate)) {\n return;\n }\n\n syncCalendarView(nextDate);\n focusCurrentDay();\n};\n\nconst selectDate = (date: Date) => {\n if (isDateDisabled(date)) {\n return;\n }\n\n const candidate = clampToRange(setDatePart(draftDateTime.value, date));\n draftDateTime.value = candidate;\n syncCalendarView(candidate);\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) {\n return;\n }\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeCalendar();\n return;\n }\n\n if (\n isActivationKey(event) ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowUp'\n ) {\n event.preventDefault();\n openCalendar();\n }\n};\n\nconst onDayKeydown = (event: KeyboardEvent, date: Date) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeCalendar(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectDate(date);\n return;\n }\n\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault();\n moveFocus(addDays(date, -1));\n break;\n case 'ArrowRight':\n event.preventDefault();\n moveFocus(addDays(date, 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(addDays(date, -7));\n break;\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(addDays(date, 7));\n break;\n case 'Home':\n event.preventDefault();\n moveFocus(startOfWeek(date, props.weekStartsOn));\n break;\n case 'End':\n event.preventDefault();\n moveFocus(addDays(startOfWeek(date, props.weekStartsOn), 6));\n break;\n case 'PageUp':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? -12 : -1));\n break;\n case 'PageDown':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? 12 : 1));\n break;\n default:\n break;\n }\n};\n\nconst updateHour = (event: Event) => {\n const nextHour = Number.parseInt((event.target as HTMLSelectElement).value, 10);\n const currentMinute = draftDateTime.value.getMinutes();\n\n if (Number.isNaN(nextHour)) {\n return;\n }\n\n const availableMinutes = minuteValues.value.filter((minute) =>\n isTimeOptionEnabled(nextHour, minute),\n );\n\n if (!availableMinutes.length) {\n return;\n }\n\n const nextMinute = availableMinutes.includes(currentMinute)\n ? currentMinute\n : availableMinutes[0];\n\n draftDateTime.value = setTimePart(draftDateTime.value, nextHour, nextMinute);\n};\n\nconst updateMinute = (event: Event) => {\n const nextMinute = Number.parseInt((event.target as HTMLSelectElement).value, 10);\n const nextHour = draftDateTime.value.getHours();\n\n if (Number.isNaN(nextMinute) || !isTimeOptionEnabled(nextHour, nextMinute)) {\n return;\n }\n\n draftDateTime.value = setTimePart(draftDateTime.value, nextHour, nextMinute);\n};\n\nconst commitDraft = () => {\n const value = toDateTimeValue(draftDateTime.value);\n\n emit('update:modelValue', value);\n emit('change', value);\n emit('select', value);\n closeCalendar(true);\n};\n\nconst clearSelection = () => {\n emit('update:modelValue', '');\n emit('change', '');\n emit('select', '');\n closeCalendar(true);\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) {\n return;\n }\n\n const target = event.target;\n if (!(target instanceof Node)) {\n return;\n }\n\n if (rootRef.value?.contains(target)) {\n return;\n }\n\n closeCalendar();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n return;\n }\n\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <input\n v-if=\"hiddenInputName\"\n type=\"hidden\"\n :name=\"hiddenInputName\"\n :value=\"modelValue\"\n >\n\n <button\n ref=\"triggerRef\"\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-date-picker__trigger t-date-time-picker__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? pickerId : undefined\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :aria-invalid=\"invalid || undefined\"\n @click=\"toggleCalendar\"\n @keydown=\"onTriggerKeydown\"\n >\n <span\n v-if=\"$slots.prefix\"\n class=\"t-date-picker__slot t-date-picker__slot--prefix\"\n >\n <slot name=\"prefix\" />\n </span>\n\n <span\n class=\"t-date-picker__value\"\n :data-placeholder=\"selectedDateTime ? undefined : true\"\n >\n {{ triggerText }}\n </span>\n\n <span class=\"t-date-picker__slot t-date-picker__slot--suffix\">\n <slot name=\"suffix\">\n <TSpinner\n v-if=\"loading\"\n size=\"sm\"\n label=\"Loading\"\n />\n <CalendarIcon\n v-else\n v-bind=\"treeIconDefaults\"\n />\n </slot>\n </span>\n </button>\n\n <transition name=\"t-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"pickerId\"\n class=\"t-date-picker__content t-date-time-picker__content\"\n role=\"dialog\"\n aria-modal=\"false\"\n :aria-labelledby=\"captionId\"\n >\n <div class=\"t-date-time-picker__calendar\">\n <div class=\"t-date-picker__header\">\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMovePrev\"\n aria-label=\"Previous month\"\n @click=\"moveCalendar(-1)\"\n >\n <ChevronLeftIcon v-bind=\"treeIconDefaults\" />\n </button>\n\n <p\n :id=\"captionId\"\n class=\"t-date-picker__month\"\n >\n {{ monthLabel }}\n </p>\n\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMoveNext\"\n aria-label=\"Next month\"\n @click=\"moveCalendar(1)\"\n >\n <ChevronRightIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n\n <div\n class=\"t-date-picker__weekdays\"\n aria-hidden=\"true\"\n >\n <span\n v-for=\"label in weekdayLabels\"\n :key=\"label\"\n class=\"t-date-picker__weekday\"\n >\n {{ label }}\n </span>\n </div>\n\n <div\n class=\"t-date-picker__grid\"\n role=\"grid\"\n :aria-labelledby=\"captionId\"\n >\n <div\n v-for=\"(week, weekIndex) in cells\"\n :key=\"weekIndex\"\n class=\"t-date-picker__row\"\n role=\"row\"\n >\n <div\n v-for=\"cell in week\"\n :key=\"cell.key\"\n class=\"t-date-picker__cell\"\n role=\"gridcell\"\n :aria-selected=\"cell.isSelected\"\n >\n <button\n :ref=\"(element) => setDayRef(element, cell.key)\"\n type=\"button\"\n class=\"t-date-picker__day\"\n :class=\"{\n 'is-muted': !cell.inMonth,\n 'is-selected': cell.isSelected,\n 'is-today': cell.isToday,\n }\"\n :data-date=\"cell.key\"\n :tabindex=\"cell.isFocused ? 0 : -1\"\n :disabled=\"cell.disabled\"\n :aria-pressed=\"cell.isSelected\"\n @click=\"selectDate(cell.date)\"\n @keydown=\"onDayKeydown($event, cell.date)\"\n >\n {{ cell.dayLabel }}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"t-date-time-picker__summary\">\n <p class=\"t-date-time-picker__eyebrow\">\n Selected slot\n </p>\n <p class=\"t-date-time-picker__summary-value\">\n {{ draftPreview }}\n </p>\n </div>\n\n <div class=\"t-date-time-picker__time\">\n <label class=\"t-date-time-picker__time-field\">\n <span class=\"t-date-time-picker__time-label\">Hour</span>\n <select\n class=\"t-date-time-picker__select\"\n :value=\"draftDateTime.getHours()\"\n aria-label=\"Select hour\"\n @change=\"updateHour\"\n >\n <option\n v-for=\"option in hourOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ option.label }}\n </option>\n </select>\n </label>\n\n <label class=\"t-date-time-picker__time-field\">\n <span class=\"t-date-time-picker__time-label\">Minute</span>\n <select\n class=\"t-date-time-picker__select\"\n :value=\"draftDateTime.getMinutes()\"\n aria-label=\"Select minute\"\n @change=\"updateMinute\"\n >\n <option\n v-for=\"option in minuteOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ option.label }}\n </option>\n </select>\n </label>\n </div>\n\n <div class=\"t-date-time-picker__footer\">\n <button\n type=\"button\"\n class=\"t-button t-button--ghost t-button--sm\"\n @click=\"clearSelection\"\n >\n Clear\n </button>\n <button\n type=\"button\"\n class=\"t-button t-button--solid t-button--sm\"\n @click=\"commitDraft\"\n >\n Apply\n </button>\n </div>\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"TDateTimePicker.js","names":[],"sources":["../../src/components/TDateTimePicker.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n addDays,\n addMonths,\n createId,\n isActivationKey,\n isEscapeKey,\n isSameDay,\n startOfDay,\n startOfMonth,\n startOfWeek,\n toDateValue,\n} from '@treeui/utils';\nimport { getTreeIcon, treeIconDefaults } from '@treeui/icons';\nimport {\n type ComponentPublicInstance,\n computed,\n nextTick,\n onBeforeUnmount,\n ref,\n toRef,\n useAttrs,\n watch,\n} from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSize } from '../types/contracts';\nimport TSpinner from './TSpinner.vue';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\ninterface CalendarCell {\n date: Date;\n dayLabel: number;\n disabled: boolean;\n inMonth: boolean;\n isFocused: boolean;\n isSelected: boolean;\n isToday: boolean;\n key: string;\n}\n\ninterface TimeOption {\n disabled: boolean;\n label: string;\n value: number;\n}\n\nconst CalendarIcon = computed(() => getTreeIcon('calendar'));\nconst ChevronLeftIcon = computed(() => getTreeIcon('chevron-left'));\nconst ChevronRightIcon = computed(() => getTreeIcon('chevron-right'));\n\nconst dateOnlyPattern = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst dateTimePattern = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::(\\d{2}))?$/;\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n open?: boolean;\n defaultOpen?: boolean;\n size?: TSize;\n disabled?: boolean;\n loading?: boolean;\n invalid?: boolean;\n placeholder?: string;\n locale?: string;\n min?: string;\n max?: string;\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;\n id?: string;\n step?: number;\n }>(),\n {\n modelValue: '',\n open: undefined,\n defaultOpen: false,\n size: 'md',\n disabled: false,\n loading: false,\n invalid: false,\n placeholder: 'Select date and time',\n locale: 'en-US',\n min: '',\n max: '',\n weekStartsOn: 0,\n id: undefined,\n step: 60,\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n change: [value: string];\n select: [value: string];\n}>();\n\nconst attrs = useAttrs();\n\nconst cloneDate = (value: Date) => new Date(value.getTime());\n\nconst formatPart = (value: number) => `${value}`.padStart(2, '0');\n\nconst endOfDay = (date: Date) =>\n new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);\n\nconst parseBoundaryValue = (\n value: string | null | undefined,\n boundary: 'exact' | 'start' | 'end' = 'exact',\n) => {\n if (!value) {\n return null;\n }\n\n const dateOnlyMatch = value.match(dateOnlyPattern);\n if (dateOnlyMatch) {\n const [, yearToken, monthToken, dayToken] = dateOnlyMatch;\n const year = Number.parseInt(yearToken, 10);\n const month = Number.parseInt(monthToken, 10);\n const day = Number.parseInt(dayToken, 10);\n const hour = boundary === 'end' ? 23 : 0;\n const minute = boundary === 'end' ? 59 : 0;\n const second = boundary === 'end' ? 59 : 0;\n const millisecond = boundary === 'end' ? 999 : 0;\n const date = new Date(year, month - 1, day, hour, minute, second, millisecond);\n\n if (\n Number.isNaN(date.getTime()) ||\n date.getFullYear() !== year ||\n date.getMonth() !== month - 1 ||\n date.getDate() !== day\n ) {\n return null;\n }\n\n return date;\n }\n\n const dateTimeMatch = value.match(dateTimePattern);\n if (!dateTimeMatch) {\n return null;\n }\n\n const [, yearToken, monthToken, dayToken, hourToken, minuteToken, secondToken] =\n dateTimeMatch;\n const year = Number.parseInt(yearToken, 10);\n const month = Number.parseInt(monthToken, 10);\n const day = Number.parseInt(dayToken, 10);\n const hour = Number.parseInt(hourToken, 10);\n const minute = Number.parseInt(minuteToken, 10);\n const second = secondToken ? Number.parseInt(secondToken, 10) : 0;\n const date = new Date(year, month - 1, day, hour, minute, second, 0);\n\n if (\n Number.isNaN(date.getTime()) ||\n date.getFullYear() !== year ||\n date.getMonth() !== month - 1 ||\n date.getDate() !== day ||\n date.getHours() !== hour ||\n date.getMinutes() !== minute ||\n date.getSeconds() !== second\n ) {\n return null;\n }\n\n return date;\n};\n\nconst toDateTimeValue = (date: Date) =>\n `${toDateValue(date)}T${formatPart(date.getHours())}:${formatPart(date.getMinutes())}`;\n\nconst setDatePart = (source: Date, targetDate: Date) =>\n new Date(\n targetDate.getFullYear(),\n targetDate.getMonth(),\n targetDate.getDate(),\n source.getHours(),\n source.getMinutes(),\n 0,\n 0,\n );\n\nconst setTimePart = (source: Date, hour: number, minute: number) =>\n new Date(\n source.getFullYear(),\n source.getMonth(),\n source.getDate(),\n hour,\n minute,\n 0,\n 0,\n );\n\nconst resolveMinuteStep = (step: number) => {\n if (!Number.isFinite(step) || step <= 0 || step % 60 !== 0) {\n return 1;\n }\n\n const minuteStep = step / 60;\n return minuteStep >= 60 ? 1 : minuteStep;\n};\n\nconst today = startOfDay(new Date());\nconst selectedDateTime = computed(() => parseBoundaryValue(props.modelValue, 'exact'));\nconst minDateTime = computed(() => parseBoundaryValue(props.min, 'start'));\nconst maxDateTime = computed(() => parseBoundaryValue(props.max, 'end'));\nconst pickerId = props.id ?? createId('t-date-time-picker');\nconst captionId = `${pickerId}-caption`;\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst dayRefs = new Map<string, HTMLButtonElement>();\n\nconst clampToRange = (value: Date) => {\n let nextValue = cloneDate(value);\n\n if (minDateTime.value && nextValue < minDateTime.value) {\n nextValue = cloneDate(minDateTime.value);\n }\n\n if (maxDateTime.value && nextValue > maxDateTime.value) {\n nextValue = cloneDate(maxDateTime.value);\n }\n\n return nextValue;\n};\n\nconst createDefaultDraftDateTime = () => {\n const reference = new Date();\n const stepMilliseconds = Math.max(props.step, 60) * 1000;\n const rounded = new Date(Math.ceil(reference.getTime() / stepMilliseconds) * stepMilliseconds);\n\n rounded.setSeconds(0, 0);\n return clampToRange(rounded);\n};\n\nconst initialDateTime = selectedDateTime.value ?? createDefaultDraftDateTime();\nconst draftDateTime = ref(initialDateTime);\nconst focusedDate = ref(startOfDay(initialDateTime));\nconst viewMonth = ref(startOfMonth(initialDateTime));\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, name: _name, ...rest } = attrs;\n return rest;\n});\n\nconst hiddenInputName = computed(() =>\n typeof attrs.name === 'string' ? attrs.name : undefined,\n);\n\nconst rootClasses = computed(() => [\n 't-date-picker',\n 't-date-time-picker',\n `t-date-picker--${props.size}`,\n {\n 'is-disabled': props.disabled,\n 'is-invalid': props.invalid,\n 'is-loading': props.loading,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst triggerText = computed(() => {\n if (!selectedDateTime.value) {\n return props.placeholder;\n }\n\n return new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n month: 'long',\n year: 'numeric',\n }).format(selectedDateTime.value);\n});\n\nconst draftPreview = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n month: 'long',\n year: 'numeric',\n }).format(draftDateTime.value),\n);\n\nconst monthLabel = computed(() =>\n new Intl.DateTimeFormat(props.locale, {\n month: 'long',\n year: 'numeric',\n }).format(viewMonth.value),\n);\n\nconst weekdayLabels = computed(() => {\n const formatter = new Intl.DateTimeFormat(props.locale, { weekday: 'short' });\n const weekBase = startOfWeek(new Date(2024, 0, 7), props.weekStartsOn);\n\n return Array.from({ length: 7 }, (_, index) =>\n formatter.format(addDays(weekBase, index)),\n );\n});\n\nconst minuteStep = computed(() => resolveMinuteStep(props.step));\n\nconst minuteValues = computed(() => {\n const values = new Set<number>();\n\n for (let minute = 0; minute < 60; minute += minuteStep.value) {\n values.add(minute);\n }\n\n values.add(draftDateTime.value.getMinutes());\n\n return [...values].sort((left, right) => left - right);\n});\n\nconst isDateDisabled = (date: Date) => {\n if (props.disabled) {\n return true;\n }\n\n const dayStart = startOfDay(date);\n const dayEnd = endOfDay(date);\n\n if (minDateTime.value && dayEnd < minDateTime.value) {\n return true;\n }\n\n if (maxDateTime.value && dayStart > maxDateTime.value) {\n return true;\n }\n\n return false;\n};\n\nconst isDateTimeDisabled = (value: Date) => {\n if (props.disabled) {\n return true;\n }\n\n if (minDateTime.value && value < minDateTime.value) {\n return true;\n }\n\n if (maxDateTime.value && value > maxDateTime.value) {\n return true;\n }\n\n return false;\n};\n\nconst syncCalendarView = (date: Date) => {\n focusedDate.value = startOfDay(date);\n viewMonth.value = startOfMonth(date);\n};\n\nconst syncDraftState = (dateTime: Date) => {\n const nextValue = clampToRange(dateTime);\n draftDateTime.value = nextValue;\n syncCalendarView(nextValue);\n};\n\nwatch(\n selectedDateTime,\n (value) => {\n if (isOpen.value) {\n return;\n }\n\n syncDraftState(value ?? createDefaultDraftDateTime());\n },\n { immediate: true },\n);\n\nconst draftDate = computed(() => startOfDay(draftDateTime.value));\n\nconst cells = computed(() => {\n const startDate = startOfWeek(startOfMonth(viewMonth.value), props.weekStartsOn);\n\n return Array.from({ length: 6 }, (_, weekIndex) =>\n Array.from({ length: 7 }, (_, dayIndex) => {\n const date = addDays(startDate, weekIndex * 7 + dayIndex);\n const key = toDateValue(date);\n\n return {\n date,\n dayLabel: date.getDate(),\n disabled: isDateDisabled(date),\n inMonth: date.getMonth() === viewMonth.value.getMonth(),\n isFocused: isSameDay(date, focusedDate.value),\n isSelected: isSameDay(date, draftDate.value),\n isToday: isSameDay(date, today),\n key,\n } satisfies CalendarCell;\n }),\n );\n});\n\nconst canMovePrev = computed(() => {\n if (!minDateTime.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) > startOfMonth(minDateTime.value);\n});\n\nconst canMoveNext = computed(() => {\n if (!maxDateTime.value) {\n return true;\n }\n\n return startOfMonth(viewMonth.value) < startOfMonth(maxDateTime.value);\n});\n\nconst isTimeOptionEnabled = (hour: number, minute: number) => {\n const candidate = setTimePart(draftDateTime.value, hour, minute);\n return !isDateTimeDisabled(candidate);\n};\n\nconst hourOptions = computed<TimeOption[]>(() =>\n Array.from({ length: 24 }, (_, hour) => ({\n disabled: !minuteValues.value.some((minute) => isTimeOptionEnabled(hour, minute)),\n label: formatPart(hour),\n value: hour,\n })),\n);\n\nconst minuteOptions = computed<TimeOption[]>(() =>\n minuteValues.value.map((minute) => ({\n disabled: !isTimeOptionEnabled(draftDateTime.value.getHours(), minute),\n label: formatPart(minute),\n value: minute,\n })),\n);\n\nconst focusCurrentDay = () => {\n const key = toDateValue(focusedDate.value);\n nextTick(() => {\n dayRefs.get(key)?.focus();\n });\n};\n\nconst setDayRef = (\n element: Element | ComponentPublicInstance | null,\n key: string,\n) => {\n if (element instanceof HTMLButtonElement) {\n dayRefs.set(key, element);\n return;\n }\n\n dayRefs.delete(key);\n};\n\nconst openCalendar = () => {\n if (props.disabled) {\n return;\n }\n\n syncDraftState(selectedDateTime.value ?? createDefaultDraftDateTime());\n setValue(true);\n focusCurrentDay();\n};\n\nconst closeCalendar = (restoreFocus = false) => {\n setValue(false);\n\n if (restoreFocus) {\n nextTick(() => {\n triggerRef.value?.focus();\n });\n }\n};\n\nconst toggleCalendar = () => {\n if (isOpen.value) {\n closeCalendar();\n return;\n }\n\n openCalendar();\n};\n\nconst moveCalendar = (direction: 1 | -1) => {\n const nextMonth = addMonths(viewMonth.value, direction);\n syncCalendarView(nextMonth);\n focusCurrentDay();\n};\n\nconst moveFocus = (nextDate: Date) => {\n if (isDateDisabled(nextDate)) {\n return;\n }\n\n syncCalendarView(nextDate);\n focusCurrentDay();\n};\n\nconst selectDate = (date: Date) => {\n if (isDateDisabled(date)) {\n return;\n }\n\n const candidate = clampToRange(setDatePart(draftDateTime.value, date));\n draftDateTime.value = candidate;\n syncCalendarView(candidate);\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) {\n return;\n }\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeCalendar();\n return;\n }\n\n if (\n isActivationKey(event) ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowUp'\n ) {\n event.preventDefault();\n openCalendar();\n }\n};\n\nconst onDayKeydown = (event: KeyboardEvent, date: Date) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeCalendar(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectDate(date);\n return;\n }\n\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault();\n moveFocus(addDays(date, -1));\n break;\n case 'ArrowRight':\n event.preventDefault();\n moveFocus(addDays(date, 1));\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(addDays(date, -7));\n break;\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(addDays(date, 7));\n break;\n case 'Home':\n event.preventDefault();\n moveFocus(startOfWeek(date, props.weekStartsOn));\n break;\n case 'End':\n event.preventDefault();\n moveFocus(addDays(startOfWeek(date, props.weekStartsOn), 6));\n break;\n case 'PageUp':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? -12 : -1));\n break;\n case 'PageDown':\n event.preventDefault();\n moveFocus(addMonths(date, event.shiftKey ? 12 : 1));\n break;\n default:\n break;\n }\n};\n\nconst updateHour = (event: Event) => {\n const nextHour = Number.parseInt((event.target as HTMLSelectElement).value, 10);\n const currentMinute = draftDateTime.value.getMinutes();\n\n if (Number.isNaN(nextHour)) {\n return;\n }\n\n const availableMinutes = minuteValues.value.filter((minute) =>\n isTimeOptionEnabled(nextHour, minute),\n );\n\n if (!availableMinutes.length) {\n return;\n }\n\n const nextMinute = availableMinutes.includes(currentMinute)\n ? currentMinute\n : availableMinutes[0];\n\n draftDateTime.value = setTimePart(draftDateTime.value, nextHour, nextMinute);\n};\n\nconst updateMinute = (event: Event) => {\n const nextMinute = Number.parseInt((event.target as HTMLSelectElement).value, 10);\n const nextHour = draftDateTime.value.getHours();\n\n if (Number.isNaN(nextMinute) || !isTimeOptionEnabled(nextHour, nextMinute)) {\n return;\n }\n\n draftDateTime.value = setTimePart(draftDateTime.value, nextHour, nextMinute);\n};\n\nconst commitDraft = () => {\n const value = toDateTimeValue(draftDateTime.value);\n\n emit('update:modelValue', value);\n emit('change', value);\n emit('select', value);\n closeCalendar(true);\n};\n\nconst clearSelection = () => {\n emit('update:modelValue', '');\n emit('change', '');\n emit('select', '');\n closeCalendar(true);\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) {\n return;\n }\n\n const target = event.target;\n if (!(target instanceof Node)) {\n return;\n }\n\n if (rootRef.value?.contains(target)) {\n return;\n }\n\n closeCalendar();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n return;\n }\n\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <input\n v-if=\"hiddenInputName\"\n type=\"hidden\"\n :name=\"hiddenInputName\"\n :value=\"modelValue\"\n >\n\n <button\n ref=\"triggerRef\"\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-date-picker__trigger t-date-time-picker__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? pickerId : undefined\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :aria-invalid=\"invalid || undefined\"\n @click=\"toggleCalendar\"\n @keydown=\"onTriggerKeydown\"\n >\n <span\n v-if=\"$slots.prefix\"\n class=\"t-date-picker__slot t-date-picker__slot--prefix\"\n >\n <slot name=\"prefix\" />\n </span>\n\n <span\n class=\"t-date-picker__value\"\n :data-placeholder=\"selectedDateTime ? undefined : true\"\n >\n {{ triggerText }}\n </span>\n\n <span class=\"t-date-picker__slot t-date-picker__slot--suffix\">\n <slot name=\"suffix\">\n <TSpinner\n v-if=\"loading\"\n size=\"sm\"\n label=\"Loading\"\n />\n <CalendarIcon\n v-else\n v-bind=\"treeIconDefaults\"\n />\n </slot>\n </span>\n </button>\n\n <transition name=\"t-fade\">\n <div\n v-if=\"isOpen && !disabled\"\n :id=\"pickerId\"\n class=\"t-date-picker__content t-date-time-picker__content\"\n role=\"dialog\"\n aria-modal=\"false\"\n :aria-labelledby=\"captionId\"\n >\n <div class=\"t-date-time-picker__calendar\">\n <div class=\"t-date-picker__header\">\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMovePrev\"\n aria-label=\"Previous month\"\n @click=\"moveCalendar(-1)\"\n >\n <ChevronLeftIcon v-bind=\"treeIconDefaults\" />\n </button>\n\n <p\n :id=\"captionId\"\n class=\"t-date-picker__month\"\n >\n {{ monthLabel }}\n </p>\n\n <button\n type=\"button\"\n class=\"t-date-picker__nav\"\n :disabled=\"!canMoveNext\"\n aria-label=\"Next month\"\n @click=\"moveCalendar(1)\"\n >\n <ChevronRightIcon v-bind=\"treeIconDefaults\" />\n </button>\n </div>\n\n <div\n class=\"t-date-picker__weekdays\"\n aria-hidden=\"true\"\n >\n <span\n v-for=\"label in weekdayLabels\"\n :key=\"label\"\n class=\"t-date-picker__weekday\"\n >\n {{ label }}\n </span>\n </div>\n\n <div\n class=\"t-date-picker__grid\"\n role=\"grid\"\n :aria-labelledby=\"captionId\"\n >\n <div\n v-for=\"(week, weekIndex) in cells\"\n :key=\"weekIndex\"\n class=\"t-date-picker__row\"\n role=\"row\"\n >\n <div\n v-for=\"cell in week\"\n :key=\"cell.key\"\n class=\"t-date-picker__cell\"\n role=\"gridcell\"\n :aria-selected=\"cell.isSelected\"\n >\n <button\n :ref=\"(element) => setDayRef(element, cell.key)\"\n type=\"button\"\n class=\"t-date-picker__day\"\n :class=\"{\n 'is-muted': !cell.inMonth,\n 'is-selected': cell.isSelected,\n 'is-today': cell.isToday,\n }\"\n :data-date=\"cell.key\"\n :tabindex=\"cell.isFocused ? 0 : -1\"\n :disabled=\"cell.disabled\"\n :aria-pressed=\"cell.isSelected\"\n @click=\"selectDate(cell.date)\"\n @keydown=\"onDayKeydown($event, cell.date)\"\n >\n {{ cell.dayLabel }}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"t-date-time-picker__summary\">\n <p class=\"t-date-time-picker__eyebrow\">\n Selected slot\n </p>\n <p class=\"t-date-time-picker__summary-value\">\n {{ draftPreview }}\n </p>\n </div>\n\n <div class=\"t-date-time-picker__time\">\n <label class=\"t-date-time-picker__time-field\">\n <span class=\"t-date-time-picker__time-label\">Hour</span>\n <select\n class=\"t-date-time-picker__select\"\n :value=\"draftDateTime.getHours()\"\n aria-label=\"Select hour\"\n @change=\"updateHour\"\n >\n <option\n v-for=\"option in hourOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ option.label }}\n </option>\n </select>\n </label>\n\n <label class=\"t-date-time-picker__time-field\">\n <span class=\"t-date-time-picker__time-label\">Minute</span>\n <select\n class=\"t-date-time-picker__select\"\n :value=\"draftDateTime.getMinutes()\"\n aria-label=\"Select minute\"\n @change=\"updateMinute\"\n >\n <option\n v-for=\"option in minuteOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ option.label }}\n </option>\n </select>\n </label>\n </div>\n\n <div class=\"t-date-time-picker__footer\">\n <button\n type=\"button\"\n class=\"t-button t-button--ghost t-button--sm\"\n @click=\"clearSelection\"\n >\n Clear\n </button>\n <button\n type=\"button\"\n class=\"t-button t-button--solid t-button--sm\"\n @click=\"commitDraft\"\n >\n Apply\n </button>\n </div>\n </div>\n </transition>\n </div>\n</template>\n"],"mappings":""}
@@ -1,2 +1,2 @@
1
- const e=require("./TSpinner.cjs"),t=require("../composables/useControllableOpen.cjs");let n=require("vue"),r=require("@treeui/utils"),i=require("@treeui/icons");var a=[`data-state`],o=[`name`,`value`],s=[`disabled`,`aria-controls`,`aria-expanded`,`aria-invalid`],c={key:0,class:`t-date-picker__slot t-date-picker__slot--prefix`},l=[`data-placeholder`],u={class:`t-date-picker__slot t-date-picker__slot--suffix`},d=[`id`],f={class:`t-date-time-picker__calendar`},p={class:`t-date-picker__header`},m=[`disabled`],h=[`disabled`],g={class:`t-date-picker__weekdays`,"aria-hidden":`true`},ee=[`aria-selected`],te=[`data-date`,`tabindex`,`disabled`,`aria-pressed`,`onClick`,`onKeydown`],ne={class:`t-date-time-picker__summary`},re={class:`t-date-time-picker__summary-value`},ie={class:`t-date-time-picker__time`},ae={class:`t-date-time-picker__time-field`},oe=[`value`],se=[`value`,`disabled`],ce={class:`t-date-time-picker__time-field`},le=[`value`],ue=[`value`,`disabled`],_=(0,n.defineComponent)({inheritAttrs:!1,__name:`TDateTimePicker`,props:{modelValue:{default:``},open:{type:Boolean,default:void 0},defaultOpen:{type:Boolean,default:!1},size:{default:`md`},disabled:{type:Boolean,default:!1},loading:{type:Boolean,default:!1},invalid:{type:Boolean,default:!1},placeholder:{default:`Select date and time`},locale:{default:`en-US`},min:{default:``},max:{default:``},weekStartsOn:{default:0},id:{default:void 0},step:{default:60}},emits:[`update:modelValue`,`update:open`,`open-change`,`change`,`select`],setup(_,{emit:de}){let fe=(0,i.getTreeIcon)(`calendar`),pe=(0,i.getTreeIcon)(`chevron-left`),me=(0,i.getTreeIcon)(`chevron-right`),he=/^(\d{4})-(\d{2})-(\d{2})$/,ge=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/,v=_,y=de,b=(0,n.useAttrs)(),x=e=>new Date(e.getTime()),S=e=>`${e}`.padStart(2,`0`),_e=e=>new Date(e.getFullYear(),e.getMonth(),e.getDate(),23,59,59,999),C=(e,t=`exact`)=>{if(!e)return null;let n=e.match(he);if(n){let[,e,r,i]=n,a=Number.parseInt(e,10),o=Number.parseInt(r,10),s=Number.parseInt(i,10),c=t===`end`?23:0,l=t===`end`?59:0,u=t===`end`?59:0,d=t===`end`?999:0,f=new Date(a,o-1,s,c,l,u,d);return Number.isNaN(f.getTime())||f.getFullYear()!==a||f.getMonth()!==o-1||f.getDate()!==s?null:f}let r=e.match(ge);if(!r)return null;let[,i,a,o,s,c,l]=r,u=Number.parseInt(i,10),d=Number.parseInt(a,10),f=Number.parseInt(o,10),p=Number.parseInt(s,10),m=Number.parseInt(c,10),h=l?Number.parseInt(l,10):0,g=new Date(u,d-1,f,p,m,h,0);return Number.isNaN(g.getTime())||g.getFullYear()!==u||g.getMonth()!==d-1||g.getDate()!==f||g.getHours()!==p||g.getMinutes()!==m||g.getSeconds()!==h?null:g},ve=e=>`${(0,r.toDateValue)(e)}T${S(e.getHours())}:${S(e.getMinutes())}`,ye=(e,t)=>new Date(t.getFullYear(),t.getMonth(),t.getDate(),e.getHours(),e.getMinutes(),0,0),w=(e,t,n)=>new Date(e.getFullYear(),e.getMonth(),e.getDate(),t,n,0,0),be=e=>{if(!Number.isFinite(e)||e<=0||e%60!=0)return 1;let t=e/60;return t>=60?1:t},xe=(0,r.startOfDay)(new Date),T=(0,n.computed)(()=>C(v.modelValue,`exact`)),E=(0,n.computed)(()=>C(v.min,`start`)),D=(0,n.computed)(()=>C(v.max,`end`)),O=v.id??(0,r.createId)(`t-date-time-picker`),k=`${O}-caption`,A=(0,n.ref)(null),j=(0,n.ref)(null),M=new Map,N=e=>{let t=x(e);return E.value&&t<E.value&&(t=x(E.value)),D.value&&t>D.value&&(t=x(D.value)),t},P=()=>{let e=new Date,t=Math.max(v.step,60)*1e3,n=new Date(Math.ceil(e.getTime()/t)*t);return n.setSeconds(0,0),N(n)},F=T.value??P(),I=(0,n.ref)(F),L=(0,n.ref)((0,r.startOfDay)(F)),R=(0,n.ref)((0,r.startOfMonth)(F)),Se=(0,n.computed)(()=>{let{class:e,style:t,name:n,...r}=b;return r}),z=(0,n.computed)(()=>typeof b.name==`string`?b.name:void 0),Ce=(0,n.computed)(()=>[`t-date-picker`,`t-date-time-picker`,`t-date-picker--${v.size}`,{"is-disabled":v.disabled,"is-invalid":v.invalid,"is-loading":v.loading,"is-open":B.value},b.class]),we=(0,n.computed)(()=>b.style),{value:B,setValue:V}=t.useControllableOpen((0,n.toRef)(v,`open`),v.defaultOpen,e=>{y(`update:open`,e),y(`open-change`,e)}),Te=(0,n.computed)(()=>T.value?new Intl.DateTimeFormat(v.locale,{day:`numeric`,hour:`2-digit`,minute:`2-digit`,month:`long`,year:`numeric`}).format(T.value):v.placeholder),Ee=(0,n.computed)(()=>new Intl.DateTimeFormat(v.locale,{day:`numeric`,hour:`2-digit`,minute:`2-digit`,month:`long`,year:`numeric`}).format(I.value)),De=(0,n.computed)(()=>new Intl.DateTimeFormat(v.locale,{month:`long`,year:`numeric`}).format(R.value)),Oe=(0,n.computed)(()=>{let e=new Intl.DateTimeFormat(v.locale,{weekday:`short`}),t=(0,r.startOfWeek)(new Date(2024,0,7),v.weekStartsOn);return Array.from({length:7},(n,i)=>e.format((0,r.addDays)(t,i)))}),ke=(0,n.computed)(()=>be(v.step)),H=(0,n.computed)(()=>{let e=new Set;for(let t=0;t<60;t+=ke.value)e.add(t);return e.add(I.value.getMinutes()),[...e].sort((e,t)=>e-t)}),U=e=>{if(v.disabled)return!0;let t=(0,r.startOfDay)(e),n=_e(e);return!!(E.value&&n<E.value||D.value&&t>D.value)},Ae=e=>!!(v.disabled||E.value&&e<E.value||D.value&&e>D.value),W=e=>{L.value=(0,r.startOfDay)(e),R.value=(0,r.startOfMonth)(e)},G=e=>{let t=N(e);I.value=t,W(t)};(0,n.watch)(T,e=>{B.value||G(e??P())},{immediate:!0});let je=(0,n.computed)(()=>(0,r.startOfDay)(I.value)),Me=(0,n.computed)(()=>{let e=(0,r.startOfWeek)((0,r.startOfMonth)(R.value),v.weekStartsOn);return Array.from({length:6},(t,n)=>Array.from({length:7},(t,i)=>{let a=(0,r.addDays)(e,n*7+i),o=(0,r.toDateValue)(a);return{date:a,dayLabel:a.getDate(),disabled:U(a),inMonth:a.getMonth()===R.value.getMonth(),isFocused:(0,r.isSameDay)(a,L.value),isSelected:(0,r.isSameDay)(a,je.value),isToday:(0,r.isSameDay)(a,xe),key:o}}))}),Ne=(0,n.computed)(()=>!E.value||(0,r.startOfMonth)(R.value)>(0,r.startOfMonth)(E.value)),Pe=(0,n.computed)(()=>!D.value||(0,r.startOfMonth)(R.value)<(0,r.startOfMonth)(D.value)),K=(e,t)=>{let n=w(I.value,e,t);return!Ae(n)},Fe=(0,n.computed)(()=>Array.from({length:24},(e,t)=>({disabled:!H.value.some(e=>K(t,e)),label:S(t),value:t}))),Ie=(0,n.computed)(()=>H.value.map(e=>({disabled:!K(I.value.getHours(),e),label:S(e),value:e}))),q=()=>{let e=(0,r.toDateValue)(L.value);(0,n.nextTick)(()=>{M.get(e)?.focus()})},Le=(e,t)=>{if(e instanceof HTMLButtonElement){M.set(t,e);return}M.delete(t)},J=()=>{v.disabled||(G(T.value??P()),V(!0),q())},Y=(e=!1)=>{V(!1),e&&(0,n.nextTick)(()=>{j.value?.focus()})},Re=()=>{if(B.value){Y();return}J()},X=e=>{let t=(0,r.addMonths)(R.value,e);W(t),q()},Z=e=>{U(e)||(W(e),q())},Q=e=>{if(U(e))return;let t=N(ye(I.value,e));I.value=t,W(t)},ze=e=>{if(!v.disabled){if((0,r.isEscapeKey)(e)&&B.value){e.preventDefault(),Y();return}((0,r.isActivationKey)(e)||e.key===`ArrowDown`||e.key===`ArrowUp`)&&(e.preventDefault(),J())}},Be=(e,t)=>{if((0,r.isEscapeKey)(e)){e.preventDefault(),Y(!0);return}if((0,r.isActivationKey)(e)){e.preventDefault(),Q(t);return}switch(e.key){case`ArrowLeft`:e.preventDefault(),Z((0,r.addDays)(t,-1));break;case`ArrowRight`:e.preventDefault(),Z((0,r.addDays)(t,1));break;case`ArrowUp`:e.preventDefault(),Z((0,r.addDays)(t,-7));break;case`ArrowDown`:e.preventDefault(),Z((0,r.addDays)(t,7));break;case`Home`:e.preventDefault(),Z((0,r.startOfWeek)(t,v.weekStartsOn));break;case`End`:e.preventDefault(),Z((0,r.addDays)((0,r.startOfWeek)(t,v.weekStartsOn),6));break;case`PageUp`:e.preventDefault(),Z((0,r.addMonths)(t,e.shiftKey?-12:-1));break;case`PageDown`:e.preventDefault(),Z((0,r.addMonths)(t,e.shiftKey?12:1));break;default:break}},Ve=e=>{let t=Number.parseInt(e.target.value,10),n=I.value.getMinutes();if(Number.isNaN(t))return;let r=H.value.filter(e=>K(t,e));if(!r.length)return;let i=r.includes(n)?n:r[0];I.value=w(I.value,t,i)},He=e=>{let t=Number.parseInt(e.target.value,10),n=I.value.getHours();Number.isNaN(t)||!K(n,t)||(I.value=w(I.value,n,t))},Ue=()=>{let e=ve(I.value);y(`update:modelValue`,e),y(`change`,e),y(`select`,e),Y(!0)},We=()=>{y(`update:modelValue`,``),y(`change`,``),y(`select`,``),Y(!0)},$=e=>{if(!B.value)return;let t=e.target;t instanceof Node&&(A.value?.contains(t)||Y())};return(0,n.watch)(B,e=>{if(e){document.addEventListener(`pointerdown`,$);return}document.removeEventListener(`pointerdown`,$)}),(0,n.onBeforeUnmount)(()=>{document.removeEventListener(`pointerdown`,$)}),(t,r)=>((0,n.openBlock)(),(0,n.createElementBlock)(`div`,{ref_key:`rootRef`,ref:A,class:(0,n.normalizeClass)(Ce.value),style:(0,n.normalizeStyle)(we.value),"data-state":(0,n.unref)(B)?`open`:`closed`},[z.value?((0,n.openBlock)(),(0,n.createElementBlock)(`input`,{key:0,type:`hidden`,name:z.value,value:_.modelValue},null,8,o)):(0,n.createCommentVNode)(``,!0),(0,n.createElementVNode)(`button`,(0,n.mergeProps)({ref_key:`triggerRef`,ref:j},Se.value,{type:`button`,class:`t-date-picker__trigger t-date-time-picker__trigger`,disabled:_.disabled,"aria-controls":(0,n.unref)(B)?(0,n.unref)(O):void 0,"aria-haspopup":`dialog`,"aria-expanded":(0,n.unref)(B),"aria-invalid":_.invalid||void 0,onClick:Re,onKeydown:ze}),[t.$slots.prefix?((0,n.openBlock)(),(0,n.createElementBlock)(`span`,c,[(0,n.renderSlot)(t.$slots,`prefix`)])):(0,n.createCommentVNode)(``,!0),(0,n.createElementVNode)(`span`,{class:`t-date-picker__value`,"data-placeholder":!T.value||void 0},(0,n.toDisplayString)(Te.value),9,l),(0,n.createElementVNode)(`span`,u,[(0,n.renderSlot)(t.$slots,`suffix`,{},()=>[_.loading?((0,n.openBlock)(),(0,n.createBlock)(e.default,{key:0,size:`sm`,label:`Loading`})):((0,n.openBlock)(),(0,n.createBlock)((0,n.unref)(fe),(0,n.normalizeProps)((0,n.mergeProps)({key:1},(0,n.unref)(i.treeIconDefaults))),null,16))])])],16,s),(0,n.createVNode)(n.Transition,{name:`t-fade`},{default:(0,n.withCtx)(()=>[(0,n.unref)(B)&&!_.disabled?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,{key:0,id:(0,n.unref)(O),class:`t-date-picker__content t-date-time-picker__content`,role:`dialog`,"aria-modal":`false`,"aria-labelledby":k},[(0,n.createElementVNode)(`div`,f,[(0,n.createElementVNode)(`div`,p,[(0,n.createElementVNode)(`button`,{type:`button`,class:`t-date-picker__nav`,disabled:!Ne.value,"aria-label":`Previous month`,onClick:r[0]||=e=>X(-1)},[(0,n.createVNode)((0,n.unref)(pe),(0,n.normalizeProps)((0,n.guardReactiveProps)((0,n.unref)(i.treeIconDefaults))),null,16)],8,m),(0,n.createElementVNode)(`p`,{id:k,class:`t-date-picker__month`},(0,n.toDisplayString)(De.value),1),(0,n.createElementVNode)(`button`,{type:`button`,class:`t-date-picker__nav`,disabled:!Pe.value,"aria-label":`Next month`,onClick:r[1]||=e=>X(1)},[(0,n.createVNode)((0,n.unref)(me),(0,n.normalizeProps)((0,n.guardReactiveProps)((0,n.unref)(i.treeIconDefaults))),null,16)],8,h)]),(0,n.createElementVNode)(`div`,g,[((0,n.openBlock)(!0),(0,n.createElementBlock)(n.Fragment,null,(0,n.renderList)(Oe.value,e=>((0,n.openBlock)(),(0,n.createElementBlock)(`span`,{key:e,class:`t-date-picker__weekday`},(0,n.toDisplayString)(e),1))),128))]),(0,n.createElementVNode)(`div`,{class:`t-date-picker__grid`,role:`grid`,"aria-labelledby":k},[((0,n.openBlock)(!0),(0,n.createElementBlock)(n.Fragment,null,(0,n.renderList)(Me.value,(e,t)=>((0,n.openBlock)(),(0,n.createElementBlock)(`div`,{key:t,class:`t-date-picker__row`,role:`row`},[((0,n.openBlock)(!0),(0,n.createElementBlock)(n.Fragment,null,(0,n.renderList)(e,e=>((0,n.openBlock)(),(0,n.createElementBlock)(`div`,{key:e.key,class:`t-date-picker__cell`,role:`gridcell`,"aria-selected":e.isSelected},[(0,n.createElementVNode)(`button`,{ref_for:!0,ref:t=>Le(t,e.key),type:`button`,class:(0,n.normalizeClass)([`t-date-picker__day`,{"is-muted":!e.inMonth,"is-selected":e.isSelected,"is-today":e.isToday}]),"data-date":e.key,tabindex:e.isFocused?0:-1,disabled:e.disabled,"aria-pressed":e.isSelected,onClick:t=>Q(e.date),onKeydown:t=>Be(t,e.date)},(0,n.toDisplayString)(e.dayLabel),43,te)],8,ee))),128))]))),128))])]),(0,n.createElementVNode)(`div`,ne,[r[2]||=(0,n.createElementVNode)(`p`,{class:`t-date-time-picker__eyebrow`},` Selected slot `,-1),(0,n.createElementVNode)(`p`,re,(0,n.toDisplayString)(Ee.value),1)]),(0,n.createElementVNode)(`div`,ie,[(0,n.createElementVNode)(`label`,ae,[r[3]||=(0,n.createElementVNode)(`span`,{class:`t-date-time-picker__time-label`},`Hour`,-1),(0,n.createElementVNode)(`select`,{class:`t-date-time-picker__select`,value:I.value.getHours(),"aria-label":`Select hour`,onChange:Ve},[((0,n.openBlock)(!0),(0,n.createElementBlock)(n.Fragment,null,(0,n.renderList)(Fe.value,e=>((0,n.openBlock)(),(0,n.createElementBlock)(`option`,{key:e.value,value:e.value,disabled:e.disabled},(0,n.toDisplayString)(e.label),9,se))),128))],40,oe)]),(0,n.createElementVNode)(`label`,ce,[r[4]||=(0,n.createElementVNode)(`span`,{class:`t-date-time-picker__time-label`},`Minute`,-1),(0,n.createElementVNode)(`select`,{class:`t-date-time-picker__select`,value:I.value.getMinutes(),"aria-label":`Select minute`,onChange:He},[((0,n.openBlock)(!0),(0,n.createElementBlock)(n.Fragment,null,(0,n.renderList)(Ie.value,e=>((0,n.openBlock)(),(0,n.createElementBlock)(`option`,{key:e.value,value:e.value,disabled:e.disabled},(0,n.toDisplayString)(e.label),9,ue))),128))],40,le)])]),(0,n.createElementVNode)(`div`,{class:`t-date-time-picker__footer`},[(0,n.createElementVNode)(`button`,{type:`button`,class:`t-button t-button--ghost t-button--sm`,onClick:We},` Clear `),(0,n.createElementVNode)(`button`,{type:`button`,class:`t-button t-button--solid t-button--sm`,onClick:Ue},` Apply `)])],8,d)):(0,n.createCommentVNode)(``,!0)]),_:1})],14,a))}});exports.default=_;
1
+ const e=require("./TSpinner.cjs"),t=require("../composables/useControllableOpen.cjs");let n=require("vue"),r=require("@treeui/utils"),i=require("@treeui/icons");var a=[`data-state`],o=[`name`,`value`],s=[`disabled`,`aria-controls`,`aria-expanded`,`aria-invalid`],c={key:0,class:`t-date-picker__slot t-date-picker__slot--prefix`},l=[`data-placeholder`],u={class:`t-date-picker__slot t-date-picker__slot--suffix`},d=[`id`],f={class:`t-date-time-picker__calendar`},p={class:`t-date-picker__header`},m=[`disabled`],h=[`disabled`],g={class:`t-date-picker__weekdays`,"aria-hidden":`true`},ee=[`aria-selected`],te=[`data-date`,`tabindex`,`disabled`,`aria-pressed`,`onClick`,`onKeydown`],ne={class:`t-date-time-picker__summary`},re={class:`t-date-time-picker__summary-value`},ie={class:`t-date-time-picker__time`},ae={class:`t-date-time-picker__time-field`},oe=[`value`],se=[`value`,`disabled`],ce={class:`t-date-time-picker__time-field`},le=[`value`],ue=[`value`,`disabled`],_=(0,n.defineComponent)({inheritAttrs:!1,__name:`TDateTimePicker`,props:{modelValue:{default:``},open:{type:Boolean,default:void 0},defaultOpen:{type:Boolean,default:!1},size:{default:`md`},disabled:{type:Boolean,default:!1},loading:{type:Boolean,default:!1},invalid:{type:Boolean,default:!1},placeholder:{default:`Select date and time`},locale:{default:`en-US`},min:{default:``},max:{default:``},weekStartsOn:{default:0},id:{default:void 0},step:{default:60}},emits:[`update:modelValue`,`update:open`,`open-change`,`change`,`select`],setup(_,{emit:de}){let fe=(0,n.computed)(()=>(0,i.getTreeIcon)(`calendar`)),pe=(0,n.computed)(()=>(0,i.getTreeIcon)(`chevron-left`)),me=(0,n.computed)(()=>(0,i.getTreeIcon)(`chevron-right`)),he=/^(\d{4})-(\d{2})-(\d{2})$/,ge=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/,v=_,y=de,b=(0,n.useAttrs)(),x=e=>new Date(e.getTime()),S=e=>`${e}`.padStart(2,`0`),_e=e=>new Date(e.getFullYear(),e.getMonth(),e.getDate(),23,59,59,999),C=(e,t=`exact`)=>{if(!e)return null;let n=e.match(he);if(n){let[,e,r,i]=n,a=Number.parseInt(e,10),o=Number.parseInt(r,10),s=Number.parseInt(i,10),c=t===`end`?23:0,l=t===`end`?59:0,u=t===`end`?59:0,d=t===`end`?999:0,f=new Date(a,o-1,s,c,l,u,d);return Number.isNaN(f.getTime())||f.getFullYear()!==a||f.getMonth()!==o-1||f.getDate()!==s?null:f}let r=e.match(ge);if(!r)return null;let[,i,a,o,s,c,l]=r,u=Number.parseInt(i,10),d=Number.parseInt(a,10),f=Number.parseInt(o,10),p=Number.parseInt(s,10),m=Number.parseInt(c,10),h=l?Number.parseInt(l,10):0,g=new Date(u,d-1,f,p,m,h,0);return Number.isNaN(g.getTime())||g.getFullYear()!==u||g.getMonth()!==d-1||g.getDate()!==f||g.getHours()!==p||g.getMinutes()!==m||g.getSeconds()!==h?null:g},ve=e=>`${(0,r.toDateValue)(e)}T${S(e.getHours())}:${S(e.getMinutes())}`,ye=(e,t)=>new Date(t.getFullYear(),t.getMonth(),t.getDate(),e.getHours(),e.getMinutes(),0,0),w=(e,t,n)=>new Date(e.getFullYear(),e.getMonth(),e.getDate(),t,n,0,0),be=e=>{if(!Number.isFinite(e)||e<=0||e%60!=0)return 1;let t=e/60;return t>=60?1:t},xe=(0,r.startOfDay)(new Date),T=(0,n.computed)(()=>C(v.modelValue,`exact`)),E=(0,n.computed)(()=>C(v.min,`start`)),D=(0,n.computed)(()=>C(v.max,`end`)),O=v.id??(0,r.createId)(`t-date-time-picker`),k=`${O}-caption`,A=(0,n.ref)(null),j=(0,n.ref)(null),M=new Map,N=e=>{let t=x(e);return E.value&&t<E.value&&(t=x(E.value)),D.value&&t>D.value&&(t=x(D.value)),t},P=()=>{let e=new Date,t=Math.max(v.step,60)*1e3,n=new Date(Math.ceil(e.getTime()/t)*t);return n.setSeconds(0,0),N(n)},F=T.value??P(),I=(0,n.ref)(F),L=(0,n.ref)((0,r.startOfDay)(F)),R=(0,n.ref)((0,r.startOfMonth)(F)),Se=(0,n.computed)(()=>{let{class:e,style:t,name:n,...r}=b;return r}),z=(0,n.computed)(()=>typeof b.name==`string`?b.name:void 0),Ce=(0,n.computed)(()=>[`t-date-picker`,`t-date-time-picker`,`t-date-picker--${v.size}`,{"is-disabled":v.disabled,"is-invalid":v.invalid,"is-loading":v.loading,"is-open":B.value},b.class]),we=(0,n.computed)(()=>b.style),{value:B,setValue:V}=t.useControllableOpen((0,n.toRef)(v,`open`),v.defaultOpen,e=>{y(`update:open`,e),y(`open-change`,e)}),Te=(0,n.computed)(()=>T.value?new Intl.DateTimeFormat(v.locale,{day:`numeric`,hour:`2-digit`,minute:`2-digit`,month:`long`,year:`numeric`}).format(T.value):v.placeholder),Ee=(0,n.computed)(()=>new Intl.DateTimeFormat(v.locale,{day:`numeric`,hour:`2-digit`,minute:`2-digit`,month:`long`,year:`numeric`}).format(I.value)),De=(0,n.computed)(()=>new Intl.DateTimeFormat(v.locale,{month:`long`,year:`numeric`}).format(R.value)),Oe=(0,n.computed)(()=>{let e=new Intl.DateTimeFormat(v.locale,{weekday:`short`}),t=(0,r.startOfWeek)(new Date(2024,0,7),v.weekStartsOn);return Array.from({length:7},(n,i)=>e.format((0,r.addDays)(t,i)))}),ke=(0,n.computed)(()=>be(v.step)),H=(0,n.computed)(()=>{let e=new Set;for(let t=0;t<60;t+=ke.value)e.add(t);return e.add(I.value.getMinutes()),[...e].sort((e,t)=>e-t)}),U=e=>{if(v.disabled)return!0;let t=(0,r.startOfDay)(e),n=_e(e);return!!(E.value&&n<E.value||D.value&&t>D.value)},Ae=e=>!!(v.disabled||E.value&&e<E.value||D.value&&e>D.value),W=e=>{L.value=(0,r.startOfDay)(e),R.value=(0,r.startOfMonth)(e)},G=e=>{let t=N(e);I.value=t,W(t)};(0,n.watch)(T,e=>{B.value||G(e??P())},{immediate:!0});let je=(0,n.computed)(()=>(0,r.startOfDay)(I.value)),Me=(0,n.computed)(()=>{let e=(0,r.startOfWeek)((0,r.startOfMonth)(R.value),v.weekStartsOn);return Array.from({length:6},(t,n)=>Array.from({length:7},(t,i)=>{let a=(0,r.addDays)(e,n*7+i),o=(0,r.toDateValue)(a);return{date:a,dayLabel:a.getDate(),disabled:U(a),inMonth:a.getMonth()===R.value.getMonth(),isFocused:(0,r.isSameDay)(a,L.value),isSelected:(0,r.isSameDay)(a,je.value),isToday:(0,r.isSameDay)(a,xe),key:o}}))}),Ne=(0,n.computed)(()=>!E.value||(0,r.startOfMonth)(R.value)>(0,r.startOfMonth)(E.value)),Pe=(0,n.computed)(()=>!D.value||(0,r.startOfMonth)(R.value)<(0,r.startOfMonth)(D.value)),K=(e,t)=>{let n=w(I.value,e,t);return!Ae(n)},Fe=(0,n.computed)(()=>Array.from({length:24},(e,t)=>({disabled:!H.value.some(e=>K(t,e)),label:S(t),value:t}))),Ie=(0,n.computed)(()=>H.value.map(e=>({disabled:!K(I.value.getHours(),e),label:S(e),value:e}))),q=()=>{let e=(0,r.toDateValue)(L.value);(0,n.nextTick)(()=>{M.get(e)?.focus()})},Le=(e,t)=>{if(e instanceof HTMLButtonElement){M.set(t,e);return}M.delete(t)},J=()=>{v.disabled||(G(T.value??P()),V(!0),q())},Y=(e=!1)=>{V(!1),e&&(0,n.nextTick)(()=>{j.value?.focus()})},Re=()=>{if(B.value){Y();return}J()},X=e=>{let t=(0,r.addMonths)(R.value,e);W(t),q()},Z=e=>{U(e)||(W(e),q())},Q=e=>{if(U(e))return;let t=N(ye(I.value,e));I.value=t,W(t)},ze=e=>{if(!v.disabled){if((0,r.isEscapeKey)(e)&&B.value){e.preventDefault(),Y();return}((0,r.isActivationKey)(e)||e.key===`ArrowDown`||e.key===`ArrowUp`)&&(e.preventDefault(),J())}},Be=(e,t)=>{if((0,r.isEscapeKey)(e)){e.preventDefault(),Y(!0);return}if((0,r.isActivationKey)(e)){e.preventDefault(),Q(t);return}switch(e.key){case`ArrowLeft`:e.preventDefault(),Z((0,r.addDays)(t,-1));break;case`ArrowRight`:e.preventDefault(),Z((0,r.addDays)(t,1));break;case`ArrowUp`:e.preventDefault(),Z((0,r.addDays)(t,-7));break;case`ArrowDown`:e.preventDefault(),Z((0,r.addDays)(t,7));break;case`Home`:e.preventDefault(),Z((0,r.startOfWeek)(t,v.weekStartsOn));break;case`End`:e.preventDefault(),Z((0,r.addDays)((0,r.startOfWeek)(t,v.weekStartsOn),6));break;case`PageUp`:e.preventDefault(),Z((0,r.addMonths)(t,e.shiftKey?-12:-1));break;case`PageDown`:e.preventDefault(),Z((0,r.addMonths)(t,e.shiftKey?12:1));break;default:break}},Ve=e=>{let t=Number.parseInt(e.target.value,10),n=I.value.getMinutes();if(Number.isNaN(t))return;let r=H.value.filter(e=>K(t,e));if(!r.length)return;let i=r.includes(n)?n:r[0];I.value=w(I.value,t,i)},He=e=>{let t=Number.parseInt(e.target.value,10),n=I.value.getHours();Number.isNaN(t)||!K(n,t)||(I.value=w(I.value,n,t))},Ue=()=>{let e=ve(I.value);y(`update:modelValue`,e),y(`change`,e),y(`select`,e),Y(!0)},We=()=>{y(`update:modelValue`,``),y(`change`,``),y(`select`,``),Y(!0)},$=e=>{if(!B.value)return;let t=e.target;t instanceof Node&&(A.value?.contains(t)||Y())};return(0,n.watch)(B,e=>{if(e){document.addEventListener(`pointerdown`,$);return}document.removeEventListener(`pointerdown`,$)}),(0,n.onBeforeUnmount)(()=>{document.removeEventListener(`pointerdown`,$)}),(t,r)=>((0,n.openBlock)(),(0,n.createElementBlock)(`div`,{ref_key:`rootRef`,ref:A,class:(0,n.normalizeClass)(Ce.value),style:(0,n.normalizeStyle)(we.value),"data-state":(0,n.unref)(B)?`open`:`closed`},[z.value?((0,n.openBlock)(),(0,n.createElementBlock)(`input`,{key:0,type:`hidden`,name:z.value,value:_.modelValue},null,8,o)):(0,n.createCommentVNode)(``,!0),(0,n.createElementVNode)(`button`,(0,n.mergeProps)({ref_key:`triggerRef`,ref:j},Se.value,{type:`button`,class:`t-date-picker__trigger t-date-time-picker__trigger`,disabled:_.disabled,"aria-controls":(0,n.unref)(B)?(0,n.unref)(O):void 0,"aria-haspopup":`dialog`,"aria-expanded":(0,n.unref)(B),"aria-invalid":_.invalid||void 0,onClick:Re,onKeydown:ze}),[t.$slots.prefix?((0,n.openBlock)(),(0,n.createElementBlock)(`span`,c,[(0,n.renderSlot)(t.$slots,`prefix`)])):(0,n.createCommentVNode)(``,!0),(0,n.createElementVNode)(`span`,{class:`t-date-picker__value`,"data-placeholder":!T.value||void 0},(0,n.toDisplayString)(Te.value),9,l),(0,n.createElementVNode)(`span`,u,[(0,n.renderSlot)(t.$slots,`suffix`,{},()=>[_.loading?((0,n.openBlock)(),(0,n.createBlock)(e.default,{key:0,size:`sm`,label:`Loading`})):((0,n.openBlock)(),(0,n.createBlock)((0,n.unref)(fe),(0,n.normalizeProps)((0,n.mergeProps)({key:1},(0,n.unref)(i.treeIconDefaults))),null,16))])])],16,s),(0,n.createVNode)(n.Transition,{name:`t-fade`},{default:(0,n.withCtx)(()=>[(0,n.unref)(B)&&!_.disabled?((0,n.openBlock)(),(0,n.createElementBlock)(`div`,{key:0,id:(0,n.unref)(O),class:`t-date-picker__content t-date-time-picker__content`,role:`dialog`,"aria-modal":`false`,"aria-labelledby":k},[(0,n.createElementVNode)(`div`,f,[(0,n.createElementVNode)(`div`,p,[(0,n.createElementVNode)(`button`,{type:`button`,class:`t-date-picker__nav`,disabled:!Ne.value,"aria-label":`Previous month`,onClick:r[0]||=e=>X(-1)},[(0,n.createVNode)((0,n.unref)(pe),(0,n.normalizeProps)((0,n.guardReactiveProps)((0,n.unref)(i.treeIconDefaults))),null,16)],8,m),(0,n.createElementVNode)(`p`,{id:k,class:`t-date-picker__month`},(0,n.toDisplayString)(De.value),1),(0,n.createElementVNode)(`button`,{type:`button`,class:`t-date-picker__nav`,disabled:!Pe.value,"aria-label":`Next month`,onClick:r[1]||=e=>X(1)},[(0,n.createVNode)((0,n.unref)(me),(0,n.normalizeProps)((0,n.guardReactiveProps)((0,n.unref)(i.treeIconDefaults))),null,16)],8,h)]),(0,n.createElementVNode)(`div`,g,[((0,n.openBlock)(!0),(0,n.createElementBlock)(n.Fragment,null,(0,n.renderList)(Oe.value,e=>((0,n.openBlock)(),(0,n.createElementBlock)(`span`,{key:e,class:`t-date-picker__weekday`},(0,n.toDisplayString)(e),1))),128))]),(0,n.createElementVNode)(`div`,{class:`t-date-picker__grid`,role:`grid`,"aria-labelledby":k},[((0,n.openBlock)(!0),(0,n.createElementBlock)(n.Fragment,null,(0,n.renderList)(Me.value,(e,t)=>((0,n.openBlock)(),(0,n.createElementBlock)(`div`,{key:t,class:`t-date-picker__row`,role:`row`},[((0,n.openBlock)(!0),(0,n.createElementBlock)(n.Fragment,null,(0,n.renderList)(e,e=>((0,n.openBlock)(),(0,n.createElementBlock)(`div`,{key:e.key,class:`t-date-picker__cell`,role:`gridcell`,"aria-selected":e.isSelected},[(0,n.createElementVNode)(`button`,{ref_for:!0,ref:t=>Le(t,e.key),type:`button`,class:(0,n.normalizeClass)([`t-date-picker__day`,{"is-muted":!e.inMonth,"is-selected":e.isSelected,"is-today":e.isToday}]),"data-date":e.key,tabindex:e.isFocused?0:-1,disabled:e.disabled,"aria-pressed":e.isSelected,onClick:t=>Q(e.date),onKeydown:t=>Be(t,e.date)},(0,n.toDisplayString)(e.dayLabel),43,te)],8,ee))),128))]))),128))])]),(0,n.createElementVNode)(`div`,ne,[r[2]||=(0,n.createElementVNode)(`p`,{class:`t-date-time-picker__eyebrow`},` Selected slot `,-1),(0,n.createElementVNode)(`p`,re,(0,n.toDisplayString)(Ee.value),1)]),(0,n.createElementVNode)(`div`,ie,[(0,n.createElementVNode)(`label`,ae,[r[3]||=(0,n.createElementVNode)(`span`,{class:`t-date-time-picker__time-label`},`Hour`,-1),(0,n.createElementVNode)(`select`,{class:`t-date-time-picker__select`,value:I.value.getHours(),"aria-label":`Select hour`,onChange:Ve},[((0,n.openBlock)(!0),(0,n.createElementBlock)(n.Fragment,null,(0,n.renderList)(Fe.value,e=>((0,n.openBlock)(),(0,n.createElementBlock)(`option`,{key:e.value,value:e.value,disabled:e.disabled},(0,n.toDisplayString)(e.label),9,se))),128))],40,oe)]),(0,n.createElementVNode)(`label`,ce,[r[4]||=(0,n.createElementVNode)(`span`,{class:`t-date-time-picker__time-label`},`Minute`,-1),(0,n.createElementVNode)(`select`,{class:`t-date-time-picker__select`,value:I.value.getMinutes(),"aria-label":`Select minute`,onChange:He},[((0,n.openBlock)(!0),(0,n.createElementBlock)(n.Fragment,null,(0,n.renderList)(Ie.value,e=>((0,n.openBlock)(),(0,n.createElementBlock)(`option`,{key:e.value,value:e.value,disabled:e.disabled},(0,n.toDisplayString)(e.label),9,ue))),128))],40,le)])]),(0,n.createElementVNode)(`div`,{class:`t-date-time-picker__footer`},[(0,n.createElementVNode)(`button`,{type:`button`,class:`t-button t-button--ghost t-button--sm`,onClick:We},` Clear `),(0,n.createElementVNode)(`button`,{type:`button`,class:`t-button t-button--solid t-button--sm`,onClick:Ue},` Apply `)])],8,d)):(0,n.createCommentVNode)(``,!0)]),_:1})],14,a))}});exports.default=_;
2
2
  //# sourceMappingURL=TDateTimePicker.vue_vue_type_script_setup_true_lang.cjs.map