@rotki/ui-library 3.0.0-beta.1 → 3.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/dist/components/accordions/accordion/RuiAccordion.vue_vue_type_script_setup_true_lang.js +3 -3
  2. package/dist/components/accordions/accordion/RuiAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  3. package/dist/components/accordions/accordions/RuiAccordions.vue_vue_type_script_setup_true_lang.js +1 -1
  4. package/dist/components/avatars/RuiAvatar.vue_vue_type_script_setup_true_lang.js +1 -1
  5. package/dist/components/avatars/RuiAvatarGroup.vue_vue_type_script_setup_true_lang.js +1 -1
  6. package/dist/components/buttons/button/RuiButton.js.map +1 -1
  7. package/dist/components/buttons/button/RuiButton.vue_vue_type_script_setup_true_lang.js +10 -10
  8. package/dist/components/buttons/button/RuiButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  9. package/dist/components/buttons/button/button-styles.js +10 -2
  10. package/dist/components/buttons/button/button-styles.js.map +1 -1
  11. package/dist/components/buttons/button-group/RuiButtonGroup.vue_vue_type_script_setup_true_lang.js +1 -1
  12. package/dist/components/calendar/RuiCalendar.vue_vue_type_script_setup_true_lang.js +1 -1
  13. package/dist/components/calendar/RuiCalendarGrid.vue_vue_type_script_setup_true_lang.js +1 -1
  14. package/dist/components/calendar/RuiCalendarHeader.vue_vue_type_script_setup_true_lang.js +1 -1
  15. package/dist/components/calendar/RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  16. package/dist/components/chips/RuiChip.vue_vue_type_script_setup_true_lang.js +1 -1
  17. package/dist/components/color-picker/RuiColorBoard.vue_vue_type_script_setup_true_lang.js +1 -1
  18. package/dist/components/color-picker/RuiColorDisplay.vue_vue_type_script_setup_true_lang.js +1 -1
  19. package/dist/components/color-picker/RuiColorHue.vue_vue_type_script_setup_true_lang.js +1 -1
  20. package/dist/components/color-picker/RuiColorInput.vue_vue_type_script_setup_true_lang.js +9 -10
  21. package/dist/components/color-picker/RuiColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  22. package/dist/components/color-picker/RuiColorPicker.vue_vue_type_script_setup_true_lang.js +3 -4
  23. package/dist/components/color-picker/RuiColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  24. package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js +33 -33
  25. package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  26. package/dist/components/date-time-picker/use-date-bounds.js +1 -1
  27. package/dist/components/date-time-picker/use-date-time-selection.js +65 -66
  28. package/dist/components/date-time-picker/use-date-time-selection.js.map +1 -1
  29. package/dist/components/date-time-picker/use-input-handler.js +1 -1
  30. package/dist/components/date-time-picker/use-keyboard-handler.js +29 -30
  31. package/dist/components/date-time-picker/use-keyboard-handler.js.map +1 -1
  32. package/dist/components/forms/auto-complete/RuiAutoComplete.js.map +1 -1
  33. package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js +25 -24
  34. package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js.map +1 -1
  35. package/dist/components/forms/auto-complete/auto-complete-styles.d.ts +3 -53
  36. package/dist/components/forms/auto-complete/auto-complete-styles.js +2 -1
  37. package/dist/components/forms/auto-complete/auto-complete-styles.js.map +1 -1
  38. package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js +49 -49
  39. package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  40. package/dist/components/forms/checkbox/RuiCheckbox.vue_vue_type_script_setup_true_lang.js +1 -1
  41. package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue_vue_type_script_setup_true_lang.js +1 -1
  42. package/dist/components/forms/file-upload/RuiFileUpload.js.map +1 -1
  43. package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js +19 -17
  44. package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js.map +1 -1
  45. package/dist/components/forms/radio-button/radio/RuiRadio.vue_vue_type_script_setup_true_lang.js +1 -1
  46. package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js +11 -11
  47. package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  48. package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js +17 -17
  49. package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  50. package/dist/components/forms/switch/RuiSwitch.vue_vue_type_script_setup_true_lang.js +1 -1
  51. package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js +21 -21
  52. package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  53. package/dist/components/forms/text-field/RuiTextField.js.map +1 -1
  54. package/dist/components/forms/text-field/RuiTextField.vue.d.ts +2 -0
  55. package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js +24 -20
  56. package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
  57. package/dist/components/forms/text-input-styles.js +1 -1
  58. package/dist/components/forms/text-input-styles.js.map +1 -1
  59. package/dist/components/helpers/RuiFormTextDetail.js.map +1 -1
  60. package/dist/components/helpers/RuiFormTextDetail.vue.d.ts +11 -1
  61. package/dist/components/helpers/RuiFormTextDetail.vue_vue_type_script_setup_true_lang.js +3 -3
  62. package/dist/components/helpers/RuiFormTextDetail.vue_vue_type_script_setup_true_lang.js.map +1 -1
  63. package/dist/components/icons/RuiIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  64. package/dist/components/logos/RuiLogo.vue_vue_type_script_setup_true_lang.js +1 -1
  65. package/dist/components/overlays/badge/RuiBadge.vue_vue_type_script_setup_true_lang.js +1 -1
  66. package/dist/components/overlays/dialog/RuiDialog.vue_vue_type_script_setup_true_lang.js +1 -1
  67. package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js +17 -17
  68. package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  69. package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.js.map +1 -1
  70. package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue_vue_type_script_setup_true_lang.js +9 -9
  71. package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  72. package/dist/components/overlays/notification/RuiNotification.vue_vue_type_script_setup_true_lang.js +3 -4
  73. package/dist/components/overlays/notification/RuiNotification.vue_vue_type_script_setup_true_lang.js.map +1 -1
  74. package/dist/components/overlays/tooltip/RuiTooltip.vue_vue_type_script_setup_true_lang.js +1 -1
  75. package/dist/components/progress/RuiProgress.vue_vue_type_script_setup_true_lang.js +1 -1
  76. package/dist/components/steppers/RuiFooterStepper.vue_vue_type_script_setup_true_lang.js +1 -1
  77. package/dist/components/steppers/RuiStepper.js.map +1 -1
  78. package/dist/components/steppers/RuiStepper.vue_vue_type_script_setup_true_lang.js +13 -8
  79. package/dist/components/steppers/RuiStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
  80. package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js +27 -27
  81. package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  82. package/dist/components/tables/RuiTableHead.vue_vue_type_script_setup_true_lang.js +1 -1
  83. package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js +1 -1
  84. package/dist/components/tables/data-table/RuiDataTableCell.vue_vue_type_script_setup_true_lang.js +1 -1
  85. package/dist/components/tables/data-table/RuiDataTableGroupRow.vue_vue_type_script_setup_true_lang.js +1 -1
  86. package/dist/components/tables/data-table/RuiDataTableMobileSort.vue_vue_type_script_setup_true_lang.js +1 -1
  87. package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js +23 -23
  88. package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
  89. package/dist/components/tables/use-pagination-navigation.js +1 -1
  90. package/dist/components/tabs/tab/RuiTab.vue_vue_type_script_setup_true_lang.js +1 -1
  91. package/dist/components/tabs/tab/use-tab-link.js +1 -1
  92. package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js +6 -6
  93. package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js.map +1 -1
  94. package/dist/components/tabs/tabs/RuiTabs.vue_vue_type_script_setup_true_lang.js +11 -12
  95. package/dist/components/tabs/tabs/RuiTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  96. package/dist/components/tabs/tabs/use-tab-indicator.js +7 -7
  97. package/dist/components/tabs/tabs/use-tab-indicator.js.map +1 -1
  98. package/dist/components/tabs/tabs/use-tab-keyboard.js +1 -1
  99. package/dist/components/tabs/tabs/use-tab-scroll.js +15 -15
  100. package/dist/components/tabs/tabs/use-tab-scroll.js.map +1 -1
  101. package/dist/components/time-picker/RuiTimePicker.vue_vue_type_script_setup_true_lang.js +60 -61
  102. package/dist/components/time-picker/RuiTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  103. package/dist/composables/breakpoint.js +3 -3
  104. package/dist/composables/breakpoint.js.map +1 -1
  105. package/dist/composables/dropdown-menu.d.ts +9 -9
  106. package/dist/composables/dropdown-menu.js +32 -32
  107. package/dist/composables/dropdown-menu.js.map +1 -1
  108. package/dist/composables/floating.d.ts +1 -1
  109. package/dist/composables/floating.js +11 -11
  110. package/dist/composables/floating.js.map +1 -1
  111. package/dist/composables/forms/auto-complete/focus.js +16 -17
  112. package/dist/composables/forms/auto-complete/focus.js.map +1 -1
  113. package/dist/composables/forms/auto-complete/keyboard-navigation.js +1 -1
  114. package/dist/composables/forms/auto-complete/search.js +6 -7
  115. package/dist/composables/forms/auto-complete/search.js.map +1 -1
  116. package/dist/composables/forms/auto-complete/selection.js +1 -1
  117. package/dist/composables/forms/auto-complete/value.js +1 -1
  118. package/dist/composables/forms/category-picker/use-category-picker.js +1 -1
  119. package/dist/composables/forms/use-prepend-append-width.js +6 -6
  120. package/dist/composables/forms/use-prepend-append-width.js.map +1 -1
  121. package/dist/composables/keyboard-scroll.js +5 -5
  122. package/dist/composables/keyboard-scroll.js.map +1 -1
  123. package/dist/composables/overlay-stack.js +1 -1
  124. package/dist/composables/sticky-header.js +8 -8
  125. package/dist/composables/sticky-header.js.map +1 -1
  126. package/dist/composables/tables/data-table/columns.js +1 -1
  127. package/dist/composables/tables/data-table/expansion.js +1 -1
  128. package/dist/composables/tables/data-table/grouping.js +27 -28
  129. package/dist/composables/tables/data-table/grouping.js.map +1 -1
  130. package/dist/composables/tables/data-table/page-scroll.d.ts +7 -5
  131. package/dist/composables/tables/data-table/page-scroll.js +48 -17
  132. package/dist/composables/tables/data-table/page-scroll.js.map +1 -1
  133. package/dist/composables/tables/data-table/pagination.js +1 -1
  134. package/dist/composables/tables/data-table/selection.js +37 -38
  135. package/dist/composables/tables/data-table/selection.js.map +1 -1
  136. package/dist/composables/tables/data-table/sort.js +1 -1
  137. package/dist/composables/theme.js +12 -12
  138. package/dist/composables/theme.js.map +1 -1
  139. package/dist/composables/timeout-manager.js +1 -1
  140. package/dist/index.d.ts +2 -2
  141. package/dist/index.js +2 -2
  142. package/dist/index.js.map +1 -1
  143. package/dist/style.css +6 -18
  144. package/dist/types/dom.d.ts +9 -0
  145. package/dist/types/theme.d.ts +2 -3
  146. package/dist/types/theme.js.map +1 -1
  147. package/dist/utils/form-text-detail.js +1 -1
  148. package/dist/utils/helpers.js +1 -1
  149. package/dist/vendor/@vueuse/core/dist/index.js +1577 -0
  150. package/dist/vendor/@vueuse/core/dist/index.js.map +1 -0
  151. package/dist/vendor/@vueuse/shared/dist/index.js +563 -0
  152. package/dist/vendor/@vueuse/shared/dist/index.js.map +1 -0
  153. package/dist/vite-plugin/index.js +5 -6
  154. package/dist/vite-plugin/index.js.map +1 -1
  155. package/dist/web-types.json +9 -2
  156. 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":";;;;;;;;;AA2DA,IAAM,eAAe;AAErB,SAAgB,qBACd,SACyB;CACzB,MAAM,EACJ,UACA,YACA,YACA,SACA,SACA,YACA,aACA,SACE;CAEJ,MAAM,YAAY,IAAwB;CAC1C,MAAM,aAAa,IAAwB;CAC3C,MAAM,WAAW,IAAwB;CAEzC,MAAM,YAAY,IAAwB;CAC1C,MAAM,cAAc,IAAwB;CAC5C,MAAM,cAAc,IAAwB;CAC5C,MAAM,mBAAmB,IAAwB;CACjD,MAAM,gBAAgB,IAAwB,cAAc,CAAC;CAE7D,MAAM,MAAM,IAAW,MAAM,GAAG,KAAA,GAAW,cAAc,CAAC,CAAC;CAE3D,MAAM,EAAE,aAAa,uBAAuB,aAAa,gBAAgB,mBAAmB,cAAc;EACxG;EACA,cAAc,SAAS;EACvB;EACA;EACA;CACF,CAAC;CAED,MAAM,cAA2B;EAC/B,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,KAAK;EACL,MAAM;EACN,IAAI;EACJ,IAAI;CACN;CAEA,MAAM,eAAe,SAA2B;EAC9C,MAAM;GACJ,IAAI,EAAE,UAAU,SAAS,KAAK,UAAU,UAAU,KAAK,UAAU,QAAQ,IACvE;GAEF,MAAM,uBAAO,IAAI,KAAK;GACtB,KAAK,YAAY,MAAI,SAAS,CAAC;GAE/B,KAAK,QAAQ,CAAC;GACd,KAAK,SAAS,MAAI,UAAU,IAAI,CAAC;GACjC,KAAK,QAAQ,MAAI,QAAQ,CAAC;GAC1B,OAAO;EACT;EACA,IAAI,OAAc;GAChB,MAAI,WAAW,OAAO,YAAY,CAAC;GACnC,MAAI,YAAY,QAAQ,MAAM,SAAS,IAAI,IAAI,KAAA,CAAS;GACxD,MAAI,UAAU,OAAO,QAAQ,CAAC;EAChC;CACF,CAAC;CAED,MAAM,eAAe,SAA2B;EAC9C,MAAM;GACJ,IAAI,EAAE,UAAU,SAAS,KAAK,UAAU,WAAW,IACjD;GAEF,MAAM,uBAAO,IAAI,KAAK;GACtB,KAAK,SACH,MAAI,SAAS,GACb,MAAI,WAAW,GACf,MAAI,WAAW,KAAK,GACpB,MAAI,gBAAgB,KAAK,CAC3B;GACA,OAAO;EACT;EACA,IAAI,OAAc;GAChB,MAAI,WAAW,OAAO,SAAS,CAAC;GAChC,MAAI,aAAa,OAAO,WAAW,CAAC;GACpC,MAAI,aAAa,OAAO,WAAW,CAAC;GACpC,MAAI,kBAAkB,OAAO,gBAAgB,CAAC;EAChD;CACF,CAAC;CAED,MAAM,WAAW,eAAwB,UAAU,YAAY,KAAK,UAAU,YAAY,CAAC;CAE3F,SAAS,cAAqB;EAC5B,OAAO,cAAc;GACnB,KAAK,MAAI,QAAQ;GACjB,MAAM,MAAI,SAAS;GACnB,aAAa,MAAI,gBAAgB;GACjC,QAAQ,MAAI,WAAW;GACvB,OAAO,MAAI,UAAU;GACrB,QAAQ,MAAI,WAAW;GACvB,MAAM,MAAI,SAAS;EACrB,GAAG,QAAQ;CACb;CAEA,SAAS,WAAW,cAA2B;EAQ7C,MAAI,YAAY;GANd,cAAc,aAAa,OAAO;GAElC,eAAe,KAAK,MAAM,aAAa,QAAQ,IAAI,YAAY;GAC/D,kBAAkB,aAAa,QAAQ;EAGzB,EAAQ,KAAK,CAAC,CAAsB;CACtD;;;;;;;;;CAUA,SAAS,mBAAyB;EAChC,IAAI,CAAC,UAAU,YAAY,KAAK,CAAC,UAAU,YAAY,GACrD;EAGF,MAAM,eAAe,MAAM,GACzB,gBAAgB,MAAI,YAAY,GAAG,MAAI,YAAY,GAAG,QAAQ,GAC9D,MAAI,aAAa,CACnB;EAEA,IAAI,CAAC,YAAY,YAAY,GAC3B;EAGF,WAAW,YAAY;CACzB;CAEA,SAAS,QAAc;EACrB,YAAY;EACZ,MAAI,WAAW,KAAA,CAAS;EACxB,MAAI,YAAY,KAAA,CAAS;EACzB,MAAI,UAAU,KAAA,CAAS;EACvB,MAAI,WAAW,KAAA,CAAS;EACxB,MAAI,aAAa,KAAA,CAAS;EAC1B,MAAI,aAAa,KAAA,CAAS;EAC1B,MAAI,kBAAkB,KAAA,CAAS;EAC/B,MAAI,YAAY,KAAA,CAA8B;CAChD;CAEA,SAAS,eAAe,MAAoB;EAC1C,OAAO,cAAc,MAAM,MAAI,cAAc,GAAG,MAAI,cAAc,CAAC;CACrE;CAEA,SAAS,cAAc,MAAmB;EACxC,MAAI,WAAW,KAAK,KAAK,CAAC;EAC1B,MAAI,YAAY,KAAK,MAAM,IAAI,CAAC;EAChC,MAAI,UAAU,KAAK,KAAK,CAAC;EACzB,MAAI,WAAW,KAAK,KAAK,CAAC;EAC1B,MAAI,aAAa,KAAK,OAAO,CAAC;EAC9B,MAAI,aAAa,eAAe,QAAQ,IAAI,KAAK,OAAO,IAAI,CAAC;EAC7D,MAAI,kBAAkB,oBAAoB,QAAQ,IAAI,KAAK,YAAY,IAAI,CAAC;CAC9E;CAEA,SAAS,SAAe;EACtB,YAAY;EAEZ,MAAM,OAAO,MAAM;EACnB,MAAI,KAAK,IAAI;EACb,cAAc,eAAe,IAAI,CAAC;EAElC,eAAe;GACb,iBAAiB;EACnB,CAAC;CACH;;;;;CAMA,SAAS,WAAiB;EACxB,YAAY;EAEZ,MAAM,OAAO,MAAM;EACnB,MAAI,KAAK,IAAI;EAYb,cAAc,eAVC,cAAc;GAC3B,KAAK,KAAK,KAAK;GACf,MAAM,MAAI,SAAS,KAAK;GACxB,aAAa,MAAI,gBAAgB,KAAK;GACtC,QAAQ,MAAI,WAAW,KAAK;GAC5B,OAAO,KAAK,MAAM,IAAI;GACtB,QAAQ,MAAI,WAAW,KAAK;GAC5B,MAAM,KAAK,KAAK;EAClB,GAAG,UAAU,IAEgB,CAAM,CAAC;EAEpC,eAAe;GACb,iBAAiB;EACnB,CAAC;CACH;CAEA,SAAS,eAAe,MAAmB;EACzC,MAAM,OAAO,KAAK,KAAK;EACvB,MAAM,QAAQ,KAAK,MAAM,IAAI;EAC7B,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,OAAO,KAAK,KAAK;EACvB,MAAM,SAAS,KAAK,OAAO;EAC3B,MAAM,SAAS,eAAe,QAAQ,IAAI,KAAK,OAAO,IAAI,KAAA;EAC1D,MAAM,cAAc,oBAAoB,QAAQ,IAAI,KAAK,YAAY,IAAI,KAAA;EAEzE,IAAI,MAAI,SAAS,MAAM,MACrB,MAAI,WAAW,IAAI;EACrB,IAAI,MAAI,UAAU,MAAM,OACtB,MAAI,YAAY,KAAK;EACvB,IAAI,MAAI,QAAQ,MAAM,KACpB,MAAI,UAAU,GAAG;EACnB,IAAI,MAAI,SAAS,MAAM,MACrB,MAAI,WAAW,IAAI;EACrB,IAAI,MAAI,WAAW,MAAM,QACvB,MAAI,aAAa,MAAM;EACzB,IAAI,MAAI,WAAW,MAAM,QACvB,MAAI,aAAa,MAAM;EACzB,IAAI,MAAI,gBAAgB,MAAM,aAC5B,MAAI,kBAAkB,WAAW;CACrC;CAEA,MAAM,EAAE,kBAAkB,eAAe,CAAC,cAAc,YAAY,IAAI,CAAC,iBAAiB,kBAAkB,CAAC,kBAAkB,sBAAsB;EACnJ,MAAM,kBAAkB,MAAI,aAAa;EACzC,MAAM,UAAU,MAAM,GAAG,iBAAiB,eAAe;EACzD,MAAM,UAAU,MAAM,GAAG,kBAAkB,eAAe;EAC1D,MAAM,UAAU,MAAM,GAAG,iBAAiB,eAAe;EACzD,MAAM,UAAU,MAAM,GAAG,kBAAkB,eAAe;EAE1D,IAAI,QAAQ,OAAO,OAAO,KAAK,QAAQ,OAAO,OAAO,GACnD;EAGF,MAAI,KAAK,MAAM,CAAC;EAChB,iBAAiB;CACnB,CAAC;;;;;;;;;;CAWD,SAAS,oBAA0B;EACjC,IAAI,gBAAgB,KAAA,GAClB;EAEF,MAAM,SAAS,qBAAqB;GAClC,KAAK,MAAI,QAAQ;GACjB,MAAM,MAAI,SAAS;GACnB,aAAa,MAAI,gBAAgB;GACjC,QAAQ,MAAI,WAAW;GACvB,OAAO,MAAI,UAAU;GACrB,QAAQ,MAAI,WAAW;GACvB,MAAM,MAAI,SAAS;EACrB,GAAG;GACD;GACA,SAAS,MAAI,cAAc;GAC3B,SAAS,MAAI,cAAc;GAC3B,MAAM;EACR,CAAC;EAED,IAAI,CAAC,QACH;EAEF,oBAAoB,cAAc,MAAM,CAAC;EACzC,iBAAiB;CACnB;CAEA,SAAS,oBAAoB,OAAgC;EAC3D,oBAAoB;GAClB,MAAM,eAAe,SAAS,WAAW,OAAO,UAAU,WACtD,QAAQ,eACR;GACJ,MAAM,OAAO,MAAM,GAAG,cAAc,MAAI,aAAa,CAAC;GACtD,YAAY,IAAI;GAChB,eAAe,IAAI;EACrB,CAAC;CACH;CAEA,MAAM,gBAAgB,gBAAoC;EACxD,IAAI,eAAe,UAAU,YAAY,KAAK,UAAU,YAAY,GAAG;GACrE,MAAI,KAAK,MAAM,CAAC;GAChB,iBAAiB;EACnB;CACF,CAAC;CAED,MAAM,aAAa,UAAU;EAC3B,MAAI,KAAK,MAAM,CAAC;EAChB,IAAI,UAAU,KAAA,GACZ,MAAM;OAGN,oBAAoB,KAAK;CAE7B,CAAC;CAED,gBAAgB;EACd,IAAI,UAAU,UAAU,GACtB,oBAAoB,MAAI,UAAU,CAAC;OAEhC,IAAI,CAAC,YACR,OAAO;CAEX,CAAC;CAED,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,KAAK,gBAAgB,GAAG;EACxB;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
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$1(dateFormat).split(/([\s./:])/g).filter(Boolean);
12
+ return get(dateFormat).split(/([\s./:])/g).filter(Boolean);
14
13
  });
15
14
  const segmentPositions = computed(() => {
16
- const segments = get$1(formatSegments);
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$1(cursorPosition)) {
33
- return get$1(segmentPositions).find((segment) => position >= segment.start && position <= segment.end);
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$1(cursorPosition, segment.end);
35
+ set(cursorPosition, segment.end);
37
36
  nextTick(() => {
38
37
  if (isDefined(textInput)) {
39
- const input = get$1(textInput);
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$1(segmentPositions).find((segment) => segment.type === segmentType);
45
+ const segments = get(segmentPositions).find((segment) => segment.type === segmentType);
47
46
  if (segments) {
48
- set$1(currentValue, void 0);
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$1(cursorPosition);
75
- const positions = get$1(segmentPositions);
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$1(currentValue, void 0);
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$1(currentValue) ?? ""}${digit}`;
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$1(currentValue, combinedValue);
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$1(currentValue) ?? getCurrent(currentSegment.type) ?? "").toString().slice(0, -1);
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$1(pasteRejected, false);
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$1(currentValue, void 0);
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$1(currentValue, void 0);
188
+ set(currentValue, void 0);
190
189
  clickedSegment = currentSegment;
191
- set$1(cursorPosition, currentSegment.end);
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$1(cursorPosition, target.selectionStart ?? 0);
196
+ set(cursorPosition, target.selectionStart ?? 0);
198
197
  }
199
198
  function selectFirstSegment() {
200
- const firstSegment = get$1(segmentPositions)[0];
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$1(textInput);
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$1(cursorPosition, 0);
224
- set$1(currentValue, void 0);
225
- set$1(pasteRejected, false);
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$1(dateFormat), accuracy, setValue, get$1(timezone));
236
- set$1(pasteRejected, !parsed);
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$1(pasteRejected, false);
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$1(dateFormat), accuracy, setValue);
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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RuiAutoComplete.js","names":[],"sources":["../../../../src/components/forms/auto-complete/RuiAutoComplete.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { AutoCompleteModelValue, AutoCompleteProps } from '@/components/forms/auto-complete/auto-complete-props';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport { autoCompleteStyles } from '@/components/forms/auto-complete/auto-complete-styles';\nimport RuiAutoCompleteOptionList from '@/components/forms/auto-complete/RuiAutoCompleteOptionList.vue';\nimport RuiAutoCompleteSelection from '@/components/forms/auto-complete/RuiAutoCompleteSelection.vue';\nimport RuiFieldLabel from '@/components/forms/field-label/RuiFieldLabel.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport { activatorHandlers } from '@/components/overlays/menu/activator-handlers';\nimport RuiMenu from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { useLabelPlacement } from '@/composables/defaults/field';\nimport { getOptionHeight, useDropdownMenu, useDropdownOptionProperty } from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport {\n splitAriaAttrs,\n useAutoCompleteChips,\n useAutoCompleteCombobox,\n useAutoCompleteFocus,\n useAutoCompleteKeyboardNavigation,\n useAutoCompleteSearch,\n useAutoCompleteSelection,\n useAutoCompleteValue,\n} from '@/composables/forms/auto-complete';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\nimport { cn } from '@/utils/tv';\n\ndefineOptions({\n name: 'RuiAutoComplete',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<AutoCompleteModelValue<TValue>>({ required: true });\n\nconst searchInputModel = defineModel<string>('searchInput', { default: '' });\n\nconst {\n options = [],\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n hideDetails = false,\n chips = false,\n label = 'Select',\n labelPlacement = undefined,\n menuOptions,\n classNames,\n prependIcon,\n hideArrow = false,\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n prependWidth,\n appendWidth,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n noFilter = false,\n hideNoData = false,\n noDataText = 'No data available',\n filter,\n hideSelected = false,\n placeholder = '',\n returnObject = false,\n customValue = false,\n hideCustomValue = false,\n required = false,\n hideSearchInput = false,\n hideSelectionWrapper = false,\n groupBy,\n itemDisabled,\n searchIncludesGroupLabel = false,\n} = defineProps<AutoCompleteProps<TValue, TItem>>();\n\nconst slots = defineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem[];\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'selection.prepend'?: (props: { index: number; item: TItem }) => any;\n 'selection'?: (props: { index: number; item: TItem; chipAttrs: Record<string, unknown> }) => any;\n 'item.prepend'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item.append'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'group-header'?: (props: { group: string; items: TItem[] }) => any;\n 'no-data'?: () => any;\n 'placeholder'?: (props: { disabled: boolean; readOnly: boolean }) => any;\n 'footer'?: () => any;\n}>();\n\nconst { getText, getIdentifier } = useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n});\n\nconst textInput = useTemplateRef<HTMLInputElement>('textInput');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\n// Owned by the option list, so it arrives through a ref callback rather than this template\nconst menuRef = shallowRef<HTMLElement | null>(null);\n\nfunction setMenuRef(element: Element | ComponentPublicInstance | null): void {\n set(menuRef, element instanceof HTMLElement ? element : null);\n}\nconst menuWrapperRef = useTemplateRef<HTMLDivElement>('menuWrapperRef');\n\nconst { focused: activatorFocusedWithin } = useFocusWithin(activator);\nconst { focused: menuWrapperFocusedWithin } = useFocusWithin(menuWrapperRef);\nconst { focused: searchInputFocused } = useFocus(textInput);\n\nconst {\n internalSearch,\n filteredOptions,\n justOpened,\n updateInternalSearch,\n textValueToProperValue,\n} = useAutoCompleteSearch<TItem>(\n () => options,\n searchInputModel,\n {\n keyAttr: () => keyAttr,\n textAttr: () => textAttr,\n noFilter: () => noFilter,\n filter: () => filter,\n customValue: () => customValue,\n hideCustomValue: () => hideCustomValue,\n returnObject: () => returnObject,\n groupBy: () => groupBy,\n searchIncludesGroupLabel: () => searchIncludesGroupLabel,\n },\n);\n\nconst isOpen = ref<boolean>(false);\nconst isHovered = ref<boolean>(false);\n\n// Calculate multiple from modelValue directly to avoid circular dependency\nconst multiple = computed<boolean>(() => Array.isArray(get(modelValue)));\n\nconst shouldApplyValueAsSearch = computed<boolean>(\n () => !(slots.selection || get(multiple) || chips),\n);\n\nconst { value } = useAutoCompleteValue<AutoCompleteModelValue<TValue>, TItem>(\n modelValue,\n () => options,\n {\n keyAttr: () => keyAttr,\n returnObject: () => returnObject,\n customValue: () => customValue,\n },\n {\n getIdentifier,\n getText,\n textValueToProperValue,\n shouldApplyValueAsSearch,\n isOpen,\n multiple,\n updateInternalSearch,\n },\n);\n\nconst resolvedItemHeight = itemHeight ?? getOptionHeight(dense);\n\n/* eslint-disable @typescript-eslint/no-use-before-define -- these call the selection composable below, which needs their helpers; the calls run after setup */\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n menuWidth,\n isActiveItem,\n itemIndexInValue,\n modelHighlightedIndex,\n moveHighlight,\n applyHighlighted,\n optionsWithSelectedHidden,\n modelUserNavigated,\n groupedOptions,\n isGrouped,\n isItemDisabled,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: resolvedItemHeight,\n keyAttr,\n textAttr,\n options: filteredOptions,\n dense: () => dense,\n value,\n menuRef,\n setValue: (item: TItem): void => { setValue(item); },\n autoSelectFirst,\n hideSelected,\n isOpen,\n getText,\n getIdentifier,\n groupBy: () => groupBy,\n itemDisabled: () => itemDisabled,\n prependWidth,\n appendWidth,\n});\n\nconst {\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onInputKeydown,\n onTab,\n setValueFocus,\n} = useAutoCompleteKeyboardNavigation<TItem>(\n {\n chips: () => chips,\n customValue: () => customValue,\n multiple,\n },\n {\n activator,\n applyHighlighted,\n clear: (): void => clear(),\n filteredOptions,\n getText,\n highlightedIndex: modelHighlightedIndex,\n internalSearch,\n isOpen,\n removeValue: (item: TItem): void => { setValue(item); },\n searchInputFocused,\n setSearchAsValue: (): void => setSearchAsValue(),\n updateInternalSearch,\n userNavigated: modelUserNavigated,\n value,\n },\n);\n\nconst { chipAttrs, selectChip } = useAutoCompleteChips<TItem>({\n focusInput: (): void => get(textInput)?.focus(),\n getIdentifier,\n setValue: (item: TItem): void => { setValue(item); },\n setValueFocus,\n});\n\n/** The selected chip's text, read out by the live region so the selection is heard, not only seen. */\nconst selectedChipAnnouncement = computed<string>(() => {\n const item = get(value)[get(focusedValueIndex)];\n return chips && item !== undefined ? `${getText(item) ?? ''}, press Backspace to remove` : '';\n});\n\nconst {\n anyFocused: focusAnyFocused,\n editing,\n inputClass: focusInputClass,\n onInputFocused: focusOnInputFocused,\n onSettledKeydown,\n setInputFocus: focusSetInputFocus,\n settleOnInput,\n unsettle,\n} = useAutoCompleteFocus(\n {\n customValue: () => customValue,\n disabled: () => disabled,\n shouldApplyValueAsSearch,\n },\n {\n activatorFocusedWithin,\n focusedValueIndex,\n internalSearch,\n isOpen,\n justOpened,\n menuWrapperFocusedWithin,\n searchInputFocused,\n setSearchAsValue: (): void => setSearchAsValue(),\n textInput,\n updateInternalSearch,\n },\n);\n\n/* eslint-enable @typescript-eslint/no-use-before-define -- the wiring above ends here */\n\nconst { clear, setSearchAsValue, setValue } = useAutoCompleteSelection<TItem>({\n getText,\n internalSearch,\n isOpen,\n itemIndexInValue,\n multiple,\n resetModel: (): void => set(modelValue, (Array.isArray(get(modelValue)) ? [] : undefined) as AutoCompleteModelValue<TValue>),\n searchInputFocused,\n settleOnInput,\n shouldApplyValueAsSearch,\n textValueToProperValue,\n updateInternalSearch,\n value,\n});\n\nconst menuMinHeight = computed<number>(\n () => Math.min(5, get(optionsWithSelectedHidden).length) * resolvedItemHeight,\n);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst valueSet = computed<boolean>(() => get(value).length > 0);\n\nconst usedPlaceholder = computed<string>(() => {\n if (get(editing))\n return placeholder;\n return '';\n});\n\nconst labelId = useId();\nconst placement = useLabelPlacement(() => labelPlacement);\nconst { activeDescendant, optionIdPrefix } = useAutoCompleteCombobox(isOpen, modelHighlightedIndex);\n\n// An empty, unfocused field shows the placeholder where the collapsed search input sits\nconst restingPlaceholder = computed<boolean>(() =>\n !!placeholder && !get(valueSet) && !get(editing) && !slots.placeholder,\n);\n\nconst ui = computed<ReturnType<typeof autoCompleteStyles>>(() => autoCompleteStyles({\n opened: get(isOpen),\n hovered: get(isHovered),\n dense,\n disabled,\n readonly: readOnly,\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n}));\n\nconst highlightedClass = autoCompleteStyles({}).highlighted();\n\nfunction updateSearchInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n\n const value = target.value;\n unsettle();\n set(isOpen, true);\n updateInternalSearch(value);\n set(justOpened, false);\n}\n\n/**\n * Moves the highlight through the options, which also leaves the picked-value display for a search.\n *\n * @param up - whether to move towards the first option\n */\nfunction onArrow(up: boolean): void {\n unsettle();\n moveHighlight(up);\n}\n\nfunction setSelectionRange(start: number, end: number): void {\n set(searchInputFocused, true);\n get(textInput)?.setSelectionRange?.(start, end);\n}\n\nfunction arrowClicked(event: MouseEvent): void {\n if (get(isOpen)) {\n set(isOpen, false);\n event.stopPropagation();\n }\n}\n\nfunction openMenu(): void {\n set(isOpen, true);\n}\n\nfunction closeMenu(): void {\n set(isOpen, false);\n}\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n\ndefineExpose({\n closeMenu,\n focus: focusSetInputFocus,\n openMenu,\n setSelectionRange,\n});\n</script>\n\n<template>\n <RuiMenu\n v-model=\"isOpen\"\n v-bind=\"{ ...getRootAttrs($attrs, []), ...menuOptions }\"\n :class=\"ui.wrapper({ class: cn($attrs.class) })\"\n :class-names=\"{\n ...menuOptions?.classNames,\n details: ['px-0', cn(menuOptions?.classNames?.details) ?? ''],\n menu: [\n { hidden: optionsWithSelectedHidden.length === 0 && customValue && !slots['no-data'] },\n cn(menuOptions?.classNames?.menu) ?? '',\n ],\n }\"\n :options=\"menuFloatingOptions\"\n :close-on-content-click=\"false\"\n full-width\n persist-on-activator-click\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"!hideDetails\"\n :disabled=\"disabled\"\n disable-auto-focus\n role=\"listbox\"\n >\n <template\n v-if=\"label\"\n #label\n >\n <RuiFieldLabel\n :id=\"labelId\"\n :text=\"label\"\n :hidden=\"placement === 'hidden'\"\n :required=\"required\"\n :disabled=\"disabled\"\n />\n </template>\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <!--\n The visual box: it takes clicks and the keys that bubble from the input and the chips, but\n is no tab stop and carries no role. The input inside is the combobox (ARIA 1.2), so the\n chips sit beside it rather than nested in it.\n -->\n <div\n ref=\"activator\"\n :class=\"ui.activator({ class: cn([hideArrow && 'pr-3', classNames?.label]) })\"\n v-bind=\"{\n ...splitAriaAttrs(getNonRootAttrs($attrs, ['onClick', 'class']), false),\n ...(readOnly ? {} : activatorHandlers(attrs)),\n }\"\n data-id=\"activator\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @click=\"focusSetInputFocus()\"\n @keydown.enter=\"onEnter($event)\"\n @keydown.tab=\"onTab($event)\"\n @keydown.left=\"moveSelectedValueHighlight($event, false)\"\n @keydown.right=\"moveSelectedValueHighlight($event, true)\"\n @keydown.up.prevent=\"onArrow(true)\"\n @keydown.down.prevent=\"onArrow(false)\"\n @keydown.home.prevent=\"modelHighlightedIndex = 0\"\n @keydown.end.prevent=\"modelHighlightedIndex = optionsWithSelectedHidden.length - 1\"\n >\n <span\n v-if=\"prependIcon\"\n :class=\"ui.prepend()\"\n data-id=\"prepend\"\n >\n <RuiIcon\n :name=\"prependIcon\"\n :size=\"dense ? 16 : 18\"\n />\n </span>\n <div\n data-id=\"value\"\n :class=\"ui.value()\"\n >\n <!--\n Placeholder slot occupies the same flex space the input takes\n when focused, so transitioning into the focused state doesn't\n shift the activator's content. pointer-events-none is required\n so clicks pass through to the activator (otherwise the slot\n swallows the first click and the menu needs two taps to open).\n -->\n <span\n v-if=\"restingPlaceholder\"\n data-id=\"resting-placeholder\"\n class=\"truncate text-rui-text-secondary pointer-events-none\"\n >\n {{ placeholder }}\n </span>\n <div\n v-if=\"!valueSet && !editing && slots.placeholder\"\n data-id=\"placeholder\"\n class=\"flex-1 min-w-0 pointer-events-none\"\n >\n <slot\n name=\"placeholder\"\n v-bind=\"{ disabled, readOnly }\"\n />\n </div>\n <RuiAutoCompleteSelection\n :items=\"value\"\n :chips=\"chips\"\n :dense=\"dense\"\n :multiple=\"multiple\"\n :search-input-focused=\"editing\"\n :hide-selection-wrapper=\"hideSelectionWrapper\"\n :get-identifier=\"getIdentifier\"\n :get-text=\"getText\"\n :chip-attrs=\"chipAttrs\"\n :selected-index=\"focusedValueIndex\"\n :select-chip=\"selectChip\"\n >\n <template\n v-if=\"slots['selection.prepend']\"\n #prepend=\"slotProps\"\n >\n <slot\n name=\"selection.prepend\"\n v-bind=\"slotProps\"\n />\n </template>\n <template\n v-if=\"slots.selection\"\n #default=\"slotProps\"\n >\n <slot\n name=\"selection\"\n v-bind=\"slotProps\"\n />\n </template>\n </RuiAutoCompleteSelection>\n <!-- with hideSearchInput the input stays as a collapsed, read-only combobox, so the field keeps a focus target -->\n <input\n ref=\"textInput\"\n :disabled=\"disabled\"\n :readonly=\"readOnly || hideSearchInput\"\n :value=\"internalSearch\"\n class=\"bg-transparent outline-hidden\"\n type=\"text\"\n :placeholder=\"usedPlaceholder\"\n :class=\"hideSearchInput ? 'w-0 h-0 min-w-0 opacity-0' : focusInputClass\"\n role=\"combobox\"\n :tabindex=\"readOnly ? -1 : undefined\"\n :aria-labelledby=\"label ? labelId : undefined\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :aria-controls=\"attrs['aria-controls']\"\n :aria-activedescendant=\"activeDescendant\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n :aria-invalid=\"hasError\"\n aria-autocomplete=\"list\"\n data-id=\"search-input\"\n v-bind=\"splitAriaAttrs(getNonRootAttrs($attrs, ['onClick', 'class']), true)\"\n @keydown=\"onSettledKeydown($event); onInputKeydown($event)\"\n @keydown.delete=\"onInputDeletePressed($event)\"\n @input.stop=\"updateSearchInput($event)\"\n @focus=\"focusOnInputFocused()\"\n />\n <span\n class=\"sr-only\"\n aria-live=\"polite\"\n >\n {{ selectedChipAnnouncement }}\n </span>\n </div>\n\n <RuiButton\n v-if=\"clearable && valueSet && !disabled && !readOnly\"\n variant=\"text\"\n icon\n size=\"sm\"\n tabindex=\"-1\"\n data-id=\"clear\"\n aria-label=\"Clear\"\n :class=\"[\n ui.clear(),\n focusAnyFocused && 'visible!',\n { 'mr-2': !dense },\n ]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </RuiButton>\n\n <span\n v-if=\"!hideArrow\"\n :class=\"ui.iconWrapper()\"\n @click=\"arrowClicked($event)\"\n >\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 20\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"2\"\n variant=\"indeterminate\"\n />\n </div>\n <fieldset :class=\"ui.fieldset()\" />\n </slot>\n </template>\n <template #default=\"{ width }\">\n <div ref=\"menuWrapperRef\">\n <RuiAutoCompleteOptionList\n v-if=\"optionsWithSelectedHidden.length > 0\"\n :container-props=\"containerProps\"\n :wrapper-props=\"wrapperProps\"\n :set-menu-ref=\"setMenuRef\"\n :is-grouped=\"isGrouped\"\n :grouped-options=\"groupedOptions\"\n :rendered-data=\"renderedData\"\n :options=\"optionsWithSelectedHidden\"\n :highlighted-index=\"modelHighlightedIndex\"\n :highlighted-class=\"highlightedClass\"\n :dense=\"dense\"\n :menu-class=\"ui.menu({ class: cn(classNames?.menu) })\"\n :menu-style=\"{ width: `${width}px`, minWidth: menuWidth, minHeight: `${menuMinHeight}px` }\"\n :get-identifier=\"getIdentifier\"\n :get-text=\"getText\"\n :is-active-item=\"isActiveItem\"\n :is-item-disabled=\"isItemDisabled\"\n :option-id-prefix=\"optionIdPrefix\"\n @select=\"setValue($event)\"\n @move-highlight=\"moveHighlight($event)\"\n >\n <template\n v-if=\"slots['group-header']\"\n #group-header=\"slotProps\"\n >\n <slot\n name=\"group-header\"\n v-bind=\"slotProps\"\n />\n </template>\n <template\n v-if=\"slots['item.prepend']\"\n #item.prepend=\"slotProps\"\n >\n <slot\n name=\"item.prepend\"\n v-bind=\"slotProps\"\n />\n </template>\n <template\n v-if=\"slots.item\"\n #item=\"slotProps\"\n >\n <slot\n name=\"item\"\n v-bind=\"slotProps\"\n />\n </template>\n <template\n v-if=\"slots['item.append']\"\n #item.append=\"slotProps\"\n >\n <slot\n name=\"item.append\"\n v-bind=\"slotProps\"\n />\n </template>\n </RuiAutoCompleteOptionList>\n\n <div\n v-else-if=\"!hideNoData\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu\"\n >\n <slot name=\"no-data\">\n <div\n v-if=\"!customValue\"\n class=\"p-4\"\n data-id=\"no-data\"\n >\n {{ noDataText }}\n </div>\n </slot>\n </div>\n <!--\n The scroll container above reserves ~15px on the right for the\n scrollbar gutter. We render an equivalent right-padding here so the\n footer's content aligns with the option rows' content rather than\n the menu's outer edge. `pr-(--rui-scrollbar-gutter,15px)`\n lets consumers override if their theme reserves a different width.\n -->\n <div\n v-if=\"slots.footer\"\n class=\"bg-rui-menu border-t border-rui-divider pr-(--rui-scrollbar-gutter,15px) -mb-2\"\n data-id=\"footer\"\n >\n <slot name=\"footer\" />\n </div>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RuiAutoComplete.js","names":[],"sources":["../../../../src/components/forms/auto-complete/RuiAutoComplete.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { AutoCompleteModelValue, AutoCompleteProps } from '@/components/forms/auto-complete/auto-complete-props';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport { autoCompleteStyles } from '@/components/forms/auto-complete/auto-complete-styles';\nimport RuiAutoCompleteOptionList from '@/components/forms/auto-complete/RuiAutoCompleteOptionList.vue';\nimport RuiAutoCompleteSelection from '@/components/forms/auto-complete/RuiAutoCompleteSelection.vue';\nimport RuiFieldLabel from '@/components/forms/field-label/RuiFieldLabel.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport { activatorHandlers } from '@/components/overlays/menu/activator-handlers';\nimport RuiMenu from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { useLabelPlacement } from '@/composables/defaults/field';\nimport { getOptionHeight, useDropdownMenu, useDropdownOptionProperty } from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport {\n splitAriaAttrs,\n useAutoCompleteChips,\n useAutoCompleteCombobox,\n useAutoCompleteFocus,\n useAutoCompleteKeyboardNavigation,\n useAutoCompleteSearch,\n useAutoCompleteSelection,\n useAutoCompleteValue,\n} from '@/composables/forms/auto-complete';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\nimport { cn } from '@/utils/tv';\n\ndefineOptions({\n name: 'RuiAutoComplete',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<AutoCompleteModelValue<TValue>>({ required: true });\n\nconst searchInputModel = defineModel<string>('searchInput', { default: '' });\n\nconst {\n options = [],\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n hideDetails = false,\n chips = false,\n label = 'Select',\n labelPlacement = undefined,\n menuOptions,\n classNames,\n prependIcon,\n hideArrow = false,\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n prependWidth,\n appendWidth,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n noFilter = false,\n hideNoData = false,\n noDataText = 'No data available',\n filter,\n hideSelected = false,\n placeholder = '',\n returnObject = false,\n customValue = false,\n hideCustomValue = false,\n required = false,\n hideSearchInput = false,\n hideSelectionWrapper = false,\n groupBy,\n itemDisabled,\n searchIncludesGroupLabel = false,\n} = defineProps<AutoCompleteProps<TValue, TItem>>();\n\nconst slots = defineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem[];\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'selection.prepend'?: (props: { index: number; item: TItem }) => any;\n 'selection'?: (props: { index: number; item: TItem; chipAttrs: Record<string, unknown> }) => any;\n 'item.prepend'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item.append'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'group-header'?: (props: { group: string; items: TItem[] }) => any;\n 'no-data'?: () => any;\n 'placeholder'?: (props: { disabled: boolean; readOnly: boolean }) => any;\n 'footer'?: () => any;\n}>();\n\nconst { getText, getIdentifier } = useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n});\n\nconst textInput = useTemplateRef<HTMLInputElement>('textInput');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\n// Owned by the option list, so it arrives through a ref callback rather than this template\nconst menuRef = shallowRef<HTMLElement | null>(null);\n\nfunction setMenuRef(element: Element | ComponentPublicInstance | null): void {\n set(menuRef, element instanceof HTMLElement ? element : null);\n}\nconst menuWrapperRef = useTemplateRef<HTMLDivElement>('menuWrapperRef');\n\nconst { focused: activatorFocusedWithin } = useFocusWithin(activator);\nconst { focused: menuWrapperFocusedWithin } = useFocusWithin(menuWrapperRef);\nconst { focused: searchInputFocused } = useFocus(textInput);\n\nconst {\n internalSearch,\n filteredOptions,\n justOpened,\n updateInternalSearch,\n textValueToProperValue,\n} = useAutoCompleteSearch<TItem>(\n () => options,\n searchInputModel,\n {\n keyAttr: () => keyAttr,\n textAttr: () => textAttr,\n noFilter: () => noFilter,\n filter: () => filter,\n customValue: () => customValue,\n hideCustomValue: () => hideCustomValue,\n returnObject: () => returnObject,\n groupBy: () => groupBy,\n searchIncludesGroupLabel: () => searchIncludesGroupLabel,\n },\n);\n\nconst isOpen = ref<boolean>(false);\nconst isHovered = ref<boolean>(false);\n\n// Calculate multiple from modelValue directly to avoid circular dependency\nconst multiple = computed<boolean>(() => Array.isArray(get(modelValue)));\n\nconst shouldApplyValueAsSearch = computed<boolean>(\n () => !(slots.selection || get(multiple) || chips),\n);\n\nconst { value } = useAutoCompleteValue<AutoCompleteModelValue<TValue>, TItem>(\n modelValue,\n () => options,\n {\n keyAttr: () => keyAttr,\n returnObject: () => returnObject,\n customValue: () => customValue,\n },\n {\n getIdentifier,\n getText,\n textValueToProperValue,\n shouldApplyValueAsSearch,\n isOpen,\n multiple,\n updateInternalSearch,\n },\n);\n\nconst resolvedItemHeight = itemHeight ?? getOptionHeight(dense);\n\n/* eslint-disable @typescript-eslint/no-use-before-define -- these call the selection composable below, which needs their helpers; the calls run after setup */\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n menuWidth,\n isActiveItem,\n itemIndexInValue,\n modelHighlightedIndex,\n moveHighlight,\n applyHighlighted,\n optionsWithSelectedHidden,\n modelUserNavigated,\n groupedOptions,\n isGrouped,\n isItemDisabled,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: resolvedItemHeight,\n keyAttr,\n textAttr,\n options: filteredOptions,\n dense: () => dense,\n value,\n menuRef,\n setValue: (item: TItem): void => { setValue(item); },\n autoSelectFirst,\n hideSelected,\n isOpen,\n getText,\n getIdentifier,\n groupBy: () => groupBy,\n itemDisabled: () => itemDisabled,\n prependWidth,\n appendWidth,\n});\n\nconst {\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onInputKeydown,\n onTab,\n setValueFocus,\n} = useAutoCompleteKeyboardNavigation<TItem>(\n {\n chips: () => chips,\n customValue: () => customValue,\n multiple,\n },\n {\n activator,\n applyHighlighted,\n clear: (): void => clear(),\n filteredOptions,\n getText,\n highlightedIndex: modelHighlightedIndex,\n internalSearch,\n isOpen,\n removeValue: (item: TItem): void => { setValue(item); },\n searchInputFocused,\n setSearchAsValue: (): void => setSearchAsValue(),\n updateInternalSearch,\n userNavigated: modelUserNavigated,\n value,\n },\n);\n\nconst { chipAttrs, selectChip } = useAutoCompleteChips<TItem>({\n focusInput: (): void => get(textInput)?.focus(),\n getIdentifier,\n setValue: (item: TItem): void => { setValue(item); },\n setValueFocus,\n});\n\n/** The selected chip's text, read out by the live region so the selection is heard, not only seen. */\nconst selectedChipAnnouncement = computed<string>(() => {\n const item = get(value)[get(focusedValueIndex)];\n return chips && item !== undefined ? `${getText(item) ?? ''}, press Backspace to remove` : '';\n});\n\nconst {\n anyFocused: focusAnyFocused,\n editing,\n inputClass: focusInputClass,\n onInputFocused: focusOnInputFocused,\n onSettledKeydown,\n setInputFocus: focusSetInputFocus,\n settleOnInput,\n unsettle,\n} = useAutoCompleteFocus(\n {\n customValue: () => customValue,\n disabled: () => disabled,\n shouldApplyValueAsSearch,\n },\n {\n activatorFocusedWithin,\n focusedValueIndex,\n internalSearch,\n isOpen,\n justOpened,\n menuWrapperFocusedWithin,\n searchInputFocused,\n setSearchAsValue: (): void => setSearchAsValue(),\n textInput,\n updateInternalSearch,\n },\n);\n\n/* eslint-enable @typescript-eslint/no-use-before-define -- the wiring above ends here */\n\nconst { clear, setSearchAsValue, setValue } = useAutoCompleteSelection<TItem>({\n getText,\n internalSearch,\n isOpen,\n itemIndexInValue,\n multiple,\n resetModel: (): void => set(modelValue, (Array.isArray(get(modelValue)) ? [] : undefined) as AutoCompleteModelValue<TValue>),\n searchInputFocused,\n settleOnInput,\n shouldApplyValueAsSearch,\n textValueToProperValue,\n updateInternalSearch,\n value,\n});\n\nconst menuMinHeight = computed<number>(\n () => Math.min(5, get(optionsWithSelectedHidden).length) * resolvedItemHeight,\n);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst valueSet = computed<boolean>(() => get(value).length > 0);\n\nconst usedPlaceholder = computed<string>(() => {\n if (get(editing))\n return placeholder;\n return '';\n});\n\nconst labelId = useId();\nconst placement = useLabelPlacement(() => labelPlacement);\nconst { activeDescendant, optionIdPrefix } = useAutoCompleteCombobox(isOpen, modelHighlightedIndex);\n\n// An empty, unfocused field shows the placeholder where the collapsed search input sits\nconst restingPlaceholder = computed<boolean>(() =>\n !!placeholder && !get(valueSet) && !get(editing) && !slots.placeholder,\n);\n\nconst ui = computed<ReturnType<typeof autoCompleteStyles>>(() => autoCompleteStyles({\n opened: get(isOpen),\n hovered: get(isHovered),\n dense,\n disabled,\n readonly: readOnly,\n multiple: get(multiple),\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n}));\n\nconst highlightedClass = autoCompleteStyles({}).highlighted();\n\nfunction updateSearchInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n\n const value = target.value;\n unsettle();\n set(isOpen, true);\n updateInternalSearch(value);\n set(justOpened, false);\n}\n\n/**\n * Moves the highlight through the options, which also leaves the picked-value display for a search.\n *\n * @param up - whether to move towards the first option\n */\nfunction onArrow(up: boolean): void {\n unsettle();\n moveHighlight(up);\n}\n\nfunction setSelectionRange(start: number, end: number): void {\n set(searchInputFocused, true);\n get(textInput)?.setSelectionRange?.(start, end);\n}\n\nfunction arrowClicked(event: MouseEvent): void {\n if (get(isOpen)) {\n set(isOpen, false);\n event.stopPropagation();\n }\n}\n\nfunction openMenu(): void {\n set(isOpen, true);\n}\n\nfunction closeMenu(): void {\n set(isOpen, false);\n}\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n\ndefineExpose({\n closeMenu,\n focus: focusSetInputFocus,\n openMenu,\n setSelectionRange,\n});\n</script>\n\n<template>\n <RuiMenu\n v-model=\"isOpen\"\n v-bind=\"{ ...getRootAttrs($attrs, []), ...menuOptions }\"\n :class=\"ui.wrapper({ class: cn($attrs.class) })\"\n :class-names=\"{\n ...menuOptions?.classNames,\n details: ['px-0', cn(menuOptions?.classNames?.details) ?? ''],\n menu: [\n { hidden: optionsWithSelectedHidden.length === 0 && customValue && !slots['no-data'] },\n cn(menuOptions?.classNames?.menu) ?? '',\n ],\n }\"\n :options=\"menuFloatingOptions\"\n :close-on-content-click=\"false\"\n full-width\n persist-on-activator-click\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"!hideDetails\"\n :disabled=\"disabled\"\n disable-auto-focus\n role=\"listbox\"\n >\n <template\n v-if=\"label\"\n #label\n >\n <RuiFieldLabel\n :id=\"labelId\"\n :text=\"label\"\n :hidden=\"placement === 'hidden'\"\n :required=\"required\"\n :disabled=\"disabled\"\n />\n </template>\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <!--\n The visual box: it takes clicks and the keys that bubble from the input and the chips, but\n is no tab stop and carries no role. The input inside is the combobox (ARIA 1.2), so the\n chips sit beside it rather than nested in it.\n -->\n <div\n ref=\"activator\"\n :class=\"ui.activator({ class: cn([hideArrow && 'pr-3', classNames?.label]) })\"\n v-bind=\"{\n ...splitAriaAttrs(getNonRootAttrs($attrs, ['onClick', 'class']), false),\n ...(readOnly ? {} : activatorHandlers(attrs)),\n }\"\n data-id=\"activator\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @click=\"focusSetInputFocus()\"\n @keydown.enter=\"onEnter($event)\"\n @keydown.tab=\"onTab($event)\"\n @keydown.left=\"moveSelectedValueHighlight($event, false)\"\n @keydown.right=\"moveSelectedValueHighlight($event, true)\"\n @keydown.up.prevent=\"onArrow(true)\"\n @keydown.down.prevent=\"onArrow(false)\"\n @keydown.home.prevent=\"modelHighlightedIndex = 0\"\n @keydown.end.prevent=\"modelHighlightedIndex = optionsWithSelectedHidden.length - 1\"\n >\n <span\n v-if=\"prependIcon\"\n :class=\"ui.prepend()\"\n data-id=\"prepend\"\n >\n <RuiIcon\n :name=\"prependIcon\"\n :size=\"dense ? 16 : 18\"\n />\n </span>\n <div\n data-id=\"value\"\n :class=\"ui.value()\"\n >\n <!--\n Placeholder slot occupies the same flex space the input takes\n when focused, so transitioning into the focused state doesn't\n shift the activator's content. pointer-events-none is required\n so clicks pass through to the activator (otherwise the slot\n swallows the first click and the menu needs two taps to open).\n -->\n <span\n v-if=\"restingPlaceholder\"\n data-id=\"resting-placeholder\"\n class=\"truncate text-rui-text-secondary pointer-events-none\"\n >\n {{ placeholder }}\n </span>\n <div\n v-if=\"!valueSet && !editing && slots.placeholder\"\n data-id=\"placeholder\"\n class=\"flex-1 min-w-0 pointer-events-none\"\n >\n <slot\n name=\"placeholder\"\n v-bind=\"{ disabled, readOnly }\"\n />\n </div>\n <RuiAutoCompleteSelection\n :items=\"value\"\n :chips=\"chips\"\n :dense=\"dense\"\n :multiple=\"multiple\"\n :search-input-focused=\"editing\"\n :hide-selection-wrapper=\"hideSelectionWrapper\"\n :get-identifier=\"getIdentifier\"\n :get-text=\"getText\"\n :chip-attrs=\"chipAttrs\"\n :selected-index=\"focusedValueIndex\"\n :select-chip=\"selectChip\"\n >\n <template\n v-if=\"slots['selection.prepend']\"\n #prepend=\"slotProps\"\n >\n <slot\n name=\"selection.prepend\"\n v-bind=\"slotProps\"\n />\n </template>\n <template\n v-if=\"slots.selection\"\n #default=\"slotProps\"\n >\n <slot\n name=\"selection\"\n v-bind=\"slotProps\"\n />\n </template>\n </RuiAutoCompleteSelection>\n <!-- with hideSearchInput the input stays as a collapsed, read-only combobox, so the field keeps a focus target -->\n <input\n ref=\"textInput\"\n :disabled=\"disabled\"\n :readonly=\"readOnly || hideSearchInput\"\n :value=\"internalSearch\"\n class=\"bg-transparent outline-hidden\"\n type=\"text\"\n :placeholder=\"usedPlaceholder\"\n :class=\"hideSearchInput ? 'w-0 h-0 min-w-0 opacity-0' : focusInputClass\"\n role=\"combobox\"\n :tabindex=\"readOnly ? -1 : undefined\"\n :aria-labelledby=\"label ? labelId : undefined\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :aria-controls=\"attrs['aria-controls']\"\n :aria-activedescendant=\"activeDescendant\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n :aria-invalid=\"hasError\"\n aria-autocomplete=\"list\"\n data-id=\"search-input\"\n v-bind=\"splitAriaAttrs(getNonRootAttrs($attrs, ['onClick', 'class']), true)\"\n @keydown=\"onSettledKeydown($event); onInputKeydown($event)\"\n @keydown.delete=\"onInputDeletePressed($event)\"\n @input.stop=\"updateSearchInput($event)\"\n @focus=\"focusOnInputFocused()\"\n />\n <span\n class=\"sr-only\"\n aria-live=\"polite\"\n >\n {{ selectedChipAnnouncement }}\n </span>\n </div>\n\n <RuiButton\n v-if=\"clearable && valueSet && !disabled && !readOnly\"\n variant=\"text\"\n icon\n size=\"sm\"\n tabindex=\"-1\"\n data-id=\"clear\"\n aria-label=\"Clear\"\n :class=\"[\n ui.clear(),\n focusAnyFocused && 'visible!',\n { 'mr-2': !dense },\n ]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </RuiButton>\n\n <span\n v-if=\"!hideArrow\"\n :class=\"ui.iconWrapper()\"\n @click=\"arrowClicked($event)\"\n >\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 20\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"2\"\n variant=\"indeterminate\"\n />\n </div>\n <fieldset :class=\"ui.fieldset()\" />\n </slot>\n </template>\n <template #default=\"{ width }\">\n <div ref=\"menuWrapperRef\">\n <RuiAutoCompleteOptionList\n v-if=\"optionsWithSelectedHidden.length > 0\"\n :container-props=\"containerProps\"\n :wrapper-props=\"wrapperProps\"\n :set-menu-ref=\"setMenuRef\"\n :is-grouped=\"isGrouped\"\n :grouped-options=\"groupedOptions\"\n :rendered-data=\"renderedData\"\n :options=\"optionsWithSelectedHidden\"\n :highlighted-index=\"modelHighlightedIndex\"\n :highlighted-class=\"highlightedClass\"\n :dense=\"dense\"\n :menu-class=\"ui.menu({ class: cn(classNames?.menu) })\"\n :menu-style=\"{ width: `${width}px`, minWidth: menuWidth, minHeight: `${menuMinHeight}px` }\"\n :get-identifier=\"getIdentifier\"\n :get-text=\"getText\"\n :is-active-item=\"isActiveItem\"\n :is-item-disabled=\"isItemDisabled\"\n :option-id-prefix=\"optionIdPrefix\"\n @select=\"setValue($event)\"\n @move-highlight=\"moveHighlight($event)\"\n >\n <template\n v-if=\"slots['group-header']\"\n #group-header=\"slotProps\"\n >\n <slot\n name=\"group-header\"\n v-bind=\"slotProps\"\n />\n </template>\n <template\n v-if=\"slots['item.prepend']\"\n #item.prepend=\"slotProps\"\n >\n <slot\n name=\"item.prepend\"\n v-bind=\"slotProps\"\n />\n </template>\n <template\n v-if=\"slots.item\"\n #item=\"slotProps\"\n >\n <slot\n name=\"item\"\n v-bind=\"slotProps\"\n />\n </template>\n <template\n v-if=\"slots['item.append']\"\n #item.append=\"slotProps\"\n >\n <slot\n name=\"item.append\"\n v-bind=\"slotProps\"\n />\n </template>\n </RuiAutoCompleteOptionList>\n\n <div\n v-else-if=\"!hideNoData\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu\"\n >\n <slot name=\"no-data\">\n <div\n v-if=\"!customValue\"\n class=\"p-4\"\n data-id=\"no-data\"\n >\n {{ noDataText }}\n </div>\n </slot>\n </div>\n <!--\n The scroll container above reserves ~15px on the right for the\n scrollbar gutter. We render an equivalent right-padding here so the\n footer's content aligns with the option rows' content rather than\n the menu's outer edge. `pr-(--rui-scrollbar-gutter,15px)`\n lets consumers override if their theme reserves a different width.\n -->\n <div\n v-if=\"slots.footer\"\n class=\"bg-rui-menu border-t border-rui-divider pr-(--rui-scrollbar-gutter,15px) -mb-2\"\n data-id=\"footer\"\n >\n <slot name=\"footer\" />\n </div>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":""}
@@ -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$1(menuRef, element instanceof HTMLElement ? element : null);
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$1(modelValue)));
205
- const shouldApplyValueAsSearch = computed(() => !(slots.selection || get$1(multiple) || __props.chips));
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$1(textInput)?.focus(),
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$1(value)[get$1(focusedValueIndex)];
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$1(modelValue, Array.isArray(get$1(modelValue)) ? [] : void 0),
298
+ resetModel: () => set(modelValue, Array.isArray(get(modelValue)) ? [] : void 0),
299
299
  searchInputFocused,
300
300
  settleOnInput,
301
301
  shouldApplyValueAsSearch,
@@ -303,25 +303,26 @@ 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$1(optionsWithSelectedHidden).length) * resolvedItemHeight);
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$1(value).length > 0);
308
+ const valueSet = computed(() => get(value).length > 0);
309
309
  const usedPlaceholder = computed(() => {
310
- if (get$1(editing)) return __props.placeholder;
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$1(valueSet) && !get$1(editing) && !slots.placeholder);
316
+ const restingPlaceholder = computed(() => !!__props.placeholder && !get(valueSet) && !get(editing) && !slots.placeholder);
317
317
  const ui = computed(() => autoCompleteStyles({
318
- opened: get$1(isOpen),
319
- hovered: get$1(isHovered),
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$1(hasError),
324
- hasSuccess: get$1(hasSuccess) && !get$1(hasError)
323
+ multiple: get(multiple),
324
+ hasError: get(hasError),
325
+ hasSuccess: get(hasSuccess) && !get(hasError)
325
326
  }));
326
327
  const highlightedClass = autoCompleteStyles({}).highlighted();
327
328
  function updateSearchInput(event) {
@@ -329,9 +330,9 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
329
330
  if (!(target instanceof HTMLInputElement)) return;
330
331
  const value = target.value;
331
332
  unsettle();
332
- set$1(isOpen, true);
333
+ set(isOpen, true);
333
334
  updateInternalSearch(value);
334
- set$1(justOpened, false);
335
+ set(justOpened, false);
335
336
  }
336
337
  /**
337
338
  * Moves the highlight through the options, which also leaves the picked-value display for a search.
@@ -343,20 +344,20 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
343
344
  moveHighlight(up);
344
345
  }
345
346
  function setSelectionRange(start, end) {
346
- set$1(searchInputFocused, true);
347
- get$1(textInput)?.setSelectionRange?.(start, end);
347
+ set(searchInputFocused, true);
348
+ get(textInput)?.setSelectionRange?.(start, end);
348
349
  }
349
350
  function arrowClicked(event) {
350
- if (get$1(isOpen)) {
351
- set$1(isOpen, false);
351
+ if (get(isOpen)) {
352
+ set(isOpen, false);
352
353
  event.stopPropagation();
353
354
  }
354
355
  }
355
356
  function openMenu() {
356
- set$1(isOpen, true);
357
+ set(isOpen, true);
357
358
  }
358
359
  function closeMenu() {
359
- set$1(isOpen, false);
360
+ set(isOpen, false);
360
361
  }
361
362
  const menuFloatingOptions = computed(() => ({
362
363
  placement: Placement.bottomStart,