@rotki/ui-library 3.0.0-beta.1 → 3.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/accordions/accordion/RuiAccordion.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/accordions/accordion/RuiAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/accordions/accordions/RuiAccordions.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/avatars/RuiAvatar.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/avatars/RuiAvatarGroup.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/buttons/button/RuiButton.js.map +1 -1
- package/dist/components/buttons/button/RuiButton.vue_vue_type_script_setup_true_lang.js +10 -10
- package/dist/components/buttons/button/RuiButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/buttons/button/button-styles.js +10 -2
- package/dist/components/buttons/button/button-styles.js.map +1 -1
- package/dist/components/buttons/button-group/RuiButtonGroup.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/calendar/RuiCalendar.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/calendar/RuiCalendarGrid.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/calendar/RuiCalendarHeader.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/calendar/RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/chips/RuiChip.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/color-picker/RuiColorBoard.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/color-picker/RuiColorDisplay.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/color-picker/RuiColorHue.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/color-picker/RuiColorInput.vue_vue_type_script_setup_true_lang.js +9 -10
- package/dist/components/color-picker/RuiColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/color-picker/RuiColorPicker.vue_vue_type_script_setup_true_lang.js +3 -4
- package/dist/components/color-picker/RuiColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js +33 -33
- package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/date-time-picker/use-date-bounds.js +1 -1
- package/dist/components/date-time-picker/use-date-time-selection.js +65 -66
- package/dist/components/date-time-picker/use-date-time-selection.js.map +1 -1
- package/dist/components/date-time-picker/use-input-handler.js +1 -1
- package/dist/components/date-time-picker/use-keyboard-handler.js +29 -30
- package/dist/components/date-time-picker/use-keyboard-handler.js.map +1 -1
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js +24 -24
- package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js +49 -49
- package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/checkbox/RuiCheckbox.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/forms/file-upload/RuiFileUpload.js.map +1 -1
- package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js +19 -17
- package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/radio-button/radio/RuiRadio.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js +11 -11
- package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js +17 -17
- package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/switch/RuiSwitch.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js +21 -21
- package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/forms/text-field/RuiTextField.js.map +1 -1
- package/dist/components/forms/text-field/RuiTextField.vue.d.ts +2 -0
- package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js +24 -20
- package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/helpers/RuiFormTextDetail.js.map +1 -1
- package/dist/components/helpers/RuiFormTextDetail.vue.d.ts +11 -1
- package/dist/components/helpers/RuiFormTextDetail.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/helpers/RuiFormTextDetail.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/icons/RuiIcon.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/logos/RuiLogo.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/overlays/badge/RuiBadge.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/overlays/dialog/RuiDialog.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js +17 -17
- package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.js.map +1 -1
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue_vue_type_script_setup_true_lang.js +9 -9
- package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/notification/RuiNotification.vue_vue_type_script_setup_true_lang.js +3 -4
- package/dist/components/overlays/notification/RuiNotification.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/overlays/tooltip/RuiTooltip.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/progress/RuiProgress.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/steppers/RuiFooterStepper.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/steppers/RuiStepper.js.map +1 -1
- package/dist/components/steppers/RuiStepper.vue_vue_type_script_setup_true_lang.js +13 -8
- package/dist/components/steppers/RuiStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js +27 -27
- package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/RuiTableHead.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/tables/data-table/RuiDataTableCell.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/tables/data-table/RuiDataTableGroupRow.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/tables/data-table/RuiDataTableMobileSort.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js +23 -23
- package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tables/use-pagination-navigation.js +1 -1
- package/dist/components/tabs/tab/RuiTab.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/tabs/tab/use-tab-link.js +1 -1
- package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tabs/tabs/RuiTabs.vue_vue_type_script_setup_true_lang.js +11 -12
- package/dist/components/tabs/tabs/RuiTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/tabs/tabs/use-tab-indicator.js +7 -7
- package/dist/components/tabs/tabs/use-tab-indicator.js.map +1 -1
- package/dist/components/tabs/tabs/use-tab-keyboard.js +1 -1
- package/dist/components/tabs/tabs/use-tab-scroll.js +15 -15
- package/dist/components/tabs/tabs/use-tab-scroll.js.map +1 -1
- package/dist/components/time-picker/RuiTimePicker.vue_vue_type_script_setup_true_lang.js +60 -61
- package/dist/components/time-picker/RuiTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/composables/breakpoint.js +3 -3
- package/dist/composables/breakpoint.js.map +1 -1
- package/dist/composables/dropdown-menu.d.ts +9 -9
- package/dist/composables/dropdown-menu.js +32 -32
- package/dist/composables/dropdown-menu.js.map +1 -1
- package/dist/composables/floating.d.ts +1 -1
- package/dist/composables/floating.js +11 -11
- package/dist/composables/floating.js.map +1 -1
- package/dist/composables/forms/auto-complete/focus.js +16 -17
- package/dist/composables/forms/auto-complete/focus.js.map +1 -1
- package/dist/composables/forms/auto-complete/keyboard-navigation.js +1 -1
- package/dist/composables/forms/auto-complete/search.js +6 -7
- package/dist/composables/forms/auto-complete/search.js.map +1 -1
- package/dist/composables/forms/auto-complete/selection.js +1 -1
- package/dist/composables/forms/auto-complete/value.js +1 -1
- package/dist/composables/forms/category-picker/use-category-picker.js +1 -1
- package/dist/composables/forms/use-prepend-append-width.js +6 -6
- package/dist/composables/forms/use-prepend-append-width.js.map +1 -1
- package/dist/composables/keyboard-scroll.js +5 -5
- package/dist/composables/keyboard-scroll.js.map +1 -1
- package/dist/composables/overlay-stack.js +1 -1
- package/dist/composables/sticky-header.js +8 -8
- package/dist/composables/sticky-header.js.map +1 -1
- package/dist/composables/tables/data-table/columns.js +1 -1
- package/dist/composables/tables/data-table/expansion.js +1 -1
- package/dist/composables/tables/data-table/grouping.js +27 -28
- package/dist/composables/tables/data-table/grouping.js.map +1 -1
- package/dist/composables/tables/data-table/page-scroll.d.ts +7 -5
- package/dist/composables/tables/data-table/page-scroll.js +48 -17
- package/dist/composables/tables/data-table/page-scroll.js.map +1 -1
- package/dist/composables/tables/data-table/pagination.js +1 -1
- package/dist/composables/tables/data-table/selection.js +37 -38
- package/dist/composables/tables/data-table/selection.js.map +1 -1
- package/dist/composables/tables/data-table/sort.js +1 -1
- package/dist/composables/theme.js +12 -12
- package/dist/composables/theme.js.map +1 -1
- package/dist/composables/timeout-manager.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/style.css +2 -18
- package/dist/types/dom.d.ts +9 -0
- package/dist/types/theme.d.ts +2 -3
- package/dist/types/theme.js.map +1 -1
- package/dist/utils/form-text-detail.js +1 -1
- package/dist/utils/helpers.js +1 -1
- package/dist/vendor/@vueuse/core/dist/index.js +1577 -0
- package/dist/vendor/@vueuse/core/dist/index.js.map +1 -0
- package/dist/vendor/@vueuse/shared/dist/index.js +563 -0
- package/dist/vendor/@vueuse/shared/dist/index.js.map +1 -0
- package/dist/vite-plugin/index.js +5 -6
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/web-types.json +9 -2
- package/package.json +5 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-date-time-selection.js","names":[],"sources":["../../../src/components/date-time-picker/use-date-time-selection.ts"],"sourcesContent":["import type { Dayjs } from 'dayjs';\nimport type { ComputedRef, Ref, WritableComputedRef } from 'vue';\nimport type { SegmentData } from '@/components/date-time-picker/types';\nimport type { TimeAccuracy } from '@/consts/time-accuracy';\nimport { dayjs } from '@/components/date-time-picker/dayjs-setup';\nimport { completePartialEntry, type PartialTimeMode } from '@/components/date-time-picker/partial-time';\nimport { buildDateTime, clampToBounds } from '@/components/date-time-picker/segment-utils';\nimport { useDateBounds } from '@/components/date-time-picker/use-date-bounds';\nimport { formatWallClock, guessTimezone, includeMilliseconds, includeSeconds } from '@/components/date-time-picker/utils';\n\ntype DateTimeModelType = 'date' | 'epoch-ms' | 'epoch';\n\ntype ModelValueType<T extends DateTimeModelType> =\n T extends 'date' ? Date | undefined :\n T extends 'epoch-ms' ? number | undefined :\n T extends 'epoch' ? number | undefined :\n Date | number | undefined;\n\ninterface DateTimeSelectionOptions<T extends DateTimeModelType> {\n modelValue: Ref<ModelValueType<T>>;\n type: T;\n accuracy: TimeAccuracy;\n minDate: Date | number | undefined;\n maxDate: Date | number | 'now' | undefined;\n allowEmpty: boolean;\n /**\n * The field's own format, so a bound named in an error message is written the\n * same way round as the value the user is looking at.\n */\n dateFormat: Ref<string>;\n /** See {@link completePartialEntry}; unset, an incomplete entry is not a value. */\n partialTime?: PartialTimeMode;\n}\n\ninterface DateTimeSelectionReturn {\n modelYear: Ref<number | undefined>;\n modelMonth: Ref<number | undefined>;\n modelDay: Ref<number | undefined>;\n modelHour: Ref<number | undefined>;\n modelMinute: Ref<number | undefined>;\n modelSecond: Ref<number | undefined>;\n modelMillisecond: Ref<number | undefined>;\n modelTimezone: Ref<string | undefined>;\n selectedDate: WritableComputedRef<Date | undefined>;\n selectedTime: WritableComputedRef<Date | undefined>;\n valueSet: ComputedRef<boolean>;\n internalErrorMessages: Readonly<Ref<string[]>>;\n now: Readonly<Ref<Dayjs>>;\n segmentData: SegmentData;\n minAllowedDate: ComputedRef<Date>;\n maxAllowedDate: ComputedRef<Date | undefined>;\n getDateTime: () => Dayjs;\n setNow: () => void;\n setToday: () => void;\n commitPartialTime: () => void;\n clear: () => void;\n isDateValid: (date: Dayjs) => boolean;\n}\n\nconst MILLISECONDS = 1000;\n\nexport function useDateTimeSelection<T extends DateTimeModelType>(\n options: DateTimeSelectionOptions<T>,\n): DateTimeSelectionReturn {\n const {\n accuracy,\n allowEmpty,\n dateFormat,\n maxDate,\n minDate,\n modelValue,\n partialTime,\n type,\n } = options;\n\n const modelYear = ref<number | undefined>();\n const modelMonth = ref<number | undefined>();\n const modelDay = ref<number | undefined>();\n\n const modelHour = ref<number | undefined>();\n const modelMinute = ref<number | undefined>();\n const modelSecond = ref<number | undefined>();\n const modelMillisecond = ref<number | undefined>();\n const modelTimezone = ref<string | undefined>(guessTimezone());\n\n const now = ref<Dayjs>(dayjs.tz(undefined, guessTimezone()));\n\n const { clearErrors, internalErrorMessages, isDateValid, maxAllowedDate, minAllowedDate } = useDateBounds({\n dateFormat,\n epochSeconds: type === 'epoch',\n maxDate,\n minDate,\n now,\n });\n\n const segmentData: SegmentData = {\n DD: modelDay,\n HH: modelHour,\n MM: modelMonth,\n SSS: modelMillisecond,\n YYYY: modelYear,\n mm: modelMinute,\n ss: modelSecond,\n };\n\n const selectedDate = computed<Date | undefined>({\n get() {\n if (!(isDefined(modelYear) && isDefined(modelMonth) && isDefined(modelDay))) {\n return undefined;\n }\n const date = new Date();\n date.setFullYear(get(modelYear));\n // Day 1 first, so a later month with fewer days cannot overflow into the next one\n date.setDate(1);\n date.setMonth(get(modelMonth) - 1);\n date.setDate(get(modelDay));\n return date;\n },\n set(value?: Date) {\n set(modelYear, value?.getFullYear());\n set(modelMonth, value ? value.getMonth() + 1 : undefined);\n set(modelDay, value?.getDate());\n },\n });\n\n const selectedTime = computed<Date | undefined>({\n get() {\n if (!(isDefined(modelHour) && isDefined(modelMinute))) {\n return undefined;\n }\n const date = new Date();\n date.setHours(\n get(modelHour),\n get(modelMinute),\n get(modelSecond) ?? 0,\n get(modelMillisecond) ?? 0,\n );\n return date;\n },\n set(value?: Date) {\n set(modelHour, value?.getHours());\n set(modelMinute, value?.getMinutes());\n set(modelSecond, value?.getSeconds());\n set(modelMillisecond, value?.getMilliseconds());\n },\n });\n\n const valueSet = computed<boolean>(() => isDefined(selectedDate) && isDefined(selectedTime));\n\n function getDateTime(): Dayjs {\n return buildDateTime({\n day: get(modelDay),\n hour: get(modelHour),\n millisecond: get(modelMillisecond),\n minute: get(modelMinute),\n month: get(modelMonth),\n second: get(modelSecond),\n year: get(modelYear),\n }, accuracy);\n }\n\n function emitUpdate(updatedModel: Dayjs): void {\n const typeMap = {\n 'date': () => updatedModel.toDate(),\n // an epoch is whole seconds; `millisecond` accuracy would otherwise emit a fraction\n 'epoch': () => Math.floor(updatedModel.valueOf() / MILLISECONDS),\n 'epoch-ms': () => updatedModel.valueOf(),\n } as const;\n\n set(modelValue, typeMap[type]() as ModelValueType<T>);\n }\n\n /**\n * Writes the segments back to the model.\n *\n * The segments are a wall-clock reading, so they are formatted and parsed in\n * the selected timezone. Mutating a `dayjs.tz()` built from the old value\n * instead would keep that value's UTC offset, and moving the date across a\n * DST boundary then shifted the time by an hour.\n */\n function updateModelValue(): void {\n if (!isDefined(selectedDate) || !isDefined(selectedTime)) {\n return;\n }\n\n const updatedModel = dayjs.tz(\n formatWallClock(get(selectedDate), get(selectedTime), accuracy),\n get(modelTimezone),\n );\n\n if (!isDateValid(updatedModel)) {\n return;\n }\n\n emitUpdate(updatedModel);\n }\n\n function clear(): void {\n clearErrors();\n set(modelYear, undefined);\n set(modelMonth, undefined);\n set(modelDay, undefined);\n set(modelHour, undefined);\n set(modelMinute, undefined);\n set(modelSecond, undefined);\n set(modelMillisecond, undefined);\n set(modelValue, undefined as ModelValueType<T>);\n }\n\n function clampToAllowed(date: Dayjs): Dayjs {\n return clampToBounds(date, get(minAllowedDate), get(maxAllowedDate));\n }\n\n function applySegments(date: Dayjs): void {\n set(modelYear, date.year());\n set(modelMonth, date.month() + 1);\n set(modelDay, date.date());\n set(modelHour, date.hour());\n set(modelMinute, date.minute());\n set(modelSecond, includeSeconds(accuracy) ? date.second() : 0);\n set(modelMillisecond, includeMilliseconds(accuracy) ? date.millisecond() : 0);\n }\n\n function setNow(): void {\n clearErrors();\n\n const date = dayjs();\n set(now, date);\n applySegments(clampToAllowed(date));\n\n nextTick(() => {\n updateModelValue();\n });\n }\n\n /**\n * Moves the date part to today and leaves the time part alone, so a picked\n * time survives. Falls back to midnight when no time has been entered yet.\n */\n function setToday(): void {\n clearErrors();\n\n const date = dayjs();\n set(now, date);\n\n const target = buildDateTime({\n day: date.date(),\n hour: get(modelHour) ?? 0,\n millisecond: get(modelMillisecond) ?? 0,\n minute: get(modelMinute) ?? 0,\n month: date.month() + 1,\n second: get(modelSecond) ?? 0,\n year: date.year(),\n }, accuracy, date);\n\n applySegments(clampToAllowed(target));\n\n nextTick(() => {\n updateModelValue();\n });\n }\n\n function updateSegments(date: Dayjs): void {\n const year = date.year();\n const month = date.month() + 1;\n const day = date.date();\n const hour = date.hour();\n const minute = date.minute();\n const second = includeSeconds(accuracy) ? date.second() : undefined;\n const millisecond = includeMilliseconds(accuracy) ? date.millisecond() : undefined;\n\n if (get(modelYear) !== year)\n set(modelYear, year);\n if (get(modelMonth) !== month)\n set(modelMonth, month);\n if (get(modelDay) !== day)\n set(modelDay, day);\n if (get(modelHour) !== hour)\n set(modelHour, hour);\n if (get(modelMinute) !== minute)\n set(modelMinute, minute);\n if (get(modelSecond) !== second)\n set(modelSecond, second);\n if (get(modelMillisecond) !== millisecond)\n set(modelMillisecond, millisecond);\n }\n\n const { ignoreUpdates } = watchIgnorable([selectedDate, selectedTime], ([newSelectedDate, newSelectedTime], [prevSelectedDate, prevSelectedTime]) => {\n const currentTimezone = get(modelTimezone);\n const newDate = dayjs.tz(newSelectedDate, currentTimezone);\n const oldDate = dayjs.tz(prevSelectedDate, currentTimezone);\n const newTime = dayjs.tz(newSelectedTime, currentTimezone);\n const oldTime = dayjs.tz(prevSelectedTime, currentTimezone);\n\n if (newDate.isSame(oldDate) && newTime.isSame(oldTime)) {\n return;\n }\n\n set(now, dayjs());\n updateModelValue();\n });\n\n /**\n * Fills in the segments an incomplete entry never reached and commits it, so\n * a bare date can become a value. Called when the user is done with the field\n * rather than on every keystroke, since a segment still being typed is not\n * yet one they left out. The fill is written back into the field, so the\n * value it decided on is the one on screen, and into the model directly:\n * running on blur and on enter, it would otherwise lose the value to a\n * consumer that closes the field on that key before the watcher emits.\n */\n function commitPartialTime(): void {\n if (partialTime === undefined)\n return;\n\n const target = completePartialEntry({\n day: get(modelDay),\n hour: get(modelHour),\n millisecond: get(modelMillisecond),\n minute: get(modelMinute),\n month: get(modelMonth),\n second: get(modelSecond),\n year: get(modelYear),\n }, {\n accuracy,\n maxDate: get(maxAllowedDate),\n minDate: get(minAllowedDate),\n mode: partialTime,\n });\n\n if (!target)\n return;\n\n ignoreUpdates(() => applySegments(target));\n updateModelValue();\n }\n\n function updateInternalModel(value: ModelValueType<T>): void {\n ignoreUpdates(() => {\n const updatedValue = type === 'epoch' && typeof value === 'number'\n ? value * MILLISECONDS\n : value;\n const date = dayjs.tz(updatedValue, get(modelTimezone));\n isDateValid(date);\n updateSegments(date);\n });\n }\n\n watch(modelTimezone, (newTimezone: string | undefined) => {\n if (newTimezone && isDefined(selectedDate) && isDefined(selectedTime)) {\n set(now, dayjs());\n updateModelValue();\n }\n });\n\n watch(modelValue, (value) => {\n set(now, dayjs());\n if (value === undefined) {\n clear();\n }\n else {\n updateInternalModel(value);\n }\n });\n\n onMounted(() => {\n if (isDefined(modelValue)) {\n updateInternalModel(get(modelValue));\n }\n else if (!allowEmpty) {\n setNow();\n }\n });\n\n return {\n clear,\n commitPartialTime,\n getDateTime,\n internalErrorMessages,\n isDateValid,\n maxAllowedDate,\n minAllowedDate,\n modelDay,\n modelHour,\n modelMillisecond,\n modelMinute,\n modelMonth,\n modelSecond,\n modelTimezone,\n modelYear,\n now: shallowReadonly(now),\n segmentData,\n selectedDate,\n selectedTime,\n setNow,\n setToday,\n valueSet,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-date-time-selection.js","names":[],"sources":["../../../src/components/date-time-picker/use-date-time-selection.ts"],"sourcesContent":["import type { Dayjs } from 'dayjs';\nimport type { ComputedRef, Ref, WritableComputedRef } from 'vue';\nimport type { SegmentData } from '@/components/date-time-picker/types';\nimport type { TimeAccuracy } from '@/consts/time-accuracy';\nimport { dayjs } from '@/components/date-time-picker/dayjs-setup';\nimport { completePartialEntry, type PartialTimeMode } from '@/components/date-time-picker/partial-time';\nimport { buildDateTime, clampToBounds } from '@/components/date-time-picker/segment-utils';\nimport { useDateBounds } from '@/components/date-time-picker/use-date-bounds';\nimport { formatWallClock, guessTimezone, includeMilliseconds, includeSeconds } from '@/components/date-time-picker/utils';\n\ntype DateTimeModelType = 'date' | 'epoch-ms' | 'epoch';\n\ntype ModelValueType<T extends DateTimeModelType> =\n T extends 'date' ? Date | undefined :\n T extends 'epoch-ms' ? number | undefined :\n T extends 'epoch' ? number | undefined :\n Date | number | undefined;\n\ninterface DateTimeSelectionOptions<T extends DateTimeModelType> {\n modelValue: Ref<ModelValueType<T>>;\n type: T;\n accuracy: TimeAccuracy;\n minDate: Date | number | undefined;\n maxDate: Date | number | 'now' | undefined;\n allowEmpty: boolean;\n /**\n * The field's own format, so a bound named in an error message is written the\n * same way round as the value the user is looking at.\n */\n dateFormat: Ref<string>;\n /** See {@link completePartialEntry}; unset, an incomplete entry is not a value. */\n partialTime?: PartialTimeMode;\n}\n\ninterface DateTimeSelectionReturn {\n modelYear: Ref<number | undefined>;\n modelMonth: Ref<number | undefined>;\n modelDay: Ref<number | undefined>;\n modelHour: Ref<number | undefined>;\n modelMinute: Ref<number | undefined>;\n modelSecond: Ref<number | undefined>;\n modelMillisecond: Ref<number | undefined>;\n modelTimezone: Ref<string | undefined>;\n selectedDate: WritableComputedRef<Date | undefined>;\n selectedTime: WritableComputedRef<Date | undefined>;\n valueSet: ComputedRef<boolean>;\n internalErrorMessages: Readonly<Ref<string[]>>;\n now: Readonly<Ref<Dayjs>>;\n segmentData: SegmentData;\n minAllowedDate: ComputedRef<Date>;\n maxAllowedDate: ComputedRef<Date | undefined>;\n getDateTime: () => Dayjs;\n setNow: () => void;\n setToday: () => void;\n commitPartialTime: () => void;\n clear: () => void;\n isDateValid: (date: Dayjs) => boolean;\n}\n\nconst MILLISECONDS = 1000;\n\nexport function useDateTimeSelection<T extends DateTimeModelType>(\n options: DateTimeSelectionOptions<T>,\n): DateTimeSelectionReturn {\n const {\n accuracy,\n allowEmpty,\n dateFormat,\n maxDate,\n minDate,\n modelValue,\n partialTime,\n type,\n } = options;\n\n const modelYear = ref<number | undefined>();\n const modelMonth = ref<number | undefined>();\n const modelDay = ref<number | undefined>();\n\n const modelHour = ref<number | undefined>();\n const modelMinute = ref<number | undefined>();\n const modelSecond = ref<number | undefined>();\n const modelMillisecond = ref<number | undefined>();\n const modelTimezone = ref<string | undefined>(guessTimezone());\n\n const now = ref<Dayjs>(dayjs.tz(undefined, guessTimezone()));\n\n const { clearErrors, internalErrorMessages, isDateValid, maxAllowedDate, minAllowedDate } = useDateBounds({\n dateFormat,\n epochSeconds: type === 'epoch',\n maxDate,\n minDate,\n now,\n });\n\n const segmentData: SegmentData = {\n DD: modelDay,\n HH: modelHour,\n MM: modelMonth,\n SSS: modelMillisecond,\n YYYY: modelYear,\n mm: modelMinute,\n ss: modelSecond,\n };\n\n const selectedDate = computed<Date | undefined>({\n get() {\n if (!(isDefined(modelYear) && isDefined(modelMonth) && isDefined(modelDay))) {\n return undefined;\n }\n const date = new Date();\n date.setFullYear(get(modelYear));\n // Day 1 first, so a later month with fewer days cannot overflow into the next one\n date.setDate(1);\n date.setMonth(get(modelMonth) - 1);\n date.setDate(get(modelDay));\n return date;\n },\n set(value?: Date) {\n set(modelYear, value?.getFullYear());\n set(modelMonth, value ? value.getMonth() + 1 : undefined);\n set(modelDay, value?.getDate());\n },\n });\n\n const selectedTime = computed<Date | undefined>({\n get() {\n if (!(isDefined(modelHour) && isDefined(modelMinute))) {\n return undefined;\n }\n const date = new Date();\n date.setHours(\n get(modelHour),\n get(modelMinute),\n get(modelSecond) ?? 0,\n get(modelMillisecond) ?? 0,\n );\n return date;\n },\n set(value?: Date) {\n set(modelHour, value?.getHours());\n set(modelMinute, value?.getMinutes());\n set(modelSecond, value?.getSeconds());\n set(modelMillisecond, value?.getMilliseconds());\n },\n });\n\n const valueSet = computed<boolean>(() => isDefined(selectedDate) && isDefined(selectedTime));\n\n function getDateTime(): Dayjs {\n return buildDateTime({\n day: get(modelDay),\n hour: get(modelHour),\n millisecond: get(modelMillisecond),\n minute: get(modelMinute),\n month: get(modelMonth),\n second: get(modelSecond),\n year: get(modelYear),\n }, accuracy);\n }\n\n function emitUpdate(updatedModel: Dayjs): void {\n const typeMap = {\n 'date': () => updatedModel.toDate(),\n // an epoch is whole seconds; `millisecond` accuracy would otherwise emit a fraction\n 'epoch': () => Math.floor(updatedModel.valueOf() / MILLISECONDS),\n 'epoch-ms': () => updatedModel.valueOf(),\n } as const;\n\n set(modelValue, typeMap[type]() as ModelValueType<T>);\n }\n\n /**\n * Writes the segments back to the model.\n *\n * The segments are a wall-clock reading, so they are formatted and parsed in\n * the selected timezone. Mutating a `dayjs.tz()` built from the old value\n * instead would keep that value's UTC offset, and moving the date across a\n * DST boundary then shifted the time by an hour.\n */\n function updateModelValue(): void {\n if (!isDefined(selectedDate) || !isDefined(selectedTime)) {\n return;\n }\n\n const updatedModel = dayjs.tz(\n formatWallClock(get(selectedDate), get(selectedTime), accuracy),\n get(modelTimezone),\n );\n\n if (!isDateValid(updatedModel)) {\n return;\n }\n\n emitUpdate(updatedModel);\n }\n\n function clear(): void {\n clearErrors();\n set(modelYear, undefined);\n set(modelMonth, undefined);\n set(modelDay, undefined);\n set(modelHour, undefined);\n set(modelMinute, undefined);\n set(modelSecond, undefined);\n set(modelMillisecond, undefined);\n set(modelValue, undefined as ModelValueType<T>);\n }\n\n function clampToAllowed(date: Dayjs): Dayjs {\n return clampToBounds(date, get(minAllowedDate), get(maxAllowedDate));\n }\n\n function applySegments(date: Dayjs): void {\n set(modelYear, date.year());\n set(modelMonth, date.month() + 1);\n set(modelDay, date.date());\n set(modelHour, date.hour());\n set(modelMinute, date.minute());\n set(modelSecond, includeSeconds(accuracy) ? date.second() : 0);\n set(modelMillisecond, includeMilliseconds(accuracy) ? date.millisecond() : 0);\n }\n\n function setNow(): void {\n clearErrors();\n\n const date = dayjs();\n set(now, date);\n applySegments(clampToAllowed(date));\n\n nextTick(() => {\n updateModelValue();\n });\n }\n\n /**\n * Moves the date part to today and leaves the time part alone, so a picked\n * time survives. Falls back to midnight when no time has been entered yet.\n */\n function setToday(): void {\n clearErrors();\n\n const date = dayjs();\n set(now, date);\n\n const target = buildDateTime({\n day: date.date(),\n hour: get(modelHour) ?? 0,\n millisecond: get(modelMillisecond) ?? 0,\n minute: get(modelMinute) ?? 0,\n month: date.month() + 1,\n second: get(modelSecond) ?? 0,\n year: date.year(),\n }, accuracy, date);\n\n applySegments(clampToAllowed(target));\n\n nextTick(() => {\n updateModelValue();\n });\n }\n\n function updateSegments(date: Dayjs): void {\n const year = date.year();\n const month = date.month() + 1;\n const day = date.date();\n const hour = date.hour();\n const minute = date.minute();\n const second = includeSeconds(accuracy) ? date.second() : undefined;\n const millisecond = includeMilliseconds(accuracy) ? date.millisecond() : undefined;\n\n if (get(modelYear) !== year)\n set(modelYear, year);\n if (get(modelMonth) !== month)\n set(modelMonth, month);\n if (get(modelDay) !== day)\n set(modelDay, day);\n if (get(modelHour) !== hour)\n set(modelHour, hour);\n if (get(modelMinute) !== minute)\n set(modelMinute, minute);\n if (get(modelSecond) !== second)\n set(modelSecond, second);\n if (get(modelMillisecond) !== millisecond)\n set(modelMillisecond, millisecond);\n }\n\n const { ignoreUpdates } = watchIgnorable([selectedDate, selectedTime], ([newSelectedDate, newSelectedTime], [prevSelectedDate, prevSelectedTime]) => {\n const currentTimezone = get(modelTimezone);\n const newDate = dayjs.tz(newSelectedDate, currentTimezone);\n const oldDate = dayjs.tz(prevSelectedDate, currentTimezone);\n const newTime = dayjs.tz(newSelectedTime, currentTimezone);\n const oldTime = dayjs.tz(prevSelectedTime, currentTimezone);\n\n if (newDate.isSame(oldDate) && newTime.isSame(oldTime)) {\n return;\n }\n\n set(now, dayjs());\n updateModelValue();\n });\n\n /**\n * Fills in the segments an incomplete entry never reached and commits it, so\n * a bare date can become a value. Called when the user is done with the field\n * rather than on every keystroke, since a segment still being typed is not\n * yet one they left out. The fill is written back into the field, so the\n * value it decided on is the one on screen, and into the model directly:\n * running on blur and on enter, it would otherwise lose the value to a\n * consumer that closes the field on that key before the watcher emits.\n */\n function commitPartialTime(): void {\n if (partialTime === undefined)\n return;\n\n const target = completePartialEntry({\n day: get(modelDay),\n hour: get(modelHour),\n millisecond: get(modelMillisecond),\n minute: get(modelMinute),\n month: get(modelMonth),\n second: get(modelSecond),\n year: get(modelYear),\n }, {\n accuracy,\n maxDate: get(maxAllowedDate),\n minDate: get(minAllowedDate),\n mode: partialTime,\n });\n\n if (!target)\n return;\n\n ignoreUpdates(() => applySegments(target));\n updateModelValue();\n }\n\n function updateInternalModel(value: ModelValueType<T>): void {\n ignoreUpdates(() => {\n const updatedValue = type === 'epoch' && typeof value === 'number'\n ? value * MILLISECONDS\n : value;\n const date = dayjs.tz(updatedValue, get(modelTimezone));\n isDateValid(date);\n updateSegments(date);\n });\n }\n\n watch(modelTimezone, (newTimezone: string | undefined) => {\n if (newTimezone && isDefined(selectedDate) && isDefined(selectedTime)) {\n set(now, dayjs());\n updateModelValue();\n }\n });\n\n watch(modelValue, (value) => {\n set(now, dayjs());\n if (value === undefined) {\n clear();\n }\n else {\n updateInternalModel(value);\n }\n });\n\n onMounted(() => {\n if (isDefined(modelValue)) {\n updateInternalModel(get(modelValue));\n }\n else if (!allowEmpty) {\n setNow();\n }\n });\n\n return {\n clear,\n commitPartialTime,\n getDateTime,\n internalErrorMessages,\n isDateValid,\n maxAllowedDate,\n minAllowedDate,\n modelDay,\n modelHour,\n modelMillisecond,\n modelMinute,\n modelMonth,\n modelSecond,\n modelTimezone,\n modelYear,\n now: shallowReadonly(now),\n segmentData,\n selectedDate,\n selectedTime,\n setNow,\n setToday,\n valueSet,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { get, set } from "../../vendor/@vueuse/shared/dist/index.js";
|
|
1
2
|
import { includeMilliseconds, includeSeconds } from "./utils.js";
|
|
2
|
-
import { get, set } from "@vueuse/shared";
|
|
3
3
|
//#region src/components/date-time-picker/use-input-handler.ts
|
|
4
4
|
function useInputHandler(data, currentValue) {
|
|
5
5
|
function setValue(segment, value) {
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { assert } from "../../utils/assert.js";
|
|
2
|
+
import { get, isDefined, set } from "../../vendor/@vueuse/shared/dist/index.js";
|
|
2
3
|
import { getClickPosition, parseAndSetDateValues } from "./segment-utils.js";
|
|
3
4
|
import { SEGMENT_CONFIG, SEGMENT_METHODS } from "./segment-config.js";
|
|
4
5
|
import { isDateTimeSegmentType } from "./types.js";
|
|
5
6
|
import { computed, nextTick, shallowReadonly, shallowRef } from "vue";
|
|
6
|
-
import { isDefined } from "@vueuse/core";
|
|
7
|
-
import { get as get$1, set as set$1 } from "@vueuse/shared";
|
|
8
7
|
//#region src/components/date-time-picker/use-keyboard-handler.ts
|
|
9
8
|
function useKeyboardHandler(options) {
|
|
10
9
|
const { accuracy, currentValue, cursorPosition, dateFormat, disabled, getCurrent, getDateTime, readonly, setValue, textInput, timezone } = options;
|
|
11
10
|
const pasteRejected = shallowRef(false);
|
|
12
11
|
const formatSegments = computed(() => {
|
|
13
|
-
return get
|
|
12
|
+
return get(dateFormat).split(/([\s./:])/g).filter(Boolean);
|
|
14
13
|
});
|
|
15
14
|
const segmentPositions = computed(() => {
|
|
16
|
-
const segments = get
|
|
15
|
+
const segments = get(formatSegments);
|
|
17
16
|
const positions = [];
|
|
18
17
|
let currentPosition = 0;
|
|
19
18
|
segments.forEach((segment) => {
|
|
@@ -29,23 +28,23 @@ function useKeyboardHandler(options) {
|
|
|
29
28
|
});
|
|
30
29
|
return positions;
|
|
31
30
|
});
|
|
32
|
-
function getCurrentSegment(position = get
|
|
33
|
-
return get
|
|
31
|
+
function getCurrentSegment(position = get(cursorPosition)) {
|
|
32
|
+
return get(segmentPositions).find((segment) => position >= segment.start && position <= segment.end);
|
|
34
33
|
}
|
|
35
34
|
function setCursorPosition(segment) {
|
|
36
|
-
set
|
|
35
|
+
set(cursorPosition, segment.end);
|
|
37
36
|
nextTick(() => {
|
|
38
37
|
if (isDefined(textInput)) {
|
|
39
|
-
const input = get
|
|
38
|
+
const input = get(textInput);
|
|
40
39
|
input.setSelectionRange(segment.start, segment.end);
|
|
41
40
|
input.focus();
|
|
42
41
|
}
|
|
43
42
|
});
|
|
44
43
|
}
|
|
45
44
|
function setSegment(segmentType) {
|
|
46
|
-
const segments = get
|
|
45
|
+
const segments = get(segmentPositions).find((segment) => segment.type === segmentType);
|
|
47
46
|
if (segments) {
|
|
48
|
-
set
|
|
47
|
+
set(currentValue, void 0);
|
|
49
48
|
setCursorPosition(segments);
|
|
50
49
|
}
|
|
51
50
|
}
|
|
@@ -71,11 +70,11 @@ function useKeyboardHandler(options) {
|
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
72
|
function navigateSegments(key) {
|
|
74
|
-
const position = get
|
|
75
|
-
const positions = get
|
|
73
|
+
const position = get(cursorPosition);
|
|
74
|
+
const positions = get(segmentPositions);
|
|
76
75
|
const currentSegmentIndex = positions.findIndex((segment) => position >= segment.start && position <= segment.end);
|
|
77
76
|
if (currentSegmentIndex === -1) return;
|
|
78
|
-
set
|
|
77
|
+
set(currentValue, void 0);
|
|
79
78
|
let nextSegmentIndex;
|
|
80
79
|
if (key === "ArrowRight") {
|
|
81
80
|
nextSegmentIndex = currentSegmentIndex + 1;
|
|
@@ -103,14 +102,14 @@ function useKeyboardHandler(options) {
|
|
|
103
102
|
*/
|
|
104
103
|
function applyDigit(segment, segmentType, digit) {
|
|
105
104
|
const config = SEGMENT_CONFIG[segmentType];
|
|
106
|
-
const combinedStr = `${get
|
|
105
|
+
const combinedStr = `${get(currentValue) ?? ""}${digit}`;
|
|
107
106
|
const combinedValue = parseInt(combinedStr);
|
|
108
107
|
const maxLength = segmentType.length;
|
|
109
108
|
const minValue = config.minValue ?? 0;
|
|
110
109
|
if (combinedValue >= minValue && combinedValue <= config.maxValue) {
|
|
111
110
|
setValue(segmentType, combinedValue);
|
|
112
111
|
setCursorPosition(segment);
|
|
113
|
-
} else if (combinedValue < minValue && combinedStr.length < maxLength) set
|
|
112
|
+
} else if (combinedValue < minValue && combinedStr.length < maxLength) set(currentValue, combinedValue);
|
|
114
113
|
if (parseInt(`${combinedStr}0`) > config.maxValue || combinedStr.length >= maxLength) navigateSegments("ArrowRight");
|
|
115
114
|
}
|
|
116
115
|
function handleDigitPressed(event, digit) {
|
|
@@ -129,7 +128,7 @@ function useKeyboardHandler(options) {
|
|
|
129
128
|
if (!(event.target instanceof HTMLInputElement)) return;
|
|
130
129
|
const currentSegment = getCurrentSegment(event.target.selectionStart ?? 0);
|
|
131
130
|
if (!currentSegment) return;
|
|
132
|
-
const updatedValue = (get
|
|
131
|
+
const updatedValue = (get(currentValue) ?? getCurrent(currentSegment.type) ?? "").toString().slice(0, -1);
|
|
133
132
|
setValue(currentSegment.type, updatedValue.length === 0 ? void 0 : parseInt(updatedValue));
|
|
134
133
|
setCursorPosition(currentSegment);
|
|
135
134
|
}
|
|
@@ -161,7 +160,7 @@ function useKeyboardHandler(options) {
|
|
|
161
160
|
}
|
|
162
161
|
function handleKeyDown(event) {
|
|
163
162
|
if (disabled || readonly) return;
|
|
164
|
-
set
|
|
163
|
+
set(pasteRejected, false);
|
|
165
164
|
if (event.ctrlKey || event.metaKey || event.altKey) return;
|
|
166
165
|
if (dispatchKey(event, event.key)) event.preventDefault();
|
|
167
166
|
}
|
|
@@ -178,7 +177,7 @@ function useKeyboardHandler(options) {
|
|
|
178
177
|
if (disabled || readonly || !(event.target instanceof HTMLInputElement)) return;
|
|
179
178
|
const currentSegment = getCurrentSegment(getClickPosition(event, event.target, true));
|
|
180
179
|
if (currentSegment) {
|
|
181
|
-
set
|
|
180
|
+
set(currentValue, void 0);
|
|
182
181
|
clickedSegment = currentSegment;
|
|
183
182
|
}
|
|
184
183
|
}
|
|
@@ -186,18 +185,18 @@ function useKeyboardHandler(options) {
|
|
|
186
185
|
if (disabled || readonly || !(event.target instanceof HTMLInputElement)) return;
|
|
187
186
|
const currentSegment = getCurrentSegment(getClickPosition(event, event.target, true));
|
|
188
187
|
if (currentSegment) {
|
|
189
|
-
set
|
|
188
|
+
set(currentValue, void 0);
|
|
190
189
|
clickedSegment = currentSegment;
|
|
191
|
-
set
|
|
190
|
+
set(cursorPosition, currentSegment.end);
|
|
192
191
|
event.target.setSelectionRange(currentSegment.start, currentSegment.end);
|
|
193
192
|
}
|
|
194
193
|
}
|
|
195
194
|
function handleInputSelection(event) {
|
|
196
195
|
const target = event.target;
|
|
197
|
-
set
|
|
196
|
+
set(cursorPosition, target.selectionStart ?? 0);
|
|
198
197
|
}
|
|
199
198
|
function selectFirstSegment() {
|
|
200
|
-
const firstSegment = get
|
|
199
|
+
const firstSegment = get(segmentPositions)[0];
|
|
201
200
|
if (firstSegment) setCursorPosition(firstSegment);
|
|
202
201
|
}
|
|
203
202
|
/**
|
|
@@ -211,7 +210,7 @@ function useKeyboardHandler(options) {
|
|
|
211
210
|
clickedSegment = void 0;
|
|
212
211
|
return;
|
|
213
212
|
}
|
|
214
|
-
const input = get
|
|
213
|
+
const input = get(textInput);
|
|
215
214
|
if (input && input.selectionStart !== input.selectionEnd) return;
|
|
216
215
|
selectFirstSegment();
|
|
217
216
|
}
|
|
@@ -220,9 +219,9 @@ function useKeyboardHandler(options) {
|
|
|
220
219
|
* another segment does not blink through the one left behind.
|
|
221
220
|
*/
|
|
222
221
|
function handleBlur() {
|
|
223
|
-
set
|
|
224
|
-
set
|
|
225
|
-
set
|
|
222
|
+
set(cursorPosition, 0);
|
|
223
|
+
set(currentValue, void 0);
|
|
224
|
+
set(pasteRejected, false);
|
|
226
225
|
clickedSegment = void 0;
|
|
227
226
|
}
|
|
228
227
|
/** Text that is no date raises `pasteRejected` until the next key, paste, input or blur. */
|
|
@@ -232,16 +231,16 @@ function useKeyboardHandler(options) {
|
|
|
232
231
|
event.preventDefault();
|
|
233
232
|
const pastedText = event.clipboardData?.getData("text");
|
|
234
233
|
if (!pastedText) return;
|
|
235
|
-
const parsed = parseAndSetDateValues(pastedText, get
|
|
236
|
-
set
|
|
234
|
+
const parsed = parseAndSetDateValues(pastedText, get(dateFormat), accuracy, setValue, get(timezone));
|
|
235
|
+
set(pasteRejected, !parsed);
|
|
237
236
|
}
|
|
238
237
|
function handleInput(event) {
|
|
239
238
|
if (disabled || readonly) return;
|
|
240
|
-
set
|
|
239
|
+
set(pasteRejected, false);
|
|
241
240
|
if (!(event.target instanceof HTMLInputElement)) return;
|
|
242
241
|
const inputText = event.target.value;
|
|
243
242
|
if (!inputText) return;
|
|
244
|
-
parseAndSetDateValues(inputText, get
|
|
243
|
+
parseAndSetDateValues(inputText, get(dateFormat), accuracy, setValue);
|
|
245
244
|
}
|
|
246
245
|
return {
|
|
247
246
|
clear,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-keyboard-handler.js","names":[],"sources":["../../../src/components/date-time-picker/use-keyboard-handler.ts"],"sourcesContent":["import type { Dayjs } from 'dayjs';\nimport type { Ref, ShallowRef } from 'vue';\nimport type { TimeAccuracy } from '@/consts/time-accuracy';\nimport { type Segment, SEGMENT_CONFIG, SEGMENT_METHODS } from '@/components/date-time-picker/segment-config';\nimport { getClickPosition, parseAndSetDateValues } from '@/components/date-time-picker/segment-utils';\nimport {\n type DateTimeSegmentType,\n isDateTimeSegmentType,\n} from '@/components/date-time-picker/types';\nimport { assert } from '@/utils/assert';\n\ninterface KeyboardHandlerOptions {\n dateFormat: Ref<string>;\n cursorPosition: Ref<number>;\n currentValue: Ref<number | undefined>;\n textInput: Readonly<ShallowRef<HTMLInputElement | null>> | Ref<HTMLInputElement | undefined>;\n setValue: (segment: DateTimeSegmentType, value?: number) => void;\n getCurrent: (segment: DateTimeSegmentType) => number | undefined;\n getDateTime: () => Dayjs;\n disabled: boolean;\n readonly: boolean;\n accuracy: TimeAccuracy;\n timezone: Readonly<Ref<string | undefined>>;\n}\n\nexport function useKeyboardHandler(options: KeyboardHandlerOptions) {\n const {\n accuracy,\n currentValue,\n cursorPosition,\n dateFormat,\n disabled,\n getCurrent,\n getDateTime,\n readonly,\n setValue,\n textInput,\n timezone,\n } = options;\n\n const pasteRejected = shallowRef<boolean>(false);\n\n const formatSegments = computed<string[]>(() => {\n const format = get(dateFormat);\n return format.split(/([\\s./:])/g).filter(Boolean);\n });\n\n const segmentPositions = computed<Segment[]>(() => {\n const segments = get(formatSegments);\n const positions: Segment[] = [];\n let currentPosition = 0;\n\n segments.forEach((segment) => {\n if (!/[\\s./:]/.test(segment)) {\n assert(isDateTimeSegmentType(segment), `Invalid date format segment: ${segment}`);\n positions.push({\n end: currentPosition + segment.length,\n start: currentPosition,\n type: segment,\n });\n }\n currentPosition += segment.length;\n });\n\n return positions;\n });\n\n function getCurrentSegment(position: number = get(cursorPosition)) {\n const segments = get(segmentPositions);\n return segments.find(segment => position >= segment.start && position <= segment.end);\n }\n\n function setCursorPosition(segment: Segment): void {\n set(cursorPosition, segment.end);\n\n nextTick(() => {\n if (isDefined(textInput)) {\n const input = get(textInput);\n input.setSelectionRange(segment.start, segment.end);\n input.focus();\n }\n });\n }\n\n function setSegment(segmentType: DateTimeSegmentType): void {\n const segments = get(segmentPositions).find(segment => segment.type === segmentType);\n if (segments) {\n set(currentValue, undefined);\n setCursorPosition(segments);\n }\n }\n\n function clear(segmentType?: string): void {\n const currentSegment = getCurrentSegment();\n if (!segmentType && !currentSegment) {\n return;\n }\n\n const typeToUse = segmentType ?? currentSegment?.type;\n if (!typeToUse) {\n return;\n }\n\n setValue(typeToUse as DateTimeSegmentType, undefined);\n\n if (currentSegment) {\n setCursorPosition(currentSegment);\n }\n }\n\n function changeSegmentValue(increment: boolean): void {\n if (!isDefined(textInput))\n return;\n const currentSegment = getCurrentSegment();\n if (!currentSegment)\n return;\n const segmentType = currentSegment.type;\n const method = SEGMENT_METHODS[segmentType];\n const selectedDate = getDateTime();\n const updatedDate = selectedDate.set(method, selectedDate.get(method) + (increment ? 1 : -1));\n if (updatedDate.year() >= 1970) {\n setValue(\n segmentType,\n segmentType === 'MM' ? updatedDate.get(method) + 1 : updatedDate.get(method),\n );\n setCursorPosition(currentSegment);\n }\n }\n\n function navigateSegments(key: string) {\n const position = get(cursorPosition);\n const positions = get(segmentPositions);\n const currentSegmentIndex = positions.findIndex(\n segment => position >= segment.start && position <= segment.end,\n );\n\n if (currentSegmentIndex === -1) {\n return;\n }\n\n set(currentValue, undefined);\n\n let nextSegmentIndex: number;\n if (key === 'ArrowRight') {\n nextSegmentIndex = currentSegmentIndex + 1;\n if (nextSegmentIndex < positions.length) {\n const nextSegment = positions[nextSegmentIndex];\n assert(nextSegment);\n setCursorPosition(nextSegment);\n }\n }\n else {\n nextSegmentIndex = currentSegmentIndex - 1;\n if (nextSegmentIndex >= 0) {\n const nextSegment = positions[nextSegmentIndex];\n assert(nextSegment);\n setCursorPosition(nextSegment);\n }\n }\n }\n\n /**\n * Adds a typed digit to the segment under the caret, moving on to the next\n * segment once this one can hold no more.\n *\n * @param segment - the segment the caret sits in\n * @param segmentType - that segment's type, which states its value range\n * @param digit - the digit that was typed\n */\n function applyDigit(segment: Segment, segmentType: DateTimeSegmentType, digit: string): void {\n const config = SEGMENT_CONFIG[segmentType];\n const value = get(currentValue) ?? '';\n const combinedStr = `${value}${digit}`;\n const combinedValue = parseInt(combinedStr);\n const maxLength = segmentType.length;\n const minValue = config.minValue ?? 0;\n\n if (combinedValue >= minValue && combinedValue <= config.maxValue) {\n setValue(segmentType, combinedValue);\n setCursorPosition(segment);\n }\n else if (combinedValue < minValue && combinedStr.length < maxLength) {\n // Track digit even if below minValue, so next digit can combine (e.g., \"0\" allows \"01\")\n set(currentValue, combinedValue);\n }\n\n const willExceedMax = parseInt(`${combinedStr}0`) > config.maxValue;\n // Use string length of typed digits, not parsed number length (e.g., \"01\" has length 2)\n if (willExceedMax || combinedStr.length >= maxLength)\n navigateSegments('ArrowRight');\n }\n\n function handleDigitPressed(event: KeyboardEvent, digit: string): void {\n if (!(event.target instanceof HTMLInputElement))\n return;\n\n const currentSegment = getCurrentSegment(event.target.selectionStart ?? 0);\n if (!currentSegment || isNaN(parseInt(digit)))\n return;\n\n const segmentType = currentSegment.type;\n if (!isDateTimeSegmentType(segmentType))\n return;\n\n applyDigit(currentSegment, segmentType, digit);\n }\n\n function handleKeyboardNavigation(event: KeyboardEvent): void {\n if (event.key === 'ArrowUp')\n changeSegmentValue(true);\n else if (event.key === 'ArrowDown')\n changeSegmentValue(false);\n }\n\n function handleBackspace(event: KeyboardEvent): void {\n if (!(event.target instanceof HTMLInputElement))\n return;\n const currentSegment = getCurrentSegment(event.target.selectionStart ?? 0);\n if (!currentSegment)\n return;\n const value = get(currentValue) ?? getCurrent(currentSegment.type) ?? '';\n const updatedValue = value.toString().slice(0, -1);\n setValue(currentSegment.type, updatedValue.length === 0 ? undefined : parseInt(updatedValue));\n setCursorPosition(currentSegment);\n }\n\n function onInputDeletePressed(event: KeyboardEvent): void {\n if (!(event.target instanceof HTMLInputElement))\n return;\n const segment = getCurrentSegment(event.target.selectionStart ?? 0);\n if (segment) {\n clear(segment.type);\n setCursorPosition(segment);\n }\n }\n\n /**\n * Runs the picker's own handling for a key.\n *\n * @param event - the keydown being handled\n * @param key - the key that was pressed\n * @returns whether the picker handled it, which decides if the browser's own\n * handling is kept: Tab moves out of the field, Escape closes the menu and\n * Enter submits the surrounding form.\n */\n function dispatchKey(event: KeyboardEvent, key: string): boolean {\n if (key === 'ArrowRight' || key === 'ArrowLeft')\n navigateSegments(key);\n else if (key === 'ArrowUp' || key === 'ArrowDown')\n handleKeyboardNavigation(event);\n else if (key === 'Backspace')\n handleBackspace(event);\n else if (key === 'Delete')\n onInputDeletePressed(event);\n else if (/^\\d$/.test(key))\n handleDigitPressed(event, key);\n else\n return false;\n\n return true;\n }\n\n function handleKeyDown(event: KeyboardEvent): void {\n if (disabled || readonly)\n return;\n\n set(pasteRejected, false);\n // Modifier combos belong to the browser: swallowing them made copy and paste impossible\n if (event.ctrlKey || event.metaKey || event.altKey)\n return;\n\n if (dispatchKey(event, event.key))\n event.preventDefault();\n }\n\n // Track the segment that was clicked, so handleFocus can restore it after DOM updates\n let clickedSegment: Segment | undefined;\n\n /**\n * Captures the clicked segment on mousedown, which fires before focus.\n *\n * The in-progress digit buffer is reset because switching segments must not\n * carry typed digits over, otherwise a leftover digit from another segment\n * combines with the next keystroke (type \"1\" in HH, click mm, type \"3\" and\n * mm would become 13 instead of 3).\n */\n function handleMouseDown(event: MouseEvent): void {\n if (disabled || readonly || !(event.target instanceof HTMLInputElement))\n return;\n const currentSegment = getCurrentSegment(getClickPosition(event, event.target, true));\n if (currentSegment) {\n set(currentValue, undefined);\n clickedSegment = currentSegment;\n }\n }\n\n function handleClick(event: MouseEvent): void {\n if (disabled || readonly || !(event.target instanceof HTMLInputElement))\n return;\n const currentSegment = getCurrentSegment(getClickPosition(event, event.target, true));\n if (currentSegment) {\n set(currentValue, undefined);\n clickedSegment = currentSegment;\n set(cursorPosition, currentSegment.end);\n event.target.setSelectionRange(currentSegment.start, currentSegment.end);\n }\n }\n\n function handleInputSelection(event: Event): void {\n const target = event.target as HTMLInputElement;\n set(cursorPosition, target.selectionStart ?? 0);\n }\n\n function selectFirstSegment(): void {\n const firstSegment = get(segmentPositions)[0];\n if (firstSegment)\n setCursorPosition(firstSegment);\n }\n\n /**\n * Restores the selection a click asked for, which opening the menu and the\n * DOM updates that follow can lose. With no click to restore, focus selects\n * the first segment, unless the user is already selecting text themselves.\n */\n function handleFocus(): void {\n if (clickedSegment) {\n setCursorPosition(clickedSegment);\n clickedSegment = undefined;\n return;\n }\n\n const input = get(textInput);\n if (input && input.selectionStart !== input.selectionEnd)\n return;\n selectFirstSegment();\n }\n\n /**\n * Forgets the cursor position and the clicked segment, so re-focusing on\n * another segment does not blink through the one left behind.\n */\n function handleBlur(): void {\n set(cursorPosition, 0);\n set(currentValue, undefined);\n set(pasteRejected, false);\n clickedSegment = undefined;\n }\n\n /** Text that is no date raises `pasteRejected` until the next key, paste, input or blur. */\n function handlePaste(event: ClipboardEvent): void {\n if (disabled || readonly)\n return;\n\n if (!(event.target instanceof HTMLInputElement)) {\n return;\n }\n\n event.preventDefault();\n\n const pastedText = event.clipboardData?.getData('text');\n if (!pastedText) {\n return;\n }\n const parsed = parseAndSetDateValues(pastedText, get(dateFormat), accuracy, setValue, get(timezone));\n set(pasteRejected, !parsed);\n }\n\n function handleInput(event: Event): void {\n if (disabled || readonly)\n return;\n set(pasteRejected, false);\n\n if (!(event.target instanceof HTMLInputElement)) {\n return;\n }\n\n const inputText = event.target.value;\n if (!inputText) {\n return;\n }\n\n parseAndSetDateValues(inputText, get(dateFormat), accuracy, setValue);\n }\n\n return {\n clear,\n getCurrentSegment,\n handleBlur,\n handleClick,\n handleFocus,\n handleInput,\n handleInputSelection,\n handleKeyDown,\n handleMouseDown,\n handlePaste,\n pasteRejected: shallowReadonly(pasteRejected),\n selectFirstSegment,\n setSegment,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-keyboard-handler.js","names":[],"sources":["../../../src/components/date-time-picker/use-keyboard-handler.ts"],"sourcesContent":["import type { Dayjs } from 'dayjs';\nimport type { Ref, ShallowRef } from 'vue';\nimport type { TimeAccuracy } from '@/consts/time-accuracy';\nimport { type Segment, SEGMENT_CONFIG, SEGMENT_METHODS } from '@/components/date-time-picker/segment-config';\nimport { getClickPosition, parseAndSetDateValues } from '@/components/date-time-picker/segment-utils';\nimport {\n type DateTimeSegmentType,\n isDateTimeSegmentType,\n} from '@/components/date-time-picker/types';\nimport { assert } from '@/utils/assert';\n\ninterface KeyboardHandlerOptions {\n dateFormat: Ref<string>;\n cursorPosition: Ref<number>;\n currentValue: Ref<number | undefined>;\n textInput: Readonly<ShallowRef<HTMLInputElement | null>> | Ref<HTMLInputElement | undefined>;\n setValue: (segment: DateTimeSegmentType, value?: number) => void;\n getCurrent: (segment: DateTimeSegmentType) => number | undefined;\n getDateTime: () => Dayjs;\n disabled: boolean;\n readonly: boolean;\n accuracy: TimeAccuracy;\n timezone: Readonly<Ref<string | undefined>>;\n}\n\nexport function useKeyboardHandler(options: KeyboardHandlerOptions) {\n const {\n accuracy,\n currentValue,\n cursorPosition,\n dateFormat,\n disabled,\n getCurrent,\n getDateTime,\n readonly,\n setValue,\n textInput,\n timezone,\n } = options;\n\n const pasteRejected = shallowRef<boolean>(false);\n\n const formatSegments = computed<string[]>(() => {\n const format = get(dateFormat);\n return format.split(/([\\s./:])/g).filter(Boolean);\n });\n\n const segmentPositions = computed<Segment[]>(() => {\n const segments = get(formatSegments);\n const positions: Segment[] = [];\n let currentPosition = 0;\n\n segments.forEach((segment) => {\n if (!/[\\s./:]/.test(segment)) {\n assert(isDateTimeSegmentType(segment), `Invalid date format segment: ${segment}`);\n positions.push({\n end: currentPosition + segment.length,\n start: currentPosition,\n type: segment,\n });\n }\n currentPosition += segment.length;\n });\n\n return positions;\n });\n\n function getCurrentSegment(position: number = get(cursorPosition)) {\n const segments = get(segmentPositions);\n return segments.find(segment => position >= segment.start && position <= segment.end);\n }\n\n function setCursorPosition(segment: Segment): void {\n set(cursorPosition, segment.end);\n\n nextTick(() => {\n if (isDefined(textInput)) {\n const input = get(textInput);\n input.setSelectionRange(segment.start, segment.end);\n input.focus();\n }\n });\n }\n\n function setSegment(segmentType: DateTimeSegmentType): void {\n const segments = get(segmentPositions).find(segment => segment.type === segmentType);\n if (segments) {\n set(currentValue, undefined);\n setCursorPosition(segments);\n }\n }\n\n function clear(segmentType?: string): void {\n const currentSegment = getCurrentSegment();\n if (!segmentType && !currentSegment) {\n return;\n }\n\n const typeToUse = segmentType ?? currentSegment?.type;\n if (!typeToUse) {\n return;\n }\n\n setValue(typeToUse as DateTimeSegmentType, undefined);\n\n if (currentSegment) {\n setCursorPosition(currentSegment);\n }\n }\n\n function changeSegmentValue(increment: boolean): void {\n if (!isDefined(textInput))\n return;\n const currentSegment = getCurrentSegment();\n if (!currentSegment)\n return;\n const segmentType = currentSegment.type;\n const method = SEGMENT_METHODS[segmentType];\n const selectedDate = getDateTime();\n const updatedDate = selectedDate.set(method, selectedDate.get(method) + (increment ? 1 : -1));\n if (updatedDate.year() >= 1970) {\n setValue(\n segmentType,\n segmentType === 'MM' ? updatedDate.get(method) + 1 : updatedDate.get(method),\n );\n setCursorPosition(currentSegment);\n }\n }\n\n function navigateSegments(key: string) {\n const position = get(cursorPosition);\n const positions = get(segmentPositions);\n const currentSegmentIndex = positions.findIndex(\n segment => position >= segment.start && position <= segment.end,\n );\n\n if (currentSegmentIndex === -1) {\n return;\n }\n\n set(currentValue, undefined);\n\n let nextSegmentIndex: number;\n if (key === 'ArrowRight') {\n nextSegmentIndex = currentSegmentIndex + 1;\n if (nextSegmentIndex < positions.length) {\n const nextSegment = positions[nextSegmentIndex];\n assert(nextSegment);\n setCursorPosition(nextSegment);\n }\n }\n else {\n nextSegmentIndex = currentSegmentIndex - 1;\n if (nextSegmentIndex >= 0) {\n const nextSegment = positions[nextSegmentIndex];\n assert(nextSegment);\n setCursorPosition(nextSegment);\n }\n }\n }\n\n /**\n * Adds a typed digit to the segment under the caret, moving on to the next\n * segment once this one can hold no more.\n *\n * @param segment - the segment the caret sits in\n * @param segmentType - that segment's type, which states its value range\n * @param digit - the digit that was typed\n */\n function applyDigit(segment: Segment, segmentType: DateTimeSegmentType, digit: string): void {\n const config = SEGMENT_CONFIG[segmentType];\n const value = get(currentValue) ?? '';\n const combinedStr = `${value}${digit}`;\n const combinedValue = parseInt(combinedStr);\n const maxLength = segmentType.length;\n const minValue = config.minValue ?? 0;\n\n if (combinedValue >= minValue && combinedValue <= config.maxValue) {\n setValue(segmentType, combinedValue);\n setCursorPosition(segment);\n }\n else if (combinedValue < minValue && combinedStr.length < maxLength) {\n // Track digit even if below minValue, so next digit can combine (e.g., \"0\" allows \"01\")\n set(currentValue, combinedValue);\n }\n\n const willExceedMax = parseInt(`${combinedStr}0`) > config.maxValue;\n // Use string length of typed digits, not parsed number length (e.g., \"01\" has length 2)\n if (willExceedMax || combinedStr.length >= maxLength)\n navigateSegments('ArrowRight');\n }\n\n function handleDigitPressed(event: KeyboardEvent, digit: string): void {\n if (!(event.target instanceof HTMLInputElement))\n return;\n\n const currentSegment = getCurrentSegment(event.target.selectionStart ?? 0);\n if (!currentSegment || isNaN(parseInt(digit)))\n return;\n\n const segmentType = currentSegment.type;\n if (!isDateTimeSegmentType(segmentType))\n return;\n\n applyDigit(currentSegment, segmentType, digit);\n }\n\n function handleKeyboardNavigation(event: KeyboardEvent): void {\n if (event.key === 'ArrowUp')\n changeSegmentValue(true);\n else if (event.key === 'ArrowDown')\n changeSegmentValue(false);\n }\n\n function handleBackspace(event: KeyboardEvent): void {\n if (!(event.target instanceof HTMLInputElement))\n return;\n const currentSegment = getCurrentSegment(event.target.selectionStart ?? 0);\n if (!currentSegment)\n return;\n const value = get(currentValue) ?? getCurrent(currentSegment.type) ?? '';\n const updatedValue = value.toString().slice(0, -1);\n setValue(currentSegment.type, updatedValue.length === 0 ? undefined : parseInt(updatedValue));\n setCursorPosition(currentSegment);\n }\n\n function onInputDeletePressed(event: KeyboardEvent): void {\n if (!(event.target instanceof HTMLInputElement))\n return;\n const segment = getCurrentSegment(event.target.selectionStart ?? 0);\n if (segment) {\n clear(segment.type);\n setCursorPosition(segment);\n }\n }\n\n /**\n * Runs the picker's own handling for a key.\n *\n * @param event - the keydown being handled\n * @param key - the key that was pressed\n * @returns whether the picker handled it, which decides if the browser's own\n * handling is kept: Tab moves out of the field, Escape closes the menu and\n * Enter submits the surrounding form.\n */\n function dispatchKey(event: KeyboardEvent, key: string): boolean {\n if (key === 'ArrowRight' || key === 'ArrowLeft')\n navigateSegments(key);\n else if (key === 'ArrowUp' || key === 'ArrowDown')\n handleKeyboardNavigation(event);\n else if (key === 'Backspace')\n handleBackspace(event);\n else if (key === 'Delete')\n onInputDeletePressed(event);\n else if (/^\\d$/.test(key))\n handleDigitPressed(event, key);\n else\n return false;\n\n return true;\n }\n\n function handleKeyDown(event: KeyboardEvent): void {\n if (disabled || readonly)\n return;\n\n set(pasteRejected, false);\n // Modifier combos belong to the browser: swallowing them made copy and paste impossible\n if (event.ctrlKey || event.metaKey || event.altKey)\n return;\n\n if (dispatchKey(event, event.key))\n event.preventDefault();\n }\n\n // Track the segment that was clicked, so handleFocus can restore it after DOM updates\n let clickedSegment: Segment | undefined;\n\n /**\n * Captures the clicked segment on mousedown, which fires before focus.\n *\n * The in-progress digit buffer is reset because switching segments must not\n * carry typed digits over, otherwise a leftover digit from another segment\n * combines with the next keystroke (type \"1\" in HH, click mm, type \"3\" and\n * mm would become 13 instead of 3).\n */\n function handleMouseDown(event: MouseEvent): void {\n if (disabled || readonly || !(event.target instanceof HTMLInputElement))\n return;\n const currentSegment = getCurrentSegment(getClickPosition(event, event.target, true));\n if (currentSegment) {\n set(currentValue, undefined);\n clickedSegment = currentSegment;\n }\n }\n\n function handleClick(event: MouseEvent): void {\n if (disabled || readonly || !(event.target instanceof HTMLInputElement))\n return;\n const currentSegment = getCurrentSegment(getClickPosition(event, event.target, true));\n if (currentSegment) {\n set(currentValue, undefined);\n clickedSegment = currentSegment;\n set(cursorPosition, currentSegment.end);\n event.target.setSelectionRange(currentSegment.start, currentSegment.end);\n }\n }\n\n function handleInputSelection(event: Event): void {\n const target = event.target as HTMLInputElement;\n set(cursorPosition, target.selectionStart ?? 0);\n }\n\n function selectFirstSegment(): void {\n const firstSegment = get(segmentPositions)[0];\n if (firstSegment)\n setCursorPosition(firstSegment);\n }\n\n /**\n * Restores the selection a click asked for, which opening the menu and the\n * DOM updates that follow can lose. With no click to restore, focus selects\n * the first segment, unless the user is already selecting text themselves.\n */\n function handleFocus(): void {\n if (clickedSegment) {\n setCursorPosition(clickedSegment);\n clickedSegment = undefined;\n return;\n }\n\n const input = get(textInput);\n if (input && input.selectionStart !== input.selectionEnd)\n return;\n selectFirstSegment();\n }\n\n /**\n * Forgets the cursor position and the clicked segment, so re-focusing on\n * another segment does not blink through the one left behind.\n */\n function handleBlur(): void {\n set(cursorPosition, 0);\n set(currentValue, undefined);\n set(pasteRejected, false);\n clickedSegment = undefined;\n }\n\n /** Text that is no date raises `pasteRejected` until the next key, paste, input or blur. */\n function handlePaste(event: ClipboardEvent): void {\n if (disabled || readonly)\n return;\n\n if (!(event.target instanceof HTMLInputElement)) {\n return;\n }\n\n event.preventDefault();\n\n const pastedText = event.clipboardData?.getData('text');\n if (!pastedText) {\n return;\n }\n const parsed = parseAndSetDateValues(pastedText, get(dateFormat), accuracy, setValue, get(timezone));\n set(pasteRejected, !parsed);\n }\n\n function handleInput(event: Event): void {\n if (disabled || readonly)\n return;\n set(pasteRejected, false);\n\n if (!(event.target instanceof HTMLInputElement)) {\n return;\n }\n\n const inputText = event.target.value;\n if (!inputText) {\n return;\n }\n\n parseAndSetDateValues(inputText, get(dateFormat), accuracy, setValue);\n }\n\n return {\n clear,\n getCurrentSegment,\n handleBlur,\n handleClick,\n handleFocus,\n handleInput,\n handleInputSelection,\n handleKeyDown,\n handleMouseDown,\n handlePaste,\n pasteRejected: shallowReadonly(pasteRejected),\n selectFirstSegment,\n setSegment,\n };\n}\n"],"mappings":"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"}
|
package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { useLabelPlacement } from "../../../composables/defaults/field.js";
|
|
2
|
+
import { get, set } from "../../../vendor/@vueuse/shared/dist/index.js";
|
|
3
|
+
import { useFocus, useFocusWithin } from "../../../vendor/@vueuse/core/dist/index.js";
|
|
2
4
|
import { cn } from "../../../utils/tv.js";
|
|
3
5
|
import RuiIcon_default from "../../icons/RuiIcon.js";
|
|
4
6
|
import RuiProgress_default from "../../progress/RuiProgress.js";
|
|
@@ -21,8 +23,6 @@ import { useAutoCompleteSearch } from "../../../composables/forms/auto-complete/
|
|
|
21
23
|
import { useAutoCompleteSelection } from "../../../composables/forms/auto-complete/selection.js";
|
|
22
24
|
import { useAutoCompleteValue } from "../../../composables/forms/auto-complete/value.js";
|
|
23
25
|
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createVNode, defineComponent, guardReactiveProps, isRef, mergeModels, mergeProps, normalizeClass, normalizeProps, normalizeStyle, openBlock, ref, renderSlot, shallowRef, toDisplayString, unref, useId, useModel, useSlots, useTemplateRef, withCtx, withKeys, withModifiers } from "vue";
|
|
24
|
-
import { useFocus, useFocusWithin } from "@vueuse/core";
|
|
25
|
-
import { get as get$1, set as set$1 } from "@vueuse/shared";
|
|
26
26
|
//#region src/components/forms/auto-complete/RuiAutoComplete.vue?vue&type=script&setup=true&lang.ts
|
|
27
27
|
var _hoisted_1 = {
|
|
28
28
|
key: 0,
|
|
@@ -182,7 +182,7 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
182
182
|
const activator = useTemplateRef("activator");
|
|
183
183
|
const menuRef = shallowRef(null);
|
|
184
184
|
function setMenuRef(element) {
|
|
185
|
-
set
|
|
185
|
+
set(menuRef, element instanceof HTMLElement ? element : null);
|
|
186
186
|
}
|
|
187
187
|
const menuWrapperRef = useTemplateRef("menuWrapperRef");
|
|
188
188
|
const { focused: activatorFocusedWithin } = useFocusWithin(activator);
|
|
@@ -201,8 +201,8 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
201
201
|
});
|
|
202
202
|
const isOpen = ref(false);
|
|
203
203
|
const isHovered = ref(false);
|
|
204
|
-
const multiple = computed(() => Array.isArray(get
|
|
205
|
-
const shouldApplyValueAsSearch = computed(() => !(slots.selection || get
|
|
204
|
+
const multiple = computed(() => Array.isArray(get(modelValue)));
|
|
205
|
+
const shouldApplyValueAsSearch = computed(() => !(slots.selection || get(multiple) || __props.chips));
|
|
206
206
|
const { value } = useAutoCompleteValue(modelValue, () => __props.options, {
|
|
207
207
|
keyAttr: () => __props.keyAttr,
|
|
208
208
|
returnObject: () => __props.returnObject,
|
|
@@ -261,7 +261,7 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
261
261
|
value
|
|
262
262
|
});
|
|
263
263
|
const { chipAttrs, selectChip } = useAutoCompleteChips({
|
|
264
|
-
focusInput: () => get
|
|
264
|
+
focusInput: () => get(textInput)?.focus(),
|
|
265
265
|
getIdentifier,
|
|
266
266
|
setValue: (item) => {
|
|
267
267
|
setValue(item);
|
|
@@ -270,7 +270,7 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
270
270
|
});
|
|
271
271
|
/** The selected chip's text, read out by the live region so the selection is heard, not only seen. */
|
|
272
272
|
const selectedChipAnnouncement = computed(() => {
|
|
273
|
-
const item = get
|
|
273
|
+
const item = get(value)[get(focusedValueIndex)];
|
|
274
274
|
return __props.chips && item !== void 0 ? `${getText(item) ?? ""}, press Backspace to remove` : "";
|
|
275
275
|
});
|
|
276
276
|
const { anyFocused: focusAnyFocused, editing, inputClass: focusInputClass, onInputFocused: focusOnInputFocused, onSettledKeydown, setInputFocus: focusSetInputFocus, settleOnInput, unsettle } = useAutoCompleteFocus({
|
|
@@ -295,7 +295,7 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
295
295
|
isOpen,
|
|
296
296
|
itemIndexInValue,
|
|
297
297
|
multiple,
|
|
298
|
-
resetModel: () => set
|
|
298
|
+
resetModel: () => set(modelValue, Array.isArray(get(modelValue)) ? [] : void 0),
|
|
299
299
|
searchInputFocused,
|
|
300
300
|
settleOnInput,
|
|
301
301
|
shouldApplyValueAsSearch,
|
|
@@ -303,25 +303,25 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
303
303
|
updateInternalSearch,
|
|
304
304
|
value
|
|
305
305
|
});
|
|
306
|
-
const menuMinHeight = computed(() => Math.min(5, get
|
|
306
|
+
const menuMinHeight = computed(() => Math.min(5, get(optionsWithSelectedHidden).length) * resolvedItemHeight);
|
|
307
307
|
const { hasError, hasSuccess } = useFormTextDetail(() => __props.errorMessages, () => __props.successMessages);
|
|
308
|
-
const valueSet = computed(() => get
|
|
308
|
+
const valueSet = computed(() => get(value).length > 0);
|
|
309
309
|
const usedPlaceholder = computed(() => {
|
|
310
|
-
if (get
|
|
310
|
+
if (get(editing)) return __props.placeholder;
|
|
311
311
|
return "";
|
|
312
312
|
});
|
|
313
313
|
const labelId = useId();
|
|
314
314
|
const placement = useLabelPlacement(() => __props.labelPlacement);
|
|
315
315
|
const { activeDescendant, optionIdPrefix } = useAutoCompleteCombobox(isOpen, modelHighlightedIndex);
|
|
316
|
-
const restingPlaceholder = computed(() => !!__props.placeholder && !get
|
|
316
|
+
const restingPlaceholder = computed(() => !!__props.placeholder && !get(valueSet) && !get(editing) && !slots.placeholder);
|
|
317
317
|
const ui = computed(() => autoCompleteStyles({
|
|
318
|
-
opened: get
|
|
319
|
-
hovered: get
|
|
318
|
+
opened: get(isOpen),
|
|
319
|
+
hovered: get(isHovered),
|
|
320
320
|
dense: __props.dense,
|
|
321
321
|
disabled: __props.disabled,
|
|
322
322
|
readonly: __props.readOnly,
|
|
323
|
-
hasError: get
|
|
324
|
-
hasSuccess: get
|
|
323
|
+
hasError: get(hasError),
|
|
324
|
+
hasSuccess: get(hasSuccess) && !get(hasError)
|
|
325
325
|
}));
|
|
326
326
|
const highlightedClass = autoCompleteStyles({}).highlighted();
|
|
327
327
|
function updateSearchInput(event) {
|
|
@@ -329,9 +329,9 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
329
329
|
if (!(target instanceof HTMLInputElement)) return;
|
|
330
330
|
const value = target.value;
|
|
331
331
|
unsettle();
|
|
332
|
-
set
|
|
332
|
+
set(isOpen, true);
|
|
333
333
|
updateInternalSearch(value);
|
|
334
|
-
set
|
|
334
|
+
set(justOpened, false);
|
|
335
335
|
}
|
|
336
336
|
/**
|
|
337
337
|
* Moves the highlight through the options, which also leaves the picked-value display for a search.
|
|
@@ -343,20 +343,20 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
|
|
|
343
343
|
moveHighlight(up);
|
|
344
344
|
}
|
|
345
345
|
function setSelectionRange(start, end) {
|
|
346
|
-
set
|
|
347
|
-
get
|
|
346
|
+
set(searchInputFocused, true);
|
|
347
|
+
get(textInput)?.setSelectionRange?.(start, end);
|
|
348
348
|
}
|
|
349
349
|
function arrowClicked(event) {
|
|
350
|
-
if (get
|
|
351
|
-
set
|
|
350
|
+
if (get(isOpen)) {
|
|
351
|
+
set(isOpen, false);
|
|
352
352
|
event.stopPropagation();
|
|
353
353
|
}
|
|
354
354
|
}
|
|
355
355
|
function openMenu() {
|
|
356
|
-
set
|
|
356
|
+
set(isOpen, true);
|
|
357
357
|
}
|
|
358
358
|
function closeMenu() {
|
|
359
|
-
set
|
|
359
|
+
set(isOpen, false);
|
|
360
360
|
}
|
|
361
361
|
const menuFloatingOptions = computed(() => ({
|
|
362
362
|
placement: Placement.bottomStart,
|