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

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 (420) hide show
  1. package/README.md +36 -15
  2. package/dist/components/accordions/accordion/RuiAccordion.js.map +1 -1
  3. package/dist/components/accordions/accordion/RuiAccordion.vue.d.ts +0 -4
  4. package/dist/components/accordions/accordion/RuiAccordion.vue_vue_type_script_setup_true_lang.js +6 -7
  5. package/dist/components/accordions/accordion/RuiAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  6. package/dist/components/accordions/accordions/RuiAccordions.js.map +1 -1
  7. package/dist/components/accordions/accordions/RuiAccordions.vue_vue_type_script_setup_true_lang.js +15 -2
  8. package/dist/components/accordions/accordions/RuiAccordions.vue_vue_type_script_setup_true_lang.js.map +1 -1
  9. package/dist/components/alerts/RuiAlert.js.map +1 -1
  10. package/dist/components/alerts/RuiAlert.vue_vue_type_script_setup_true_lang.js +43 -212
  11. package/dist/components/alerts/RuiAlert.vue_vue_type_script_setup_true_lang.js.map +1 -1
  12. package/dist/components/avatars/RuiAvatar.js.map +1 -1
  13. package/dist/components/avatars/RuiAvatar.vue_vue_type_script_setup_true_lang.js +21 -31
  14. package/dist/components/avatars/RuiAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  15. package/dist/components/avatars/RuiAvatarGroup.js.map +1 -1
  16. package/dist/components/avatars/RuiAvatarGroup.vue.d.ts +6 -0
  17. package/dist/components/avatars/RuiAvatarGroup.vue_vue_type_script_setup_true_lang.js +16 -12
  18. package/dist/components/avatars/RuiAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  19. package/dist/components/avatars/avatar-props.d.ts +16 -3
  20. package/dist/components/avatars/avatar-props.js +42 -5
  21. package/dist/components/avatars/avatar-props.js.map +1 -1
  22. package/dist/components/buttons/button/RuiButton.js.map +1 -1
  23. package/dist/components/buttons/button/RuiButton.vue.d.ts +0 -1
  24. package/dist/components/buttons/button/RuiButton.vue_vue_type_script_setup_true_lang.js +29 -16
  25. package/dist/components/buttons/button/RuiButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  26. package/dist/components/buttons/button/button-props.d.ts +0 -2
  27. package/dist/components/buttons/button/button-props.js +5 -5
  28. package/dist/components/buttons/button/button-props.js.map +1 -1
  29. package/dist/components/buttons/button/button-styles.d.ts +4 -1
  30. package/dist/components/buttons/button/button-styles.js +127 -54
  31. package/dist/components/buttons/button/button-styles.js.map +1 -1
  32. package/dist/components/buttons/button-group/RuiButtonGroup.js.map +1 -1
  33. package/dist/components/buttons/button-group/RuiButtonGroup.vue.d.ts +8 -2
  34. package/dist/components/buttons/button-group/RuiButtonGroup.vue_vue_type_script_setup_true_lang.js +65 -96
  35. package/dist/components/buttons/button-group/RuiButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  36. package/dist/components/buttons/button-group/button-group-context.d.ts +28 -0
  37. package/dist/components/buttons/button-group/button-group-context.js +22 -0
  38. package/dist/components/buttons/button-group/button-group-context.js.map +1 -0
  39. package/dist/components/calendar/RuiCalendar.js.map +1 -1
  40. package/dist/components/calendar/RuiCalendar.vue_vue_type_script_setup_true_lang.js +5 -2
  41. package/dist/components/calendar/RuiCalendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  42. package/dist/components/calendar/RuiCalendarGrid.js.map +1 -1
  43. package/dist/components/calendar/RuiCalendarGrid.vue_vue_type_script_setup_true_lang.js +11 -7
  44. package/dist/components/calendar/RuiCalendarGrid.vue_vue_type_script_setup_true_lang.js.map +1 -1
  45. package/dist/components/calendar/RuiCalendarHeader.js.map +1 -1
  46. package/dist/components/calendar/RuiCalendarHeader.vue_vue_type_script_setup_true_lang.js +20 -21
  47. package/dist/components/calendar/RuiCalendarHeader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  48. package/dist/components/calendar/RuiCalendarMenu.js.map +1 -1
  49. package/dist/components/calendar/RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js +16 -20
  50. package/dist/components/calendar/RuiCalendarMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  51. package/dist/components/cards/RuiCard.js.map +1 -1
  52. package/dist/components/cards/RuiCard.vue.d.ts +0 -3
  53. package/dist/components/cards/RuiCard.vue_vue_type_script_setup_true_lang.js +31 -19
  54. package/dist/components/cards/RuiCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  55. package/dist/components/cards/RuiCardHeader.js.map +1 -1
  56. package/dist/components/cards/RuiCardHeader.vue_vue_type_script_setup_true_lang.js +13 -25
  57. package/dist/components/cards/RuiCardHeader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  58. package/dist/components/chips/RuiChip.js.map +1 -1
  59. package/dist/components/chips/RuiChip.vue.d.ts +5 -2
  60. package/dist/components/chips/RuiChip.vue_vue_type_script_setup_true_lang.js +127 -78
  61. package/dist/components/chips/RuiChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  62. package/dist/components/chips/chip-props.d.ts +2 -0
  63. package/dist/components/chips/chip-props.js +5 -3
  64. package/dist/components/chips/chip-props.js.map +1 -1
  65. package/dist/components/color-picker/RuiColorBoard.js.map +1 -1
  66. package/dist/components/color-picker/RuiColorBoard.vue_vue_type_script_setup_true_lang.js +4 -4
  67. package/dist/components/color-picker/RuiColorBoard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  68. package/dist/components/color-picker/RuiColorHue.js.map +1 -1
  69. package/dist/components/color-picker/RuiColorHue.vue_vue_type_script_setup_true_lang.js +1 -1
  70. package/dist/components/color-picker/RuiColorHue.vue_vue_type_script_setup_true_lang.js.map +1 -1
  71. package/dist/components/color-picker/RuiColorInput.js.map +1 -1
  72. package/dist/components/color-picker/RuiColorInput.vue_vue_type_script_setup_true_lang.js +38 -27
  73. package/dist/components/color-picker/RuiColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  74. package/dist/components/color-picker/RuiColorPicker.js.map +1 -1
  75. package/dist/components/color-picker/RuiColorPicker.vue_vue_type_script_setup_true_lang.js +1 -1
  76. package/dist/components/color-picker/RuiColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  77. package/dist/components/date-time-picker/RuiDateTimePicker.js.map +1 -1
  78. package/dist/components/date-time-picker/RuiDateTimePicker.vue.d.ts +3 -2
  79. package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js +49 -47
  80. package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  81. package/dist/components/date-time-picker/RuiDateTimePickerMenu.js.map +1 -1
  82. package/dist/components/date-time-picker/RuiDateTimePickerMenu.vue_vue_type_script_setup_true_lang.js +2 -2
  83. package/dist/components/date-time-picker/RuiDateTimePickerMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  84. package/dist/components/date-time-picker/RuiTimezoneSelect.js.map +1 -1
  85. package/dist/components/date-time-picker/RuiTimezoneSelect.vue.d.ts +4 -3
  86. package/dist/components/date-time-picker/RuiTimezoneSelect.vue_vue_type_script_setup_true_lang.js +3 -3
  87. package/dist/components/date-time-picker/RuiTimezoneSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  88. package/dist/components/date-time-picker/date-time-picker-styles.d.ts +128 -219
  89. package/dist/components/date-time-picker/date-time-picker-styles.js +1 -2
  90. package/dist/components/date-time-picker/date-time-picker-styles.js.map +1 -1
  91. package/dist/components/date-time-picker/dayjs-setup.d.ts +12 -1
  92. package/dist/components/date-time-picker/dayjs-setup.js +19 -4
  93. package/dist/components/date-time-picker/dayjs-setup.js.map +1 -1
  94. package/dist/components/date-time-picker/segment-utils.d.ts +1 -2
  95. package/dist/components/date-time-picker/segment-utils.js +1 -2
  96. package/dist/components/date-time-picker/segment-utils.js.map +1 -1
  97. package/dist/components/date-time-picker/use-date-bounds.d.ts +1 -1
  98. package/dist/components/date-time-picker/use-date-bounds.js +1 -1
  99. package/dist/components/date-time-picker/use-date-bounds.js.map +1 -1
  100. package/dist/components/date-time-picker/use-date-time-selection.d.ts +1 -2
  101. package/dist/components/date-time-picker/use-date-time-selection.js +1 -2
  102. package/dist/components/date-time-picker/use-date-time-selection.js.map +1 -1
  103. package/dist/components/date-time-picker/utils.d.ts +0 -1
  104. package/dist/components/date-time-picker/utils.js +1 -2
  105. package/dist/components/date-time-picker/utils.js.map +1 -1
  106. package/dist/components/forms/auto-complete/RuiAutoComplete.js.map +1 -1
  107. package/dist/components/forms/auto-complete/RuiAutoComplete.vue.d.ts +1 -68
  108. package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js +184 -180
  109. package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js.map +1 -1
  110. package/dist/components/forms/auto-complete/RuiAutoCompleteOption.js.map +1 -1
  111. package/dist/components/forms/auto-complete/RuiAutoCompleteOption.vue_vue_type_script_setup_true_lang.js +1 -0
  112. package/dist/components/forms/auto-complete/RuiAutoCompleteOption.vue_vue_type_script_setup_true_lang.js.map +1 -1
  113. package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.js.map +1 -1
  114. package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.vue.d.ts +2 -0
  115. package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.vue_vue_type_script_setup_true_lang.js +8 -3
  116. package/dist/components/forms/auto-complete/RuiAutoCompleteOptionList.vue_vue_type_script_setup_true_lang.js.map +1 -1
  117. package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.js.map +1 -1
  118. package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.vue.d.ts +4 -0
  119. package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.vue_vue_type_script_setup_true_lang.js +24 -10
  120. package/dist/components/forms/auto-complete/RuiAutoCompleteSelection.vue_vue_type_script_setup_true_lang.js.map +1 -1
  121. package/dist/components/forms/auto-complete/auto-complete-props.d.ts +66 -0
  122. package/dist/components/forms/auto-complete/auto-complete-styles.d.ts +128 -218
  123. package/dist/components/forms/auto-complete/auto-complete-styles.js +4 -2
  124. package/dist/components/forms/auto-complete/auto-complete-styles.js.map +1 -1
  125. package/dist/components/forms/category-picker/RuiCategoryPicker.js.map +1 -1
  126. package/dist/components/forms/category-picker/RuiCategoryPicker.vue.d.ts +3 -6
  127. package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js +106 -105
  128. package/dist/components/forms/category-picker/RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  129. package/dist/components/forms/category-picker/category-picker-styles.d.ts +83 -232
  130. package/dist/components/forms/category-picker/category-picker-styles.js +20 -26
  131. package/dist/components/forms/category-picker/category-picker-styles.js.map +1 -1
  132. package/dist/components/forms/check-control-styles.d.ts +61 -2
  133. package/dist/components/forms/check-control-styles.js +53 -46
  134. package/dist/components/forms/check-control-styles.js.map +1 -1
  135. package/dist/components/forms/checkbox/RuiCheckbox.js.map +1 -1
  136. package/dist/components/forms/checkbox/RuiCheckbox.vue_vue_type_script_setup_true_lang.js +31 -18
  137. package/dist/components/forms/checkbox/RuiCheckbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  138. package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.js.map +1 -1
  139. package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue_vue_type_script_setup_true_lang.js +3 -3
  140. package/dist/components/forms/checkbox/checkbox-group/RuiCheckboxGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  141. package/dist/components/forms/field-defaults/RuiFieldDefaults.js +7 -0
  142. package/dist/components/forms/field-defaults/RuiFieldDefaults.js.map +1 -0
  143. package/dist/components/forms/field-defaults/RuiFieldDefaults.vue.d.ts +16 -0
  144. package/dist/components/forms/field-defaults/RuiFieldDefaults.vue_vue_type_script_setup_true_lang.js +18 -0
  145. package/dist/components/forms/field-defaults/RuiFieldDefaults.vue_vue_type_script_setup_true_lang.js.map +1 -0
  146. package/dist/components/forms/field-label/RuiFieldLabel.js +7 -0
  147. package/dist/components/forms/field-label/RuiFieldLabel.js.map +1 -0
  148. package/dist/components/forms/field-label/RuiFieldLabel.vue.d.ts +17 -0
  149. package/dist/components/forms/field-label/RuiFieldLabel.vue_vue_type_script_setup_true_lang.js +57 -0
  150. package/dist/components/forms/field-label/RuiFieldLabel.vue_vue_type_script_setup_true_lang.js.map +1 -0
  151. package/dist/components/forms/file-upload/RuiFileUpload.js.map +1 -1
  152. package/dist/components/forms/file-upload/RuiFileUpload.vue.d.ts +1 -1
  153. package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js +39 -20
  154. package/dist/components/forms/file-upload/RuiFileUpload.vue_vue_type_script_setup_true_lang.js.map +1 -1
  155. package/dist/components/forms/radio-button/radio/RuiRadio.js.map +1 -1
  156. package/dist/components/forms/radio-button/radio/RuiRadio.vue_vue_type_script_setup_true_lang.js +13 -14
  157. package/dist/components/forms/radio-button/radio/RuiRadio.vue_vue_type_script_setup_true_lang.js.map +1 -1
  158. package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.js.map +1 -1
  159. package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.vue_vue_type_script_setup_true_lang.js +3 -3
  160. package/dist/components/forms/radio-button/radio-group/RuiRadioGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  161. package/dist/components/forms/revealable-text-field/RuiRevealableTextField.js.map +1 -1
  162. package/dist/components/forms/revealable-text-field/RuiRevealableTextField.vue_vue_type_script_setup_true_lang.js +6 -6
  163. package/dist/components/forms/revealable-text-field/RuiRevealableTextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
  164. package/dist/components/forms/select/RuiMenuSelect.js.map +1 -1
  165. package/dist/components/forms/select/RuiMenuSelect.vue.d.ts +3 -12
  166. package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js +50 -45
  167. package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  168. package/dist/components/forms/select/RuiSimpleSelect.js.map +1 -1
  169. package/dist/components/forms/select/RuiSimpleSelect.vue.d.ts +0 -1
  170. package/dist/components/forms/select/RuiSimpleSelect.vue_vue_type_script_setup_true_lang.js +13 -20
  171. package/dist/components/forms/select/RuiSimpleSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  172. package/dist/components/forms/select/menu-select-styles.d.ts +99 -221
  173. package/dist/components/forms/select/menu-select-styles.js +1 -4
  174. package/dist/components/forms/select/menu-select-styles.js.map +1 -1
  175. package/dist/components/forms/slider/RuiSlider.js.map +1 -1
  176. package/dist/components/forms/slider/RuiSlider.vue.d.ts +0 -4
  177. package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js +113 -90
  178. package/dist/components/forms/slider/RuiSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  179. package/dist/components/forms/slider/slider-styles.d.ts +40 -40
  180. package/dist/components/forms/slider/slider-styles.js +67 -109
  181. package/dist/components/forms/slider/slider-styles.js.map +1 -1
  182. package/dist/components/forms/switch/RuiSwitch.js.map +1 -1
  183. package/dist/components/forms/switch/RuiSwitch.vue_vue_type_script_setup_true_lang.js +32 -44
  184. package/dist/components/forms/switch/RuiSwitch.vue_vue_type_script_setup_true_lang.js.map +1 -1
  185. package/dist/components/forms/text-area/RuiTextArea.js.map +1 -1
  186. package/dist/components/forms/text-area/RuiTextArea.vue.d.ts +3 -2
  187. package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js +120 -99
  188. package/dist/components/forms/text-area/RuiTextArea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  189. package/dist/components/forms/text-area/text-area-styles.d.ts +41 -115
  190. package/dist/components/forms/text-area/text-area-styles.js +34 -194
  191. package/dist/components/forms/text-area/text-area-styles.js.map +1 -1
  192. package/dist/components/forms/text-field/RuiTextField.js.map +1 -1
  193. package/dist/components/forms/text-field/RuiTextField.vue.d.ts +3 -2
  194. package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js +93 -90
  195. package/dist/components/forms/text-field/RuiTextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
  196. package/dist/components/forms/text-field/text-field-styles.d.ts +37 -145
  197. package/dist/components/forms/text-field/text-field-styles.js +24 -267
  198. package/dist/components/forms/text-field/text-field-styles.js.map +1 -1
  199. package/dist/components/forms/text-input-styles.d.ts +98 -229
  200. package/dist/components/forms/text-input-styles.js +63 -193
  201. package/dist/components/forms/text-input-styles.js.map +1 -1
  202. package/dist/components/helpers/RuiFormTextDetail.js.map +1 -1
  203. package/dist/components/helpers/RuiFormTextDetail.vue_vue_type_script_setup_true_lang.js +2 -2
  204. package/dist/components/helpers/RuiFormTextDetail.vue_vue_type_script_setup_true_lang.js.map +1 -1
  205. package/dist/components/icons/RuiIcon.js.map +1 -1
  206. package/dist/components/icons/RuiIcon.vue_vue_type_script_setup_true_lang.js +6 -3
  207. package/dist/components/icons/RuiIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  208. package/dist/components/index.d.ts +5 -3
  209. package/dist/components/index.js +4 -3
  210. package/dist/components/loaders/RuiSkeletonBase.js.map +1 -1
  211. package/dist/components/loaders/RuiSkeletonBase.vue_vue_type_script_setup_true_lang.js +6 -5
  212. package/dist/components/loaders/RuiSkeletonBase.vue_vue_type_script_setup_true_lang.js.map +1 -1
  213. package/dist/components/loaders/RuiSkeletonLoader.js.map +1 -1
  214. package/dist/components/loaders/RuiSkeletonLoader.vue_vue_type_script_setup_true_lang.js +8 -8
  215. package/dist/components/loaders/RuiSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  216. package/dist/components/logos/RuiLogo.js.map +1 -1
  217. package/dist/components/logos/RuiLogo.vue.d.ts +8 -10
  218. package/dist/components/logos/RuiLogo.vue_vue_type_script_setup_true_lang.js +42 -43
  219. package/dist/components/logos/RuiLogo.vue_vue_type_script_setup_true_lang.js.map +1 -1
  220. package/dist/components/logos/verified-logo.d.ts +17 -0
  221. package/dist/components/logos/verified-logo.js +36 -0
  222. package/dist/components/logos/verified-logo.js.map +1 -0
  223. package/dist/components/overlays/badge/RuiBadge.js.map +1 -1
  224. package/dist/components/overlays/badge/RuiBadge.vue_vue_type_script_setup_true_lang.js +75 -47
  225. package/dist/components/overlays/badge/RuiBadge.vue_vue_type_script_setup_true_lang.js.map +1 -1
  226. package/dist/components/overlays/dialog/RuiDialog.js.map +1 -1
  227. package/dist/components/overlays/dialog/RuiDialog.vue.d.ts +3 -2
  228. package/dist/components/overlays/dialog/RuiDialog.vue_vue_type_script_setup_true_lang.js +16 -12
  229. package/dist/components/overlays/dialog/RuiDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  230. package/dist/components/overlays/menu/RuiMenu.js.map +1 -1
  231. package/dist/components/overlays/menu/RuiMenu.vue.d.ts +15 -10
  232. package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js +29 -22
  233. package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  234. package/dist/components/overlays/menu/activator-handlers.d.ts +11 -0
  235. package/dist/components/overlays/menu/activator-handlers.js +18 -0
  236. package/dist/components/overlays/menu/activator-handlers.js.map +1 -0
  237. package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.js.map +1 -1
  238. package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue.d.ts +6 -2
  239. package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue_vue_type_script_setup_true_lang.js +40 -16
  240. package/dist/components/overlays/navigation-drawer/RuiNavigationDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  241. package/dist/components/overlays/notification/RuiNotification.js.map +1 -1
  242. package/dist/components/overlays/notification/RuiNotification.vue_vue_type_script_setup_true_lang.js +4 -4
  243. package/dist/components/overlays/notification/RuiNotification.vue_vue_type_script_setup_true_lang.js.map +1 -1
  244. package/dist/components/overlays/tooltip/RuiTooltip.js.map +1 -1
  245. package/dist/components/overlays/tooltip/RuiTooltip.vue.d.ts +0 -5
  246. package/dist/components/overlays/tooltip/RuiTooltip.vue_vue_type_script_setup_true_lang.js +16 -10
  247. package/dist/components/overlays/tooltip/RuiTooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  248. package/dist/components/overlays/tooltip/tooltip-styles.d.ts +5 -12
  249. package/dist/components/overlays/tooltip/tooltip-styles.js +11 -18
  250. package/dist/components/overlays/tooltip/tooltip-styles.js.map +1 -1
  251. package/dist/components/progress/RuiProgress.js.map +1 -1
  252. package/dist/components/progress/RuiProgress.vue.d.ts +4 -0
  253. package/dist/components/progress/RuiProgress.vue_vue_type_script_setup_true_lang.js +72 -46
  254. package/dist/components/progress/RuiProgress.vue_vue_type_script_setup_true_lang.js.map +1 -1
  255. package/dist/components/steppers/RuiFooterStepper.js.map +1 -1
  256. package/dist/components/steppers/RuiFooterStepper.vue_vue_type_script_setup_true_lang.js +27 -28
  257. package/dist/components/steppers/RuiFooterStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
  258. package/dist/components/steppers/RuiStepper.js.map +1 -1
  259. package/dist/components/steppers/RuiStepper.vue.d.ts +0 -4
  260. package/dist/components/steppers/RuiStepper.vue_vue_type_script_setup_true_lang.js +25 -23
  261. package/dist/components/steppers/RuiStepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
  262. package/dist/components/steppers/RuiStepperIcon.js.map +1 -1
  263. package/dist/components/steppers/RuiStepperIcon.vue.d.ts +2 -0
  264. package/dist/components/steppers/RuiStepperIcon.vue_vue_type_script_setup_true_lang.js +47 -47
  265. package/dist/components/steppers/RuiStepperIcon.vue_vue_type_script_setup_true_lang.js.map +1 -1
  266. package/dist/components/tables/RuiDataTable.js.map +1 -1
  267. package/dist/components/tables/RuiDataTable.vue.d.ts +3 -6
  268. package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js +65 -47
  269. package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  270. package/dist/components/tables/RuiExpandButton.js.map +1 -1
  271. package/dist/components/tables/RuiExpandButton.vue_vue_type_script_setup_true_lang.js +1 -0
  272. package/dist/components/tables/RuiExpandButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  273. package/dist/components/tables/RuiTable.js.map +1 -1
  274. package/dist/components/tables/RuiTable.vue_vue_type_script_setup_true_lang.js +8 -5
  275. package/dist/components/tables/RuiTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  276. package/dist/components/tables/RuiTableEmptyState.js.map +1 -1
  277. package/dist/components/tables/RuiTableEmptyState.vue_vue_type_script_setup_true_lang.js +1 -1
  278. package/dist/components/tables/RuiTableEmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
  279. package/dist/components/tables/RuiTableHead.js.map +1 -1
  280. package/dist/components/tables/RuiTableHead.vue_vue_type_script_setup_true_lang.js +88 -66
  281. package/dist/components/tables/RuiTableHead.vue_vue_type_script_setup_true_lang.js.map +1 -1
  282. package/dist/components/tables/RuiTablePagination.js.map +1 -1
  283. package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js +8 -11
  284. package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  285. package/dist/components/tables/data-table/RuiDataTableExpandedRow.js.map +1 -1
  286. package/dist/components/tables/data-table/RuiDataTableExpandedRow.vue_vue_type_script_setup_true_lang.js +3 -3
  287. package/dist/components/tables/data-table/RuiDataTableExpandedRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
  288. package/dist/components/tables/data-table/RuiDataTableGroupRow.js.map +1 -1
  289. package/dist/components/tables/data-table/RuiDataTableGroupRow.vue_vue_type_script_setup_true_lang.js +7 -3
  290. package/dist/components/tables/data-table/RuiDataTableGroupRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
  291. package/dist/components/tables/data-table/RuiDataTableMobileSort.js.map +1 -1
  292. package/dist/components/tables/data-table/RuiDataTableMobileSort.vue_vue_type_script_setup_true_lang.js +1 -1
  293. package/dist/components/tables/data-table/RuiDataTableMobileSort.vue_vue_type_script_setup_true_lang.js.map +1 -1
  294. package/dist/components/tables/data-table/RuiDataTableRow.js.map +1 -1
  295. package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js +58 -19
  296. package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
  297. package/dist/components/tables/data-table-styles.d.ts +24 -0
  298. package/dist/components/tables/data-table-styles.js +35 -20
  299. package/dist/components/tables/data-table-styles.js.map +1 -1
  300. package/dist/components/tables/table_no_data_placeholder.js +1 -1
  301. package/dist/components/tables/table_no_data_placeholder.js.map +1 -1
  302. package/dist/components/tables/table_no_data_placeholder_dark.js +1 -1
  303. package/dist/components/tables/table_no_data_placeholder_dark.js.map +1 -1
  304. package/dist/components/tabs/tab/RuiTab.js.map +1 -1
  305. package/dist/components/tabs/tab/RuiTab.vue.d.ts +2 -5
  306. package/dist/components/tabs/tab/RuiTab.vue_vue_type_script_setup_true_lang.js +94 -68
  307. package/dist/components/tabs/tab/RuiTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  308. package/dist/components/tabs/tab-item/RuiTabItem.js.map +1 -1
  309. package/dist/components/tabs/tab-item/RuiTabItem.vue.d.ts +0 -1
  310. package/dist/components/tabs/tab-item/RuiTabItem.vue_vue_type_script_setup_true_lang.js +5 -11
  311. package/dist/components/tabs/tab-item/RuiTabItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  312. package/dist/components/tabs/tab-items/RuiTabItems.js.map +1 -1
  313. package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js +3 -8
  314. package/dist/components/tabs/tab-items/RuiTabItems.vue_vue_type_script_setup_true_lang.js.map +1 -1
  315. package/dist/components/tabs/tab-props.d.ts +7 -1
  316. package/dist/components/tabs/tab-props.js +7 -1
  317. package/dist/components/tabs/tab-props.js.map +1 -1
  318. package/dist/components/tabs/tabs/RuiTabs.js.map +1 -1
  319. package/dist/components/tabs/tabs/RuiTabs.vue.d.ts +2 -1
  320. package/dist/components/tabs/tabs/RuiTabs.vue_vue_type_script_setup_true_lang.js +178 -32
  321. package/dist/components/tabs/tabs/RuiTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  322. package/dist/components/tabs/tabs/use-tab-indicator.d.ts +27 -0
  323. package/dist/components/tabs/tabs/use-tab-indicator.js +67 -0
  324. package/dist/components/tabs/tabs/use-tab-indicator.js.map +1 -0
  325. package/dist/components/tabs/tabs/use-tab-keyboard.d.ts +17 -0
  326. package/dist/components/tabs/tabs/use-tab-keyboard.js +57 -0
  327. package/dist/components/tabs/tabs/use-tab-keyboard.js.map +1 -0
  328. package/dist/components/tabs/tabs/use-tab-routing.js +1 -2
  329. package/dist/components/tabs/tabs/use-tab-routing.js.map +1 -1
  330. package/dist/components/time-picker/RuiTimePicker.js.map +1 -1
  331. package/dist/components/time-picker/RuiTimePicker.vue_vue_type_script_setup_true_lang.js +35 -32
  332. package/dist/components/time-picker/RuiTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  333. package/dist/components/time-picker/time-picker-styles.d.ts +18 -0
  334. package/dist/components/time-picker/time-picker-styles.js +16 -10
  335. package/dist/components/time-picker/time-picker-styles.js.map +1 -1
  336. package/dist/composables/defaults/field.d.ts +22 -0
  337. package/dist/composables/defaults/field.js +29 -0
  338. package/dist/composables/defaults/field.js.map +1 -0
  339. package/dist/composables/defaults/logo.d.ts +18 -0
  340. package/dist/composables/defaults/logo.js +13 -0
  341. package/dist/composables/defaults/logo.js.map +1 -0
  342. package/dist/composables/dropdown-menu.d.ts +14 -2
  343. package/dist/composables/dropdown-menu.js +12 -2
  344. package/dist/composables/dropdown-menu.js.map +1 -1
  345. package/dist/composables/floating.d.ts +0 -2
  346. package/dist/composables/floating.js +5 -2
  347. package/dist/composables/floating.js.map +1 -1
  348. package/dist/composables/forms/auto-complete/chips.d.ts +22 -0
  349. package/dist/composables/forms/auto-complete/chips.js +42 -0
  350. package/dist/composables/forms/auto-complete/chips.js.map +1 -0
  351. package/dist/composables/forms/auto-complete/combobox.d.ts +25 -0
  352. package/dist/composables/forms/auto-complete/combobox.js +35 -0
  353. package/dist/composables/forms/auto-complete/combobox.js.map +1 -0
  354. package/dist/composables/forms/auto-complete/focus.d.ts +5 -2
  355. package/dist/composables/forms/auto-complete/focus.js +36 -10
  356. package/dist/composables/forms/auto-complete/focus.js.map +1 -1
  357. package/dist/composables/forms/auto-complete/index.d.ts +3 -0
  358. package/dist/composables/forms/auto-complete/keyboard-navigation.d.ts +8 -1
  359. package/dist/composables/forms/auto-complete/keyboard-navigation.js +48 -20
  360. package/dist/composables/forms/auto-complete/keyboard-navigation.js.map +1 -1
  361. package/dist/composables/forms/auto-complete/selection.d.ts +37 -0
  362. package/dist/composables/forms/auto-complete/selection.js +60 -0
  363. package/dist/composables/forms/auto-complete/selection.js.map +1 -0
  364. package/dist/composables/forms/auto-complete/value.js +19 -0
  365. package/dist/composables/forms/auto-complete/value.js.map +1 -1
  366. package/dist/composables/keyboard-scroll.d.ts +17 -0
  367. package/dist/composables/keyboard-scroll.js +58 -0
  368. package/dist/composables/keyboard-scroll.js.map +1 -0
  369. package/dist/composables/sticky-header.d.ts +8 -0
  370. package/dist/composables/sticky-header.js +13 -2
  371. package/dist/composables/sticky-header.js.map +1 -1
  372. package/dist/composables/tables/data-table/page-scroll.d.ts +30 -0
  373. package/dist/composables/tables/data-table/page-scroll.js +113 -0
  374. package/dist/composables/tables/data-table/page-scroll.js.map +1 -0
  375. package/dist/consts/colors.d.ts +4 -0
  376. package/dist/consts/colors.js +1 -37
  377. package/dist/consts/colors.js.map +1 -1
  378. package/dist/consts/tokens.d.ts +56 -0
  379. package/dist/consts/tokens.js +46 -0
  380. package/dist/consts/tokens.js.map +1 -0
  381. package/dist/consts/typography.d.ts +51 -18
  382. package/dist/index.d.ts +13 -0
  383. package/dist/index.js +12 -7
  384. package/dist/index.js.map +1 -1
  385. package/dist/library-icons.json +0 -10
  386. package/dist/style.css +5644 -4677
  387. package/dist/styles/colors.css +315 -0
  388. package/dist/styles/tokens.css +50 -0
  389. package/dist/tailwind.css +145 -0
  390. package/dist/theme/theme.css +403 -0
  391. package/dist/theme.css +403 -0
  392. package/dist/utils/helpers.d.ts +0 -8
  393. package/dist/utils/helpers.js +1 -17
  394. package/dist/utils/helpers.js.map +1 -1
  395. package/dist/utils/tv.js +13 -21
  396. package/dist/utils/tv.js.map +1 -1
  397. package/dist/web-types.json +271 -413
  398. package/package.json +17 -16
  399. package/dist/components/icons/LazyIcon.vue.d.ts +0 -8
  400. package/dist/components/logos/use-logo-sources.d.ts +0 -29
  401. package/dist/components/logos/use-logo-sources.js +0 -90
  402. package/dist/components/logos/use-logo-sources.js.map +0 -1
  403. package/dist/components/steppers/RuiStepperCustomIcon.js +0 -7
  404. package/dist/components/steppers/RuiStepperCustomIcon.js.map +0 -1
  405. package/dist/components/steppers/RuiStepperCustomIcon.vue.d.ts +0 -8
  406. package/dist/components/steppers/RuiStepperCustomIcon.vue_vue_type_script_setup_true_lang.js +0 -43
  407. package/dist/components/steppers/RuiStepperCustomIcon.vue_vue_type_script_setup_true_lang.js.map +0 -1
  408. package/dist/composables/colors/common.d.ts +0 -20
  409. package/dist/composables/colors/index.d.ts +0 -3
  410. package/dist/composables/colors/text-input.d.ts +0 -27
  411. package/dist/composables/forms/auto-complete/index.js +0 -5
  412. package/dist/composables/forms/use-label-with-quote.d.ts +0 -2
  413. package/dist/composables/popper.d.ts +0 -16
  414. package/dist/composables/popper.js +0 -23
  415. package/dist/composables/popper.js.map +0 -1
  416. package/dist/consts/forms/auto-complete/index.d.ts +0 -4
  417. package/dist/theme/index.d.ts +0 -5
  418. package/dist/theme/index.js +0 -162
  419. package/dist/theme/index.js.map +0 -1
  420. package/dist/utils/forms/auto-complete/index.d.ts +0 -2
@@ -1,9 +1,15 @@
1
- import { computed, nextTick, toValue, watch } from "vue";
1
+ import { computed, nextTick, shallowRef, toValue, watch } from "vue";
2
2
  import { watchDebounced } from "@vueuse/core";
3
3
  import { get as get$1, set as set$1 } from "@vueuse/shared";
4
4
  //#region src/composables/forms/auto-complete/focus.ts
5
5
  function useAutoCompleteFocus(options, deps) {
6
+ /**
7
+ * The input holds focus after a pick, as the combobox, but shows the picked value rather than an
8
+ * empty search until the user types, clicks or opens the list again.
9
+ */
10
+ const settled = shallowRef(false);
6
11
  const anyFocused = computed(() => get$1(deps.activatorFocusedWithin) || get$1(deps.menuWrapperFocusedWithin));
12
+ const editing = computed(() => get$1(deps.searchInputFocused) && !get$1(settled));
7
13
  const inputClass = computed(() => {
8
14
  const isFocused = get$1(anyFocused);
9
15
  const isDisabled = toValue(options.disabled);
@@ -13,19 +19,34 @@ function useAutoCompleteFocus(options, deps) {
13
19
  if (hasSearch) return "flex-1 min-w-[4rem]";
14
20
  return "flex-1 min-w-0";
15
21
  });
22
+ function unsettle() {
23
+ set$1(settled, false);
24
+ }
25
+ /** Puts focus on the input, as the combobox, while it keeps showing the value just picked. */
26
+ function settleOnInput() {
27
+ set$1(settled, true);
28
+ set$1(deps.searchInputFocused, true);
29
+ }
30
+ /**
31
+ * The first character typed after a pick replaces the shown value, as it would after tabbing in,
32
+ * rather than adding to it.
33
+ *
34
+ * @param event - the key press on the input
35
+ */
36
+ function onSettledKeydown(event) {
37
+ if (!get$1(settled) || event.key.length !== 1 || event.ctrlKey || event.metaKey || event.altKey) return;
38
+ get$1(deps.textInput)?.select();
39
+ unsettle();
40
+ }
16
41
  async function setInputFocus() {
42
+ unsettle();
17
43
  await nextTick(() => {
18
44
  set$1(deps.searchInputFocused, true);
19
45
  });
20
46
  }
21
- async function onActivatorFocused() {
22
- await nextTick(() => {
23
- if (!get$1(deps.activatorFocused)) set$1(deps.searchInputFocused, true);
24
- });
25
- }
26
47
  function onInputFocused() {
27
48
  set$1(deps.focusedValueIndex, -1);
28
- if (toValue(options.shouldApplyValueAsSearch)) get$1(deps.textInput)?.select();
49
+ if (toValue(options.shouldApplyValueAsSearch) && !get$1(settled)) get$1(deps.textInput)?.select();
29
50
  if (!get$1(deps.isOpen)) set$1(deps.justOpened, true);
30
51
  }
31
52
  /**
@@ -35,7 +56,9 @@ function useAutoCompleteFocus(options, deps) {
35
56
  */
36
57
  let pendingCustomSearch = "";
37
58
  watch(anyFocused, (focused) => {
38
- if (!focused && toValue(options.customValue)) pendingCustomSearch = get$1(deps.internalSearch);
59
+ if (focused) return;
60
+ unsettle();
61
+ if (toValue(options.customValue)) pendingCustomSearch = get$1(deps.internalSearch);
39
62
  });
40
63
  watchDebounced(anyFocused, (focused) => {
41
64
  if (!focused) {
@@ -55,10 +78,13 @@ function useAutoCompleteFocus(options, deps) {
55
78
  });
56
79
  return {
57
80
  anyFocused,
81
+ editing,
58
82
  inputClass,
59
- onActivatorFocused,
60
83
  onInputFocused,
61
- setInputFocus
84
+ onSettledKeydown,
85
+ setInputFocus,
86
+ settleOnInput,
87
+ unsettle
62
88
  };
63
89
  }
64
90
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"focus.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/focus.ts"],"sourcesContent":["import type { ComputedRef, MaybeRefOrGetter, Ref, TemplateRef } from 'vue';\n\nexport interface UseAutoCompleteFocusOptions {\n /** Whether custom (free-text) values are allowed. */\n customValue: MaybeRefOrGetter<boolean>;\n /** Whether to apply the current value as the search text when focused. */\n shouldApplyValueAsSearch: MaybeRefOrGetter<boolean>;\n /** Whether the autocomplete is disabled. */\n disabled: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteFocusDeps {\n activatorFocused: Ref<boolean>;\n activatorFocusedWithin: Ref<boolean>;\n focusedValueIndex: Ref<number>;\n internalSearch: Ref<string>;\n isOpen: Ref<boolean>;\n justOpened: Ref<boolean>;\n menuWrapperFocusedWithin: Ref<boolean>;\n searchInputFocused: Ref<boolean>;\n textInput: Ref<HTMLInputElement | undefined> | TemplateRef<HTMLInputElement>;\n setSearchAsValue: () => void;\n updateInternalSearch: (value?: string) => void;\n}\n\nexport interface UseAutoCompleteFocusReturn {\n anyFocused: ComputedRef<boolean>;\n inputClass: ComputedRef<string>;\n onActivatorFocused: () => Promise<void>;\n onInputFocused: () => void;\n setInputFocus: () => Promise<void>;\n}\n\nexport function useAutoCompleteFocus(\n options: UseAutoCompleteFocusOptions,\n deps: UseAutoCompleteFocusDeps,\n): UseAutoCompleteFocusReturn {\n const anyFocused = computed<boolean>(\n () => get(deps.activatorFocusedWithin) || get(deps.menuWrapperFocusedWithin),\n );\n\n const inputClass = computed<string>(() => {\n const isFocused = get(anyFocused);\n const isDisabled = toValue(options.disabled);\n const shouldApply = toValue(options.shouldApplyValueAsSearch);\n const hasSearch = get(deps.internalSearch);\n\n if ((!isFocused || isDisabled) && !shouldApply)\n return 'w-0 h-0';\n if (hasSearch)\n return 'flex-1 min-w-[4rem]';\n return 'flex-1 min-w-0';\n });\n\n async function setInputFocus(): Promise<void> {\n await nextTick(() => {\n set(deps.searchInputFocused, true);\n });\n }\n\n async function onActivatorFocused(): Promise<void> {\n await nextTick(() => {\n if (!get(deps.activatorFocused)) {\n set(deps.searchInputFocused, true);\n }\n });\n }\n\n function onInputFocused(): void {\n set(deps.focusedValueIndex, -1);\n\n const shouldApply = toValue(options.shouldApplyValueAsSearch);\n if (shouldApply) {\n const textInput = get(deps.textInput);\n textInput?.select();\n }\n\n if (!get(deps.isOpen)) {\n set(deps.justOpened, true);\n }\n }\n\n /**\n * The search text as it stood when focus was lost, kept for the custom-value\n * blur flow: it is captured before the debounced handler runs, because the\n * watchers in value.ts clear the search when the menu closes.\n */\n let pendingCustomSearch = '';\n\n watch(anyFocused, (focused) => {\n if (!focused && toValue(options.customValue)) {\n pendingCustomSearch = get(deps.internalSearch);\n }\n });\n\n // Debounced, so the menu does not close for a moment while focus moves between its own elements\n watchDebounced(\n anyFocused,\n (focused) => {\n if (!focused) {\n set(deps.isOpen, false);\n\n const customValue = toValue(options.customValue);\n const searchToUse = pendingCustomSearch;\n pendingCustomSearch = '';\n\n if (customValue && searchToUse) {\n deps.updateInternalSearch(searchToUse);\n deps.setSearchAsValue();\n }\n\n const shouldApply = toValue(options.shouldApplyValueAsSearch);\n if (!shouldApply) {\n deps.updateInternalSearch();\n }\n }\n },\n {\n debounce: 100,\n maxWait: 200,\n },\n );\n\n return {\n anyFocused,\n inputClass,\n onActivatorFocused,\n onInputFocused,\n setInputFocus,\n };\n}\n"],"mappings":";;;;AAiCA,SAAgB,qBACd,SACA,MAC4B;CAC5B,MAAM,aAAa,eACX,MAAI,KAAK,sBAAsB,KAAK,MAAI,KAAK,wBAAwB,CAC7E;CAEA,MAAM,aAAa,eAAuB;EACxC,MAAM,YAAY,MAAI,UAAU;EAChC,MAAM,aAAa,QAAQ,QAAQ,QAAQ;EAC3C,MAAM,cAAc,QAAQ,QAAQ,wBAAwB;EAC5D,MAAM,YAAY,MAAI,KAAK,cAAc;EAEzC,KAAK,CAAC,aAAa,eAAe,CAAC,aACjC,OAAO;EACT,IAAI,WACF,OAAO;EACT,OAAO;CACT,CAAC;CAED,eAAe,gBAA+B;EAC5C,MAAM,eAAe;GACnB,MAAI,KAAK,oBAAoB,IAAI;EACnC,CAAC;CACH;CAEA,eAAe,qBAAoC;EACjD,MAAM,eAAe;GACnB,IAAI,CAAC,MAAI,KAAK,gBAAgB,GAC5B,MAAI,KAAK,oBAAoB,IAAI;EAErC,CAAC;CACH;CAEA,SAAS,iBAAuB;EAC9B,MAAI,KAAK,mBAAmB,EAAE;EAG9B,IADoB,QAAQ,QAAQ,wBAChC,GAEF,MADsB,KAAK,SAC3B,CAAA,EAAW,OAAO;EAGpB,IAAI,CAAC,MAAI,KAAK,MAAM,GAClB,MAAI,KAAK,YAAY,IAAI;CAE7B;;;;;;CAOA,IAAI,sBAAsB;CAE1B,MAAM,aAAa,YAAY;EAC7B,IAAI,CAAC,WAAW,QAAQ,QAAQ,WAAW,GACzC,sBAAsB,MAAI,KAAK,cAAc;CAEjD,CAAC;CAGD,eACE,aACC,YAAY;EACX,IAAI,CAAC,SAAS;GACZ,MAAI,KAAK,QAAQ,KAAK;GAEtB,MAAM,cAAc,QAAQ,QAAQ,WAAW;GAC/C,MAAM,cAAc;GACpB,sBAAsB;GAEtB,IAAI,eAAe,aAAa;IAC9B,KAAK,qBAAqB,WAAW;IACrC,KAAK,iBAAiB;GACxB;GAGA,IAAI,CADgB,QAAQ,QAAQ,wBAC/B,GACH,KAAK,qBAAqB;EAE9B;CACF,GACA;EACE,UAAU;EACV,SAAS;CACX,CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"focus.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/focus.ts"],"sourcesContent":["import type { ComputedRef, MaybeRefOrGetter, Ref, TemplateRef } from 'vue';\n\nexport interface UseAutoCompleteFocusOptions {\n /** Whether custom (free-text) values are allowed. */\n customValue: MaybeRefOrGetter<boolean>;\n /** Whether to apply the current value as the search text when focused. */\n shouldApplyValueAsSearch: MaybeRefOrGetter<boolean>;\n /** Whether the autocomplete is disabled. */\n disabled: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteFocusDeps {\n activatorFocusedWithin: Ref<boolean>;\n focusedValueIndex: Ref<number>;\n internalSearch: Ref<string>;\n isOpen: Ref<boolean>;\n justOpened: Ref<boolean>;\n menuWrapperFocusedWithin: Ref<boolean>;\n searchInputFocused: Ref<boolean>;\n textInput: Ref<HTMLInputElement | undefined> | TemplateRef<HTMLInputElement>;\n setSearchAsValue: () => void;\n updateInternalSearch: (value?: string) => void;\n}\n\nexport interface UseAutoCompleteFocusReturn {\n anyFocused: ComputedRef<boolean>;\n /** Whether the user is searching: the input has focus and is not just showing a picked value. */\n editing: ComputedRef<boolean>;\n inputClass: ComputedRef<string>;\n onInputFocused: () => void;\n onSettledKeydown: (event: KeyboardEvent) => void;\n setInputFocus: () => Promise<void>;\n settleOnInput: () => void;\n unsettle: () => void;\n}\n\nexport function useAutoCompleteFocus(\n options: UseAutoCompleteFocusOptions,\n deps: UseAutoCompleteFocusDeps,\n): UseAutoCompleteFocusReturn {\n /**\n * The input holds focus after a pick, as the combobox, but shows the picked value rather than an\n * empty search until the user types, clicks or opens the list again.\n */\n const settled = shallowRef<boolean>(false);\n\n const anyFocused = computed<boolean>(\n () => get(deps.activatorFocusedWithin) || get(deps.menuWrapperFocusedWithin),\n );\n\n const editing = computed<boolean>(() => get(deps.searchInputFocused) && !get(settled));\n\n const inputClass = computed<string>(() => {\n const isFocused = get(anyFocused);\n const isDisabled = toValue(options.disabled);\n const shouldApply = toValue(options.shouldApplyValueAsSearch);\n const hasSearch = get(deps.internalSearch);\n\n if ((!isFocused || isDisabled) && !shouldApply)\n return 'w-0 h-0';\n if (hasSearch)\n return 'flex-1 min-w-[4rem]';\n return 'flex-1 min-w-0';\n });\n\n function unsettle(): void {\n set(settled, false);\n }\n\n /** Puts focus on the input, as the combobox, while it keeps showing the value just picked. */\n function settleOnInput(): void {\n set(settled, true);\n set(deps.searchInputFocused, true);\n }\n\n /**\n * The first character typed after a pick replaces the shown value, as it would after tabbing in,\n * rather than adding to it.\n *\n * @param event - the key press on the input\n */\n function onSettledKeydown(event: KeyboardEvent): void {\n if (!get(settled) || event.key.length !== 1 || event.ctrlKey || event.metaKey || event.altKey)\n return;\n get(deps.textInput)?.select();\n unsettle();\n }\n\n async function setInputFocus(): Promise<void> {\n unsettle();\n await nextTick(() => {\n set(deps.searchInputFocused, true);\n });\n }\n\n function onInputFocused(): void {\n set(deps.focusedValueIndex, -1);\n\n // focus coming back after a pick keeps the value as it is, unselected\n const shouldApply = toValue(options.shouldApplyValueAsSearch);\n if (shouldApply && !get(settled)) {\n const textInput = get(deps.textInput);\n textInput?.select();\n }\n\n if (!get(deps.isOpen)) {\n set(deps.justOpened, true);\n }\n }\n\n /**\n * The search text as it stood when focus was lost, kept for the custom-value\n * blur flow: it is captured before the debounced handler runs, because the\n * watchers in value.ts clear the search when the menu closes.\n */\n let pendingCustomSearch = '';\n\n watch(anyFocused, (focused) => {\n if (focused)\n return;\n unsettle();\n if (toValue(options.customValue))\n pendingCustomSearch = get(deps.internalSearch);\n });\n\n // Debounced, so the menu does not close for a moment while focus moves between its own elements\n watchDebounced(\n anyFocused,\n (focused) => {\n if (!focused) {\n set(deps.isOpen, false);\n\n const customValue = toValue(options.customValue);\n const searchToUse = pendingCustomSearch;\n pendingCustomSearch = '';\n\n if (customValue && searchToUse) {\n deps.updateInternalSearch(searchToUse);\n deps.setSearchAsValue();\n }\n\n const shouldApply = toValue(options.shouldApplyValueAsSearch);\n if (!shouldApply) {\n deps.updateInternalSearch();\n }\n }\n },\n {\n debounce: 100,\n maxWait: 200,\n },\n );\n\n return {\n anyFocused,\n editing,\n inputClass,\n onInputFocused,\n onSettledKeydown,\n setInputFocus,\n settleOnInput,\n unsettle,\n };\n}\n"],"mappings":";;;;AAoCA,SAAgB,qBACd,SACA,MAC4B;;;;;CAK5B,MAAM,UAAU,WAAoB,KAAK;CAEzC,MAAM,aAAa,eACX,MAAI,KAAK,sBAAsB,KAAK,MAAI,KAAK,wBAAwB,CAC7E;CAEA,MAAM,UAAU,eAAwB,MAAI,KAAK,kBAAkB,KAAK,CAAC,MAAI,OAAO,CAAC;CAErF,MAAM,aAAa,eAAuB;EACxC,MAAM,YAAY,MAAI,UAAU;EAChC,MAAM,aAAa,QAAQ,QAAQ,QAAQ;EAC3C,MAAM,cAAc,QAAQ,QAAQ,wBAAwB;EAC5D,MAAM,YAAY,MAAI,KAAK,cAAc;EAEzC,KAAK,CAAC,aAAa,eAAe,CAAC,aACjC,OAAO;EACT,IAAI,WACF,OAAO;EACT,OAAO;CACT,CAAC;CAED,SAAS,WAAiB;EACxB,MAAI,SAAS,KAAK;CACpB;;CAGA,SAAS,gBAAsB;EAC7B,MAAI,SAAS,IAAI;EACjB,MAAI,KAAK,oBAAoB,IAAI;CACnC;;;;;;;CAQA,SAAS,iBAAiB,OAA4B;EACpD,IAAI,CAAC,MAAI,OAAO,KAAK,MAAM,IAAI,WAAW,KAAK,MAAM,WAAW,MAAM,WAAW,MAAM,QACrF;EACF,MAAI,KAAK,SAAS,CAAC,EAAE,OAAO;EAC5B,SAAS;CACX;CAEA,eAAe,gBAA+B;EAC5C,SAAS;EACT,MAAM,eAAe;GACnB,MAAI,KAAK,oBAAoB,IAAI;EACnC,CAAC;CACH;CAEA,SAAS,iBAAuB;EAC9B,MAAI,KAAK,mBAAmB,EAAE;EAI9B,IADoB,QAAQ,QAAQ,wBAChC,KAAe,CAAC,MAAI,OAAO,GAE7B,MADsB,KAAK,SAC3B,CAAA,EAAW,OAAO;EAGpB,IAAI,CAAC,MAAI,KAAK,MAAM,GAClB,MAAI,KAAK,YAAY,IAAI;CAE7B;;;;;;CAOA,IAAI,sBAAsB;CAE1B,MAAM,aAAa,YAAY;EAC7B,IAAI,SACF;EACF,SAAS;EACT,IAAI,QAAQ,QAAQ,WAAW,GAC7B,sBAAsB,MAAI,KAAK,cAAc;CACjD,CAAC;CAGD,eACE,aACC,YAAY;EACX,IAAI,CAAC,SAAS;GACZ,MAAI,KAAK,QAAQ,KAAK;GAEtB,MAAM,cAAc,QAAQ,QAAQ,WAAW;GAC/C,MAAM,cAAc;GACpB,sBAAsB;GAEtB,IAAI,eAAe,aAAa;IAC9B,KAAK,qBAAqB,WAAW;IACrC,KAAK,iBAAiB;GACxB;GAGA,IAAI,CADgB,QAAQ,QAAQ,wBAC/B,GACH,KAAK,qBAAqB;EAE9B;CACF,GACA;EACE,UAAU;EACV,SAAS;CACX,CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -1,4 +1,7 @@
1
+ export * from './chips.js';
2
+ export * from './combobox.js';
1
3
  export * from './focus.js';
2
4
  export * from './keyboard-navigation.js';
3
5
  export * from './search.js';
6
+ export * from './selection.js';
4
7
  export * from './value.js';
@@ -20,14 +20,21 @@ export interface UseAutoCompleteKeyboardNavigationDeps<TItem> {
20
20
  removeValue: (item: TItem) => void;
21
21
  setSearchAsValue: () => void;
22
22
  getText: (item: TItem) => string | undefined;
23
+ updateInternalSearch: (value?: string) => void;
23
24
  activator: Ref<HTMLElement | undefined> | TemplateRef<HTMLElement>;
24
25
  }
25
26
  export interface UseAutoCompleteKeyboardNavigationReturn {
27
+ /**
28
+ * The selected value (a chip) that Backspace or Delete would remove, -1 for none. It is only
29
+ * drawn as selected: focus stays in the input, where no browser shortcut takes the key.
30
+ */
26
31
  focusedValueIndex: Ref<number>;
27
32
  moveSelectedValueHighlight: (event: KeyboardEvent, next: boolean) => void;
28
33
  onEnter: (event: KeyboardEvent) => void;
29
34
  onTab: (event: KeyboardEvent) => void;
30
- onInputDeletePressed: () => void;
35
+ onInputDeletePressed: (event: KeyboardEvent) => void;
36
+ /** Drops the value selection on any key that does not act on it. */
37
+ onInputKeydown: (event: KeyboardEvent) => void;
31
38
  setValueFocus: (index: number) => void;
32
39
  }
33
40
  export declare function useAutoCompleteKeyboardNavigation<TItem>(options: UseAutoCompleteKeyboardNavigationOptions, deps: UseAutoCompleteKeyboardNavigationDeps<TItem>): UseAutoCompleteKeyboardNavigationReturn;
@@ -1,4 +1,4 @@
1
- import { nextTick, ref, toValue, watch } from "vue";
1
+ import { ref, toValue, watch } from "vue";
2
2
  import { get, set } from "@vueuse/shared";
3
3
  //#region src/composables/forms/auto-complete/keyboard-navigation.ts
4
4
  function useAutoCompleteKeyboardNavigation(options, deps) {
@@ -88,36 +88,64 @@ function useAutoCompleteKeyboardNavigation(options, deps) {
88
88
  event.preventDefault();
89
89
  }
90
90
  }
91
- function onInputDeletePressed() {
92
- const internalSearch = get(deps.internalSearch);
91
+ /**
92
+ * Removes the selected value. Alt with it, where custom values are accepted, puts the value's
93
+ * text back in the search instead of dropping it.
94
+ *
95
+ * @param event - the Backspace or Delete press
96
+ * @param item - the value the highlighted chip stands for
97
+ */
98
+ function removeSelectedValue(event, item) {
99
+ event.preventDefault();
100
+ set(focusedValueIndex, -1);
101
+ deps.removeValue(item);
102
+ if (event.altKey && toValue(options.customValue)) deps.updateInternalSearch(deps.getText(item) ?? "");
103
+ }
104
+ /** Backspace or Delete with nothing typed: steps onto the last value, or drops it outright. */
105
+ function deleteFromEmptySearch(value) {
106
+ if (!toValue(options.multiple)) {
107
+ deps.clear();
108
+ return;
109
+ }
110
+ if (toValue(options.chips)) {
111
+ set(focusedValueIndex, value.length - 1);
112
+ return;
113
+ }
114
+ const lastItem = value.at(-1);
115
+ if (lastItem !== void 0) deps.removeValue(lastItem);
116
+ }
117
+ function onInputDeletePressed(event) {
93
118
  const value = get(deps.value);
94
- const total = value.length;
95
- const multiple = toValue(options.multiple);
96
- if (!internalSearch && total > 0) {
97
- if (multiple) {
98
- if (toValue(options.chips)) set(focusedValueIndex, total - 1);
99
- else {
100
- const lastItem = value[total - 1];
101
- if (lastItem !== void 0) deps.removeValue(lastItem);
102
- }
103
- } else deps.clear();
119
+ const selected = value[get(focusedValueIndex)];
120
+ if (selected !== void 0) {
121
+ removeSelectedValue(event, selected);
122
+ return;
104
123
  }
124
+ if (!get(deps.internalSearch) && value.length > 0) deleteFromEmptySearch(value);
125
+ }
126
+ function onInputKeydown(event) {
127
+ if (get(focusedValueIndex) === -1) return;
128
+ if (![
129
+ "Backspace",
130
+ "Delete",
131
+ "ArrowLeft",
132
+ "ArrowRight",
133
+ "Alt",
134
+ "AltGraph",
135
+ "Control",
136
+ "Meta",
137
+ "Shift"
138
+ ].includes(event.key)) set(focusedValueIndex, -1);
105
139
  }
106
140
  watch(deps.value, () => {
107
141
  setValueFocus(-1);
108
142
  });
109
- watch(focusedValueIndex, async (index) => {
110
- const multiple = toValue(options.multiple);
111
- if (index === -1 || !multiple) return;
112
- await nextTick(() => {
113
- (get(deps.activator)?.querySelector(`[data-index="${index}"]`))?.focus();
114
- });
115
- });
116
143
  return {
117
144
  focusedValueIndex,
118
145
  moveSelectedValueHighlight,
119
146
  onEnter,
120
147
  onInputDeletePressed,
148
+ onInputKeydown,
121
149
  onTab,
122
150
  setValueFocus
123
151
  };
@@ -1 +1 @@
1
- {"version":3,"file":"keyboard-navigation.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/keyboard-navigation.ts"],"sourcesContent":["import type { MaybeRefOrGetter, Ref, TemplateRef } from 'vue';\nimport { get, set } from '@vueuse/shared';\n\nexport interface UseAutoCompleteKeyboardNavigationOptions {\n /** Whether multiple items can be selected. */\n multiple: MaybeRefOrGetter<boolean>;\n /** Whether custom (free-text) values are allowed. */\n customValue: MaybeRefOrGetter<boolean>;\n /** Whether selected values are displayed as chips. */\n chips: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteKeyboardNavigationDeps<TItem> {\n internalSearch: Ref<string>;\n value: Ref<TItem[]>;\n filteredOptions: Ref<TItem[]>;\n isOpen: Ref<boolean>;\n highlightedIndex: Ref<number>;\n userNavigated: Ref<boolean>;\n searchInputFocused: Ref<boolean>;\n applyHighlighted: () => void;\n clear: () => void;\n removeValue: (item: TItem) => void;\n setSearchAsValue: () => void;\n getText: (item: TItem) => string | undefined;\n activator: Ref<HTMLElement | undefined> | TemplateRef<HTMLElement>;\n}\n\nexport interface UseAutoCompleteKeyboardNavigationReturn {\n focusedValueIndex: Ref<number>;\n moveSelectedValueHighlight: (event: KeyboardEvent, next: boolean) => void;\n onEnter: (event: KeyboardEvent) => void;\n onTab: (event: KeyboardEvent) => void;\n onInputDeletePressed: () => void;\n setValueFocus: (index: number) => void;\n}\n\nexport function useAutoCompleteKeyboardNavigation<TItem>(\n options: UseAutoCompleteKeyboardNavigationOptions,\n deps: UseAutoCompleteKeyboardNavigationDeps<TItem>,\n): UseAutoCompleteKeyboardNavigationReturn {\n const focusedValueIndex = ref<number>(-1);\n\n function setValueFocus(index: number): void {\n set(focusedValueIndex, index);\n }\n\n function moveSelectedValueHighlight(event: KeyboardEvent, next: boolean): void {\n const multiple = toValue(options.multiple);\n const internalSearchValue = get(deps.internalSearch);\n\n if (!multiple || internalSearchValue.length > 0)\n return;\n\n event.preventDefault();\n const total = get(deps.value).length;\n\n let current = get(focusedValueIndex);\n\n if (current === -1) {\n set(focusedValueIndex, next ? 0 : total - 1);\n return;\n }\n\n const move = next ? 1 : -1;\n current += move;\n\n if (current < 0 || current >= total) {\n set(focusedValueIndex, -1);\n set(deps.searchInputFocused, true);\n }\n else {\n set(focusedValueIndex, current);\n }\n }\n\n function highlightedMatchesSearch(filteredOptions: TItem[], highlightedIndex: number, search: string): boolean {\n if (highlightedIndex < 0 || highlightedIndex >= filteredOptions.length)\n return false;\n\n const option = filteredOptions[highlightedIndex];\n if (typeof option === 'string')\n return option === search;\n\n if (option && typeof option === 'object')\n return (deps.getText(option) ?? '') === search;\n\n return false;\n }\n\n function applyCustomValue(event: KeyboardEvent, multiple: boolean): void {\n deps.setSearchAsValue();\n if (!multiple)\n set(deps.isOpen, false);\n event.preventDefault();\n }\n\n function submitClosestForm(): void {\n const activator = get(deps.activator);\n const form = activator?.closest('form');\n form?.dispatchEvent(new Event('submit'));\n }\n\n function tryApplyHighlight(event: KeyboardEvent): boolean {\n const filteredOptions = get(deps.filteredOptions);\n const highlightedIndex = get(deps.highlightedIndex);\n if (highlightedIndex <= -1 || filteredOptions.length === 0)\n return false;\n\n deps.applyHighlighted();\n event.preventDefault();\n return true;\n }\n\n function tryApplyCustom(event: KeyboardEvent, requireEmptyOptions: boolean): boolean {\n const customValue = toValue(options.customValue);\n const internalSearch = get(deps.internalSearch);\n if (!customValue || !internalSearch)\n return false;\n\n const filteredOptions = get(deps.filteredOptions);\n if (requireEmptyOptions) {\n if (filteredOptions.length > 0)\n return false;\n }\n else {\n const highlightedIndex = get(deps.highlightedIndex);\n if (highlightedMatchesSearch(filteredOptions, highlightedIndex, internalSearch))\n return false;\n }\n\n applyCustomValue(event, toValue(options.multiple));\n return true;\n }\n\n /**\n * Enter with the menu open, which takes the option the user navigated to\n * before anything the component highlighted on its own.\n */\n function handleOpenMenuEnter(event: KeyboardEvent): boolean {\n if (get(deps.userNavigated) && tryApplyHighlight(event))\n return true;\n\n // Fall back to a typed custom value when it differs from the highlight.\n if (tryApplyCustom(event, false))\n return true;\n\n // Use the (auto-)highlighted option if there is one.\n if (tryApplyHighlight(event))\n return true;\n\n // No options matched but custom values are allowed — accept the typed text.\n return tryApplyCustom(event, true);\n }\n\n function onEnter(event: KeyboardEvent): void {\n if (get(deps.isOpen)) {\n if (handleOpenMenuEnter(event))\n return;\n\n // Open with nothing to act on, so Enter is swallowed rather than submitting a surrounding form\n event.preventDefault();\n return;\n }\n\n // Nothing selected, menu closed: open menu\n if (get(deps.value).length === 0) {\n set(deps.isOpen, true);\n return event.preventDefault();\n }\n\n // Menu closed with a value selected: let Enter submit the parent form.\n submitClosestForm();\n }\n\n function onTab(event: KeyboardEvent): void {\n const isOpen = get(deps.isOpen);\n const filteredOptions = get(deps.filteredOptions);\n const highlightedIndex = get(deps.highlightedIndex);\n const multiple = toValue(options.multiple);\n\n if (isOpen && filteredOptions.length > 0 && highlightedIndex > -1 && !multiple) {\n deps.applyHighlighted();\n event.preventDefault();\n }\n }\n\n function onInputDeletePressed(): void {\n const internalSearch = get(deps.internalSearch);\n const value = get(deps.value);\n const total = value.length;\n const multiple = toValue(options.multiple);\n\n if (!internalSearch && total > 0) {\n if (multiple) {\n if (toValue(options.chips)) {\n set(focusedValueIndex, total - 1);\n }\n else {\n const lastItem = value[total - 1];\n if (lastItem !== undefined)\n deps.removeValue(lastItem);\n }\n }\n else {\n deps.clear();\n }\n }\n }\n\n // Watch value changes to reset focused index\n watch(deps.value, () => {\n setValueFocus(-1);\n });\n\n // Watch focused value index to handle chip focus\n watch(focusedValueIndex, async (index) => {\n const multiple = toValue(options.multiple);\n if (index === -1 || !multiple)\n return;\n\n await nextTick(() => {\n const activator = get(deps.activator);\n const element = activator?.querySelector<HTMLElement>(`[data-index=\"${index}\"]`);\n element?.focus();\n });\n });\n\n return {\n // eslint-disable-next-line @rotki/composable-return-readonly -- written by focus.ts onInputFocused\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onTab,\n setValueFocus,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"keyboard-navigation.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/keyboard-navigation.ts"],"sourcesContent":["import type { MaybeRefOrGetter, Ref, TemplateRef } from 'vue';\nimport { get, set } from '@vueuse/shared';\n\nexport interface UseAutoCompleteKeyboardNavigationOptions {\n /** Whether multiple items can be selected. */\n multiple: MaybeRefOrGetter<boolean>;\n /** Whether custom (free-text) values are allowed. */\n customValue: MaybeRefOrGetter<boolean>;\n /** Whether selected values are displayed as chips. */\n chips: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteKeyboardNavigationDeps<TItem> {\n internalSearch: Ref<string>;\n value: Ref<TItem[]>;\n filteredOptions: Ref<TItem[]>;\n isOpen: Ref<boolean>;\n highlightedIndex: Ref<number>;\n userNavigated: Ref<boolean>;\n searchInputFocused: Ref<boolean>;\n applyHighlighted: () => void;\n clear: () => void;\n removeValue: (item: TItem) => void;\n setSearchAsValue: () => void;\n getText: (item: TItem) => string | undefined;\n updateInternalSearch: (value?: string) => void;\n activator: Ref<HTMLElement | undefined> | TemplateRef<HTMLElement>;\n}\n\nexport interface UseAutoCompleteKeyboardNavigationReturn {\n /**\n * The selected value (a chip) that Backspace or Delete would remove, -1 for none. It is only\n * drawn as selected: focus stays in the input, where no browser shortcut takes the key.\n */\n focusedValueIndex: Ref<number>;\n moveSelectedValueHighlight: (event: KeyboardEvent, next: boolean) => void;\n onEnter: (event: KeyboardEvent) => void;\n onTab: (event: KeyboardEvent) => void;\n onInputDeletePressed: (event: KeyboardEvent) => void;\n /** Drops the value selection on any key that does not act on it. */\n onInputKeydown: (event: KeyboardEvent) => void;\n setValueFocus: (index: number) => void;\n}\n\nexport function useAutoCompleteKeyboardNavigation<TItem>(\n options: UseAutoCompleteKeyboardNavigationOptions,\n deps: UseAutoCompleteKeyboardNavigationDeps<TItem>,\n): UseAutoCompleteKeyboardNavigationReturn {\n const focusedValueIndex = ref<number>(-1);\n\n function setValueFocus(index: number): void {\n set(focusedValueIndex, index);\n }\n\n function moveSelectedValueHighlight(event: KeyboardEvent, next: boolean): void {\n const multiple = toValue(options.multiple);\n const internalSearchValue = get(deps.internalSearch);\n\n if (!multiple || internalSearchValue.length > 0)\n return;\n\n event.preventDefault();\n const total = get(deps.value).length;\n\n let current = get(focusedValueIndex);\n\n if (current === -1) {\n set(focusedValueIndex, next ? 0 : total - 1);\n return;\n }\n\n const move = next ? 1 : -1;\n current += move;\n\n if (current < 0 || current >= total) {\n set(focusedValueIndex, -1);\n set(deps.searchInputFocused, true);\n }\n else {\n set(focusedValueIndex, current);\n }\n }\n\n function highlightedMatchesSearch(filteredOptions: TItem[], highlightedIndex: number, search: string): boolean {\n if (highlightedIndex < 0 || highlightedIndex >= filteredOptions.length)\n return false;\n\n const option = filteredOptions[highlightedIndex];\n if (typeof option === 'string')\n return option === search;\n\n if (option && typeof option === 'object')\n return (deps.getText(option) ?? '') === search;\n\n return false;\n }\n\n function applyCustomValue(event: KeyboardEvent, multiple: boolean): void {\n deps.setSearchAsValue();\n if (!multiple)\n set(deps.isOpen, false);\n event.preventDefault();\n }\n\n function submitClosestForm(): void {\n const activator = get(deps.activator);\n const form = activator?.closest('form');\n form?.dispatchEvent(new Event('submit'));\n }\n\n function tryApplyHighlight(event: KeyboardEvent): boolean {\n const filteredOptions = get(deps.filteredOptions);\n const highlightedIndex = get(deps.highlightedIndex);\n if (highlightedIndex <= -1 || filteredOptions.length === 0)\n return false;\n\n deps.applyHighlighted();\n event.preventDefault();\n return true;\n }\n\n function tryApplyCustom(event: KeyboardEvent, requireEmptyOptions: boolean): boolean {\n const customValue = toValue(options.customValue);\n const internalSearch = get(deps.internalSearch);\n if (!customValue || !internalSearch)\n return false;\n\n const filteredOptions = get(deps.filteredOptions);\n if (requireEmptyOptions) {\n if (filteredOptions.length > 0)\n return false;\n }\n else {\n const highlightedIndex = get(deps.highlightedIndex);\n if (highlightedMatchesSearch(filteredOptions, highlightedIndex, internalSearch))\n return false;\n }\n\n applyCustomValue(event, toValue(options.multiple));\n return true;\n }\n\n /**\n * Enter with the menu open, which takes the option the user navigated to\n * before anything the component highlighted on its own.\n */\n function handleOpenMenuEnter(event: KeyboardEvent): boolean {\n if (get(deps.userNavigated) && tryApplyHighlight(event))\n return true;\n\n // Fall back to a typed custom value when it differs from the highlight.\n if (tryApplyCustom(event, false))\n return true;\n\n // Use the (auto-)highlighted option if there is one.\n if (tryApplyHighlight(event))\n return true;\n\n // No options matched but custom values are allowed — accept the typed text.\n return tryApplyCustom(event, true);\n }\n\n function onEnter(event: KeyboardEvent): void {\n if (get(deps.isOpen)) {\n if (handleOpenMenuEnter(event))\n return;\n\n // Open with nothing to act on, so Enter is swallowed rather than submitting a surrounding form\n event.preventDefault();\n return;\n }\n\n // Nothing selected, menu closed: open menu\n if (get(deps.value).length === 0) {\n set(deps.isOpen, true);\n return event.preventDefault();\n }\n\n // Menu closed with a value selected: let Enter submit the parent form.\n submitClosestForm();\n }\n\n function onTab(event: KeyboardEvent): void {\n const isOpen = get(deps.isOpen);\n const filteredOptions = get(deps.filteredOptions);\n const highlightedIndex = get(deps.highlightedIndex);\n const multiple = toValue(options.multiple);\n\n if (isOpen && filteredOptions.length > 0 && highlightedIndex > -1 && !multiple) {\n deps.applyHighlighted();\n event.preventDefault();\n }\n }\n\n /**\n * Removes the selected value. Alt with it, where custom values are accepted, puts the value's\n * text back in the search instead of dropping it.\n *\n * @param event - the Backspace or Delete press\n * @param item - the value the highlighted chip stands for\n */\n function removeSelectedValue(event: KeyboardEvent, item: TItem): void {\n event.preventDefault();\n set(focusedValueIndex, -1);\n deps.removeValue(item);\n if (event.altKey && toValue(options.customValue))\n deps.updateInternalSearch(deps.getText(item) ?? '');\n }\n\n /** Backspace or Delete with nothing typed: steps onto the last value, or drops it outright. */\n function deleteFromEmptySearch(value: TItem[]): void {\n if (!toValue(options.multiple)) {\n deps.clear();\n return;\n }\n if (toValue(options.chips)) {\n set(focusedValueIndex, value.length - 1);\n return;\n }\n const lastItem = value.at(-1);\n if (lastItem !== undefined)\n deps.removeValue(lastItem);\n }\n\n function onInputDeletePressed(event: KeyboardEvent): void {\n const value = get(deps.value);\n const selected = value[get(focusedValueIndex)];\n if (selected !== undefined) {\n removeSelectedValue(event, selected);\n return;\n }\n\n if (!get(deps.internalSearch) && value.length > 0)\n deleteFromEmptySearch(value);\n }\n\n function onInputKeydown(event: KeyboardEvent): void {\n if (get(focusedValueIndex) === -1)\n return;\n // a modifier pressed on its own (the Alt of Alt + Backspace) keeps the selection for the key it modifies\n const keepsSelection = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Alt', 'AltGraph', 'Control', 'Meta', 'Shift'].includes(event.key);\n if (!keepsSelection)\n set(focusedValueIndex, -1);\n }\n\n // Watch value changes to reset focused index\n watch(deps.value, () => {\n setValueFocus(-1);\n });\n\n return {\n // eslint-disable-next-line @rotki/composable-return-readonly -- written by focus.ts onInputFocused\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onInputKeydown,\n onTab,\n setValueFocus,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { ComputedRef, Ref } from 'vue';
2
+ export interface UseAutoCompleteSelectionDeps<TItem> {
3
+ value: Ref<TItem[]>;
4
+ multiple: ComputedRef<boolean>;
5
+ /** Whether a single pick shows its text in the input, rather than a slot or chip drawing it. */
6
+ shouldApplyValueAsSearch: ComputedRef<boolean>;
7
+ isOpen: Ref<boolean>;
8
+ searchInputFocused: Ref<boolean>;
9
+ internalSearch: Ref<string>;
10
+ /** The item's place in the selection, -1 when it is not picked. */
11
+ itemIndexInValue: (item: TItem) => number;
12
+ getText: (item: TItem) => string | undefined;
13
+ textValueToProperValue: (text: string) => TItem;
14
+ updateInternalSearch: (value?: string) => void;
15
+ /** Keeps focus on the input after a pick, showing the value rather than an empty search. */
16
+ settleOnInput: () => void;
17
+ /** Empties the model: an empty list when several values are picked, undefined otherwise. */
18
+ resetModel: () => void;
19
+ }
20
+ export interface UseAutoCompleteSelectionReturn<TItem> {
21
+ /**
22
+ * Picks an item, or drops it when several values are picked and it already is one.
23
+ * `skipRefocused` leaves focus where it is, for a value set from typed text.
24
+ */
25
+ setValue: (item: TItem, skipRefocused?: boolean) => Promise<void>;
26
+ /** Turns the typed search into a value, where custom values are accepted. */
27
+ setSearchAsValue: () => void;
28
+ clear: () => void;
29
+ }
30
+ /**
31
+ * The changes to the auto-complete's selection: picking or dropping an item, taking the typed text
32
+ * as a value, and clearing, with the search and focus that follow each.
33
+ *
34
+ * @param deps - the selection state and the operations it calls
35
+ * @returns the selection operations
36
+ */
37
+ export declare function useAutoCompleteSelection<TItem>(deps: UseAutoCompleteSelectionDeps<TItem>): UseAutoCompleteSelectionReturn<TItem>;
@@ -0,0 +1,60 @@
1
+ import { nextTick } from "vue";
2
+ import { get, set } from "@vueuse/shared";
3
+ //#region src/composables/forms/auto-complete/selection.ts
4
+ /**
5
+ * The changes to the auto-complete's selection: picking or dropping an item, taking the typed text
6
+ * as a value, and clearing, with the search and focus that follow each.
7
+ *
8
+ * @param deps - the selection state and the operations it calls
9
+ * @returns the selection operations
10
+ */
11
+ function useAutoCompleteSelection(deps) {
12
+ function toggleInSelection(item) {
13
+ const newValue = [...get(deps.value)];
14
+ const indexInValue = deps.itemIndexInValue(item);
15
+ if (indexInValue === -1) {
16
+ deps.updateInternalSearch();
17
+ newValue.push(item);
18
+ } else newValue.splice(indexInValue, 1);
19
+ set(deps.value, newValue);
20
+ }
21
+ function replaceSelection(item) {
22
+ if (get(deps.shouldApplyValueAsSearch)) deps.updateInternalSearch(deps.getText(item));
23
+ else deps.updateInternalSearch();
24
+ set(deps.value, [item]);
25
+ }
26
+ async function setValue(item, skipRefocused = false) {
27
+ if (get(deps.multiple)) {
28
+ toggleInSelection(item);
29
+ if (!skipRefocused) set(deps.searchInputFocused, true);
30
+ return;
31
+ }
32
+ replaceSelection(item);
33
+ if (skipRefocused) {
34
+ set(deps.isOpen, false);
35
+ return;
36
+ }
37
+ deps.settleOnInput();
38
+ await nextTick(() => {
39
+ set(deps.isOpen, false);
40
+ });
41
+ }
42
+ function setSearchAsValue() {
43
+ const searchToBeValue = get(deps.internalSearch);
44
+ if (!searchToBeValue) return;
45
+ setValue(deps.textValueToProperValue(searchToBeValue), true);
46
+ }
47
+ function clear() {
48
+ deps.updateInternalSearch();
49
+ deps.resetModel();
50
+ }
51
+ return {
52
+ clear,
53
+ setSearchAsValue,
54
+ setValue
55
+ };
56
+ }
57
+ //#endregion
58
+ export { useAutoCompleteSelection };
59
+
60
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/selection.ts"],"sourcesContent":["import type { ComputedRef, Ref } from 'vue';\n\nexport interface UseAutoCompleteSelectionDeps<TItem> {\n value: Ref<TItem[]>;\n multiple: ComputedRef<boolean>;\n /** Whether a single pick shows its text in the input, rather than a slot or chip drawing it. */\n shouldApplyValueAsSearch: ComputedRef<boolean>;\n isOpen: Ref<boolean>;\n searchInputFocused: Ref<boolean>;\n internalSearch: Ref<string>;\n /** The item's place in the selection, -1 when it is not picked. */\n itemIndexInValue: (item: TItem) => number;\n getText: (item: TItem) => string | undefined;\n textValueToProperValue: (text: string) => TItem;\n updateInternalSearch: (value?: string) => void;\n /** Keeps focus on the input after a pick, showing the value rather than an empty search. */\n settleOnInput: () => void;\n /** Empties the model: an empty list when several values are picked, undefined otherwise. */\n resetModel: () => void;\n}\n\nexport interface UseAutoCompleteSelectionReturn<TItem> {\n /**\n * Picks an item, or drops it when several values are picked and it already is one.\n * `skipRefocused` leaves focus where it is, for a value set from typed text.\n */\n setValue: (item: TItem, skipRefocused?: boolean) => Promise<void>;\n /** Turns the typed search into a value, where custom values are accepted. */\n setSearchAsValue: () => void;\n clear: () => void;\n}\n\n/**\n * The changes to the auto-complete's selection: picking or dropping an item, taking the typed text\n * as a value, and clearing, with the search and focus that follow each.\n *\n * @param deps - the selection state and the operations it calls\n * @returns the selection operations\n */\nexport function useAutoCompleteSelection<TItem>(deps: UseAutoCompleteSelectionDeps<TItem>): UseAutoCompleteSelectionReturn<TItem> {\n function toggleInSelection(item: TItem): void {\n const newValue = [...get(deps.value)];\n const indexInValue = deps.itemIndexInValue(item);\n if (indexInValue === -1) {\n deps.updateInternalSearch();\n newValue.push(item);\n }\n else {\n newValue.splice(indexInValue, 1);\n }\n set(deps.value, newValue);\n }\n\n function replaceSelection(item: TItem): void {\n if (get(deps.shouldApplyValueAsSearch))\n deps.updateInternalSearch(deps.getText(item));\n else deps.updateInternalSearch();\n set(deps.value, [item]);\n }\n\n async function setValue(item: TItem, skipRefocused = false): Promise<void> {\n if (get(deps.multiple)) {\n toggleInSelection(item);\n if (!skipRefocused)\n set(deps.searchInputFocused, true);\n return;\n }\n\n replaceSelection(item);\n if (skipRefocused) {\n set(deps.isOpen, false);\n return;\n }\n // focus stays on the combobox, which keeps showing the value just picked\n deps.settleOnInput();\n await nextTick(() => {\n set(deps.isOpen, false);\n });\n }\n\n function setSearchAsValue(): void {\n const searchToBeValue = get(deps.internalSearch);\n if (!searchToBeValue)\n return;\n\n setValue(deps.textValueToProperValue(searchToBeValue), true);\n }\n\n function clear(): void {\n deps.updateInternalSearch();\n deps.resetModel();\n }\n\n return { clear, setSearchAsValue, setValue };\n}\n"],"mappings":";;;;;;;;;;AAuCA,SAAgB,yBAAgC,MAAkF;CAChI,SAAS,kBAAkB,MAAmB;EAC5C,MAAM,WAAW,CAAC,GAAG,IAAI,KAAK,KAAK,CAAC;EACpC,MAAM,eAAe,KAAK,iBAAiB,IAAI;EAC/C,IAAI,iBAAiB,IAAI;GACvB,KAAK,qBAAqB;GAC1B,SAAS,KAAK,IAAI;EACpB,OAEE,SAAS,OAAO,cAAc,CAAC;EAEjC,IAAI,KAAK,OAAO,QAAQ;CAC1B;CAEA,SAAS,iBAAiB,MAAmB;EAC3C,IAAI,IAAI,KAAK,wBAAwB,GACnC,KAAK,qBAAqB,KAAK,QAAQ,IAAI,CAAC;OACzC,KAAK,qBAAqB;EAC/B,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC;CACxB;CAEA,eAAe,SAAS,MAAa,gBAAgB,OAAsB;EACzE,IAAI,IAAI,KAAK,QAAQ,GAAG;GACtB,kBAAkB,IAAI;GACtB,IAAI,CAAC,eACH,IAAI,KAAK,oBAAoB,IAAI;GACnC;EACF;EAEA,iBAAiB,IAAI;EACrB,IAAI,eAAe;GACjB,IAAI,KAAK,QAAQ,KAAK;GACtB;EACF;EAEA,KAAK,cAAc;EACnB,MAAM,eAAe;GACnB,IAAI,KAAK,QAAQ,KAAK;EACxB,CAAC;CACH;CAEA,SAAS,mBAAyB;EAChC,MAAM,kBAAkB,IAAI,KAAK,cAAc;EAC/C,IAAI,CAAC,iBACH;EAEF,SAAS,KAAK,uBAAuB,eAAe,GAAG,IAAI;CAC7D;CAEA,SAAS,QAAc;EACrB,KAAK,qBAAqB;EAC1B,KAAK,WAAW;CAClB;CAEA,OAAO;EAAE;EAAO;EAAkB;CAAS;AAC7C"}
@@ -1,4 +1,5 @@
1
1
  import { assert } from "../../../utils/assert.js";
2
+ import { isEqual } from "../../../utils/is-equal.js";
2
3
  import { computed, toValue, watch } from "vue";
3
4
  import { get, set } from "@vueuse/shared";
4
5
  //#region src/composables/forms/auto-complete/value.ts
@@ -71,6 +72,24 @@ function useAutoCompleteValue(modelValue, options, opts, deps) {
71
72
  deps.updateInternalSearch(deps.getText(firstItem));
72
73
  }
73
74
  }, { immediate: true });
75
+ /**
76
+ * Reconciles the selection against a new set of options, comparing by
77
+ * reference before by value so an unchanged list costs nothing.
78
+ *
79
+ * Only a selection the previous options could resolve is reconciled: a value
80
+ * that was already unresolvable is one the consumer set before its options
81
+ * arrived, since an async list starts empty, and clearing it here would
82
+ * discard a legitimate value.
83
+ *
84
+ * @param curr - the new options
85
+ * @param old - the options before them
86
+ */
87
+ function onOptionsChanged(curr, old) {
88
+ if (curr === old || toValue(opts.customValue) || isEqual(curr, old)) return;
89
+ if (!toValue(deps.multiple) && resolveIn(old).length === 0) return;
90
+ setSelected(get(value));
91
+ }
92
+ watch(() => toValue(options), onOptionsChanged);
74
93
  return {
75
94
  resolveIn,
76
95
  setSelected,
@@ -1 +1 @@
1
- {"version":3,"file":"value.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/value.ts"],"sourcesContent":["import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue';\nimport { get, set } from '@vueuse/shared';\nimport { assert } from '@/utils/assert';\n\nexport interface UseAutoCompleteValueOptions<TItem> {\n /** The property used as the unique identifier for each item. */\n keyAttr?: MaybeRefOrGetter<keyof TItem | undefined>;\n /** Whether to return the full item object instead of just the key. */\n returnObject?: MaybeRefOrGetter<boolean>;\n /** Whether custom (free-text) values are allowed. */\n customValue?: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteValueReturn<TItem> {\n value: ComputedRef<TItem[]>;\n setSelected: (selected: TItem[]) => void;\n /** Resolves the current model value against an arbitrary options list. */\n resolveIn: (options: TItem[]) => TItem[];\n}\n\nexport interface UseAutoCompleteValueDeps<TItem> {\n getIdentifier: (item: TItem) => any;\n getText: (item: TItem) => string | undefined;\n textValueToProperValue: (val: any, returnObject?: boolean) => TItem;\n shouldApplyValueAsSearch: MaybeRefOrGetter<boolean>;\n isOpen: MaybeRefOrGetter<boolean>;\n multiple: MaybeRefOrGetter<boolean>;\n updateInternalSearch: (value?: string) => void;\n}\n\nexport function useAutoCompleteValue<TValue, TItem>(\n modelValue: Ref<TValue | undefined>,\n options: MaybeRefOrGetter<TItem[]>,\n opts: UseAutoCompleteValueOptions<TItem>,\n deps: UseAutoCompleteValueDeps<TItem>,\n): UseAutoCompleteValueReturn<TItem> {\n function buildOptionsMap(optionsData: TItem[]): Map<any, TItem> {\n const map = new Map<any, TItem>();\n for (const item of optionsData) {\n map.set(deps.getIdentifier(item), item);\n }\n return map;\n }\n\n // Create maps for O(1) lookup performance\n const optionsMap = computed<Map<any, TItem>>(() => buildOptionsMap(toValue(options)));\n\n function convertModelValueToArray(modelValueData: TValue | undefined): any[] {\n if (modelValueData === null || modelValueData === undefined)\n return [];\n return Array.isArray(modelValueData) ? modelValueData : [modelValueData];\n }\n\n function filterValues(\n valueArray: any[],\n optionsMapData: Map<any, TItem>,\n customValue: boolean,\n returnObject: boolean,\n getKey: (val: any) => any,\n ): TItem[] {\n const filtered: TItem[] = [];\n for (const val of valueArray) {\n const inOptions = optionsMapData.get(getKey(val));\n if (inOptions !== undefined)\n filtered.push(inOptions);\n else if (customValue)\n filtered.push(deps.textValueToProperValue(val, returnObject));\n }\n return filtered;\n }\n\n function processReturnObjectValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const multipleValue = Array.isArray(modelValueData);\n const valueArray = convertModelValueToArray(modelValueData);\n const filtered = filterValues(valueArray, optionsMapData, customValue, returnObject, val => deps.getIdentifier(val));\n\n if (multipleValue || filtered.length === 0)\n return filtered;\n\n const val = filtered[0];\n assert(val !== undefined && val !== null);\n return [val];\n }\n\n function processStandardValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const valueArray = convertModelValueToArray(modelValueData);\n return filterValues(valueArray, optionsMapData, customValue, returnObject, val => val);\n }\n\n function onUpdateModelValue(value: TValue | undefined): void {\n set(modelValue, value);\n }\n\n function setSelected(selected: TItem[]): void {\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject);\n const selection = keyAttr && !returnObject ? selected.map(item => item[keyAttr]) : selected;\n\n if (toValue(deps.multiple))\n return onUpdateModelValue(selection as TValue);\n\n if (selection.length === 0)\n return onUpdateModelValue(undefined);\n\n return onUpdateModelValue(selection[0] as TValue);\n }\n\n function resolveAgainst(optionsMapData: Map<any, TItem>): TItem[] {\n const modelValueData = get(modelValue);\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject) ?? false;\n const customValue = toValue(opts.customValue) ?? false;\n\n if (keyAttr && returnObject)\n return processReturnObjectValue(modelValueData, optionsMapData, returnObject, customValue);\n\n return processStandardValue(modelValueData, optionsMapData, returnObject, customValue);\n }\n\n function resolveIn(optionsData: TItem[]): TItem[] {\n return resolveAgainst(buildOptionsMap(optionsData));\n }\n\n const value = computed<TItem[]>({\n get: () => resolveAgainst(get(optionsMap)),\n set: (selected: TItem[]): void => {\n setSelected(selected);\n },\n });\n\n // Watches `value` rather than `modelValue`, so the search text resyncs once late `options` resolve it\n watch([value, () => toValue(deps.isOpen)], () => {\n if (toValue(deps.isOpen) || !toValue(deps.shouldApplyValueAsSearch))\n return;\n\n const currentValue = get(value);\n if (toValue(deps.multiple) || currentValue.length === 0) {\n deps.updateInternalSearch();\n }\n else {\n const firstItem = currentValue[0];\n assert(firstItem !== undefined && firstItem !== null);\n deps.updateInternalSearch(deps.getText(firstItem));\n }\n }, { immediate: true });\n\n return {\n resolveIn,\n setSelected,\n value,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"value.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/value.ts"],"sourcesContent":["import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue';\nimport { get, set } from '@vueuse/shared';\nimport { assert } from '@/utils/assert';\nimport { isEqual } from '@/utils/is-equal';\n\nexport interface UseAutoCompleteValueOptions<TItem> {\n /** The property used as the unique identifier for each item. */\n keyAttr?: MaybeRefOrGetter<keyof TItem | undefined>;\n /** Whether to return the full item object instead of just the key. */\n returnObject?: MaybeRefOrGetter<boolean>;\n /** Whether custom (free-text) values are allowed. */\n customValue?: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteValueReturn<TItem> {\n value: ComputedRef<TItem[]>;\n setSelected: (selected: TItem[]) => void;\n /** Resolves the current model value against an arbitrary options list. */\n resolveIn: (options: TItem[]) => TItem[];\n}\n\nexport interface UseAutoCompleteValueDeps<TItem> {\n getIdentifier: (item: TItem) => any;\n getText: (item: TItem) => string | undefined;\n textValueToProperValue: (val: any, returnObject?: boolean) => TItem;\n shouldApplyValueAsSearch: MaybeRefOrGetter<boolean>;\n isOpen: MaybeRefOrGetter<boolean>;\n multiple: MaybeRefOrGetter<boolean>;\n updateInternalSearch: (value?: string) => void;\n}\n\nexport function useAutoCompleteValue<TValue, TItem>(\n modelValue: Ref<TValue | undefined>,\n options: MaybeRefOrGetter<TItem[]>,\n opts: UseAutoCompleteValueOptions<TItem>,\n deps: UseAutoCompleteValueDeps<TItem>,\n): UseAutoCompleteValueReturn<TItem> {\n function buildOptionsMap(optionsData: TItem[]): Map<any, TItem> {\n const map = new Map<any, TItem>();\n for (const item of optionsData) {\n map.set(deps.getIdentifier(item), item);\n }\n return map;\n }\n\n // Create maps for O(1) lookup performance\n const optionsMap = computed<Map<any, TItem>>(() => buildOptionsMap(toValue(options)));\n\n function convertModelValueToArray(modelValueData: TValue | undefined): any[] {\n if (modelValueData === null || modelValueData === undefined)\n return [];\n return Array.isArray(modelValueData) ? modelValueData : [modelValueData];\n }\n\n function filterValues(\n valueArray: any[],\n optionsMapData: Map<any, TItem>,\n customValue: boolean,\n returnObject: boolean,\n getKey: (val: any) => any,\n ): TItem[] {\n const filtered: TItem[] = [];\n for (const val of valueArray) {\n const inOptions = optionsMapData.get(getKey(val));\n if (inOptions !== undefined)\n filtered.push(inOptions);\n else if (customValue)\n filtered.push(deps.textValueToProperValue(val, returnObject));\n }\n return filtered;\n }\n\n function processReturnObjectValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const multipleValue = Array.isArray(modelValueData);\n const valueArray = convertModelValueToArray(modelValueData);\n const filtered = filterValues(valueArray, optionsMapData, customValue, returnObject, val => deps.getIdentifier(val));\n\n if (multipleValue || filtered.length === 0)\n return filtered;\n\n const val = filtered[0];\n assert(val !== undefined && val !== null);\n return [val];\n }\n\n function processStandardValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const valueArray = convertModelValueToArray(modelValueData);\n return filterValues(valueArray, optionsMapData, customValue, returnObject, val => val);\n }\n\n function onUpdateModelValue(value: TValue | undefined): void {\n set(modelValue, value);\n }\n\n function setSelected(selected: TItem[]): void {\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject);\n const selection = keyAttr && !returnObject ? selected.map(item => item[keyAttr]) : selected;\n\n if (toValue(deps.multiple))\n return onUpdateModelValue(selection as TValue);\n\n if (selection.length === 0)\n return onUpdateModelValue(undefined);\n\n return onUpdateModelValue(selection[0] as TValue);\n }\n\n function resolveAgainst(optionsMapData: Map<any, TItem>): TItem[] {\n const modelValueData = get(modelValue);\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject) ?? false;\n const customValue = toValue(opts.customValue) ?? false;\n\n if (keyAttr && returnObject)\n return processReturnObjectValue(modelValueData, optionsMapData, returnObject, customValue);\n\n return processStandardValue(modelValueData, optionsMapData, returnObject, customValue);\n }\n\n function resolveIn(optionsData: TItem[]): TItem[] {\n return resolveAgainst(buildOptionsMap(optionsData));\n }\n\n const value = computed<TItem[]>({\n get: () => resolveAgainst(get(optionsMap)),\n set: (selected: TItem[]): void => {\n setSelected(selected);\n },\n });\n\n // Watches `value` rather than `modelValue`, so the search text resyncs once late `options` resolve it\n watch([value, () => toValue(deps.isOpen)], () => {\n if (toValue(deps.isOpen) || !toValue(deps.shouldApplyValueAsSearch))\n return;\n\n const currentValue = get(value);\n if (toValue(deps.multiple) || currentValue.length === 0) {\n deps.updateInternalSearch();\n }\n else {\n const firstItem = currentValue[0];\n assert(firstItem !== undefined && firstItem !== null);\n deps.updateInternalSearch(deps.getText(firstItem));\n }\n }, { immediate: true });\n\n /**\n * Reconciles the selection against a new set of options, comparing by\n * reference before by value so an unchanged list costs nothing.\n *\n * Only a selection the previous options could resolve is reconciled: a value\n * that was already unresolvable is one the consumer set before its options\n * arrived, since an async list starts empty, and clearing it here would\n * discard a legitimate value.\n *\n * @param curr - the new options\n * @param old - the options before them\n */\n function onOptionsChanged(curr: TItem[], old: TItem[]): void {\n if (curr === old || toValue(opts.customValue) || isEqual(curr, old))\n return;\n\n if (!toValue(deps.multiple) && resolveIn(old).length === 0)\n return;\n\n setSelected(get(value));\n }\n\n watch(() => toValue(options), onOptionsChanged);\n\n return {\n resolveIn,\n setSelected,\n value,\n };\n}\n"],"mappings":";;;;;AA+BA,SAAgB,qBACd,YACA,SACA,MACA,MACmC;CACnC,SAAS,gBAAgB,aAAuC;EAC9D,MAAM,sBAAM,IAAI,IAAgB;EAChC,KAAK,MAAM,QAAQ,aACjB,IAAI,IAAI,KAAK,cAAc,IAAI,GAAG,IAAI;EAExC,OAAO;CACT;CAGA,MAAM,aAAa,eAAgC,gBAAgB,QAAQ,OAAO,CAAC,CAAC;CAEpF,SAAS,yBAAyB,gBAA2C;EAC3E,IAAI,mBAAmB,QAAQ,mBAAmB,KAAA,GAChD,OAAO,CAAC;EACV,OAAO,MAAM,QAAQ,cAAc,IAAI,iBAAiB,CAAC,cAAc;CACzE;CAEA,SAAS,aACP,YACA,gBACA,aACA,cACA,QACS;EACT,MAAM,WAAoB,CAAC;EAC3B,KAAK,MAAM,OAAO,YAAY;GAC5B,MAAM,YAAY,eAAe,IAAI,OAAO,GAAG,CAAC;GAChD,IAAI,cAAc,KAAA,GAChB,SAAS,KAAK,SAAS;QACpB,IAAI,aACP,SAAS,KAAK,KAAK,uBAAuB,KAAK,YAAY,CAAC;EAChE;EACA,OAAO;CACT;CAEA,SAAS,yBACP,gBACA,gBACA,cACA,aACS;EACT,MAAM,gBAAgB,MAAM,QAAQ,cAAc;EAElD,MAAM,WAAW,aADE,yBAAyB,cACd,GAAY,gBAAgB,aAAa,eAAc,QAAO,KAAK,cAAc,GAAG,CAAC;EAEnH,IAAI,iBAAiB,SAAS,WAAW,GACvC,OAAO;EAET,MAAM,MAAM,SAAS;EACrB,OAAO,QAAQ,KAAA,KAAa,QAAQ,IAAI;EACxC,OAAO,CAAC,GAAG;CACb;CAEA,SAAS,qBACP,gBACA,gBACA,cACA,aACS;EAET,OAAO,aADY,yBAAyB,cACxB,GAAY,gBAAgB,aAAa,eAAc,QAAO,GAAG;CACvF;CAEA,SAAS,mBAAmB,OAAiC;EAC3D,IAAI,YAAY,KAAK;CACvB;CAEA,SAAS,YAAY,UAAyB;EAC5C,MAAM,UAAU,QAAQ,KAAK,OAAO;EACpC,MAAM,eAAe,QAAQ,KAAK,YAAY;EAC9C,MAAM,YAAY,WAAW,CAAC,eAAe,SAAS,KAAI,SAAQ,KAAK,QAAQ,IAAI;EAEnF,IAAI,QAAQ,KAAK,QAAQ,GACvB,OAAO,mBAAmB,SAAmB;EAE/C,IAAI,UAAU,WAAW,GACvB,OAAO,mBAAmB,KAAA,CAAS;EAErC,OAAO,mBAAmB,UAAU,EAAY;CAClD;CAEA,SAAS,eAAe,gBAA0C;EAChE,MAAM,iBAAiB,IAAI,UAAU;EACrC,MAAM,UAAU,QAAQ,KAAK,OAAO;EACpC,MAAM,eAAe,QAAQ,KAAK,YAAY,KAAK;EACnD,MAAM,cAAc,QAAQ,KAAK,WAAW,KAAK;EAEjD,IAAI,WAAW,cACb,OAAO,yBAAyB,gBAAgB,gBAAgB,cAAc,WAAW;EAE3F,OAAO,qBAAqB,gBAAgB,gBAAgB,cAAc,WAAW;CACvF;CAEA,SAAS,UAAU,aAA+B;EAChD,OAAO,eAAe,gBAAgB,WAAW,CAAC;CACpD;CAEA,MAAM,QAAQ,SAAkB;EAC9B,WAAW,eAAe,IAAI,UAAU,CAAC;EACzC,MAAM,aAA4B;GAChC,YAAY,QAAQ;EACtB;CACF,CAAC;CAGD,MAAM,CAAC,aAAa,QAAQ,KAAK,MAAM,CAAC,SAAS;EAC/C,IAAI,QAAQ,KAAK,MAAM,KAAK,CAAC,QAAQ,KAAK,wBAAwB,GAChE;EAEF,MAAM,eAAe,IAAI,KAAK;EAC9B,IAAI,QAAQ,KAAK,QAAQ,KAAK,aAAa,WAAW,GACpD,KAAK,qBAAqB;OAEvB;GACH,MAAM,YAAY,aAAa;GAC/B,OAAO,cAAc,KAAA,KAAa,cAAc,IAAI;GACpD,KAAK,qBAAqB,KAAK,QAAQ,SAAS,CAAC;EACnD;CACF,GAAG,EAAE,WAAW,KAAK,CAAC;;;;;;;;;;;;;CActB,SAAS,iBAAiB,MAAe,KAAoB;EAC3D,IAAI,SAAS,OAAO,QAAQ,KAAK,WAAW,KAAK,QAAQ,MAAM,GAAG,GAChE;EAEF,IAAI,CAAC,QAAQ,KAAK,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,WAAW,GACvD;EAEF,YAAY,IAAI,KAAK,CAAC;CACxB;CAEA,YAAY,QAAQ,OAAO,GAAG,gBAAgB;CAE9C,OAAO;EACL;EACA;EACA;CACF;AACF"}
@@ -0,0 +1,17 @@
1
+ import type { ComputedRef, Ref } from 'vue';
2
+ /**
3
+ * Whether a box scrolls while holding nothing a keyboard user can reach, the case WCAG 2.1.1 and
4
+ * axe's scrollable-region-focusable rule cover: the arrow keys only scroll what has focus.
5
+ *
6
+ * @param element - the scrolling box
7
+ * @returns true when the box overflows and has no focusable content
8
+ */
9
+ export declare function needsOwnFocus(element: HTMLElement): boolean;
10
+ /**
11
+ * Makes a scrolling box a tab stop while, and only while, it overflows with nothing focusable
12
+ * inside, so keyboard users can scroll it without a stray tab stop on every box that fits.
13
+ *
14
+ * @param target - the scrolling box
15
+ * @returns the `tabindex` to bind on the box: 0 when it needs focus, otherwise undefined
16
+ */
17
+ export declare function useKeyboardScroll(target: Readonly<Ref<HTMLElement | null | undefined>>): ComputedRef<0 | undefined>;
@@ -0,0 +1,58 @@
1
+ import { computed, onMounted, shallowRef } from "vue";
2
+ import { useMutationObserver, useResizeObserver } from "@vueuse/core";
3
+ import { get as get$1, set as set$1 } from "@vueuse/shared";
4
+ //#region src/composables/keyboard-scroll.ts
5
+ /** What a keyboard user can tab to inside a scrolling box, and so scroll it by. */
6
+ var FOCUSABLE = [
7
+ "a[href]",
8
+ "button:not([disabled])",
9
+ "input:not([disabled]):not([type=hidden])",
10
+ "select:not([disabled])",
11
+ "textarea:not([disabled])",
12
+ "[tabindex]:not([tabindex=\"-1\"])"
13
+ ].join(",");
14
+ /**
15
+ * Whether a box scrolls while holding nothing a keyboard user can reach, the case WCAG 2.1.1 and
16
+ * axe's scrollable-region-focusable rule cover: the arrow keys only scroll what has focus.
17
+ *
18
+ * @param element - the scrolling box
19
+ * @returns true when the box overflows and has no focusable content
20
+ */
21
+ function needsOwnFocus(element) {
22
+ return (element.scrollWidth > element.clientWidth + 1 || element.scrollHeight > element.clientHeight + 1) && element.querySelector(FOCUSABLE) === null;
23
+ }
24
+ /**
25
+ * Makes a scrolling box a tab stop while, and only while, it overflows with nothing focusable
26
+ * inside, so keyboard users can scroll it without a stray tab stop on every box that fits.
27
+ *
28
+ * @param target - the scrolling box
29
+ * @returns the `tabindex` to bind on the box: 0 when it needs focus, otherwise undefined
30
+ */
31
+ function useKeyboardScroll(target) {
32
+ const focusable = shallowRef(false);
33
+ let scheduled = false;
34
+ function update() {
35
+ scheduled = false;
36
+ const element = get$1(target);
37
+ set$1(focusable, element ? needsOwnFocus(element) : false);
38
+ }
39
+ /** Reading the scroll sizes forces layout, so a burst of resizes and mutations is checked once a frame. */
40
+ function schedule() {
41
+ if (scheduled) return;
42
+ scheduled = true;
43
+ requestAnimationFrame(update);
44
+ }
45
+ onMounted(update);
46
+ useResizeObserver(target, schedule);
47
+ useMutationObserver(target, schedule, {
48
+ childList: true,
49
+ subtree: true,
50
+ attributes: true,
51
+ attributeFilter: ["disabled", "tabindex"]
52
+ });
53
+ return computed(() => get$1(focusable) ? 0 : void 0);
54
+ }
55
+ //#endregion
56
+ export { needsOwnFocus, useKeyboardScroll };
57
+
58
+ //# sourceMappingURL=keyboard-scroll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard-scroll.js","names":[],"sources":["../../src/composables/keyboard-scroll.ts"],"sourcesContent":["import type { ComputedRef, Ref } from 'vue';\nimport { useMutationObserver, useResizeObserver } from '@vueuse/core';\n\n/** What a keyboard user can tab to inside a scrolling box, and so scroll it by. */\nconst FOCUSABLE = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=hidden])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n\n/**\n * Whether a box scrolls while holding nothing a keyboard user can reach, the case WCAG 2.1.1 and\n * axe's scrollable-region-focusable rule cover: the arrow keys only scroll what has focus.\n *\n * @param element - the scrolling box\n * @returns true when the box overflows and has no focusable content\n */\nexport function needsOwnFocus(element: HTMLElement): boolean {\n const overflows = element.scrollWidth > element.clientWidth + 1 || element.scrollHeight > element.clientHeight + 1;\n return overflows && element.querySelector(FOCUSABLE) === null;\n}\n\n/**\n * Makes a scrolling box a tab stop while, and only while, it overflows with nothing focusable\n * inside, so keyboard users can scroll it without a stray tab stop on every box that fits.\n *\n * @param target - the scrolling box\n * @returns the `tabindex` to bind on the box: 0 when it needs focus, otherwise undefined\n */\nexport function useKeyboardScroll(target: Readonly<Ref<HTMLElement | null | undefined>>): ComputedRef<0 | undefined> {\n const focusable = shallowRef<boolean>(false);\n let scheduled = false;\n\n function update(): void {\n scheduled = false;\n const element = get(target);\n set(focusable, element ? needsOwnFocus(element) : false);\n }\n\n /** Reading the scroll sizes forces layout, so a burst of resizes and mutations is checked once a frame. */\n function schedule(): void {\n if (scheduled)\n return;\n scheduled = true;\n requestAnimationFrame(update);\n }\n\n // the first check runs at mount, so the box is reachable from its first render, not a frame later\n onMounted(update);\n useResizeObserver(target, schedule);\n // the box keeps its size while its content grows or swaps, which changes whether it overflows\n useMutationObserver(target, schedule, { childList: true, subtree: true, attributes: true, attributeFilter: ['disabled', 'tabindex'] });\n\n return computed<0 | undefined>(() => (get(focusable) ? 0 : undefined));\n}\n"],"mappings":";;;;;AAIA,IAAM,YAAY;CAChB;CACA;CACA;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,GAAG;;;;;;;;AASV,SAAgB,cAAc,SAA+B;CAE3D,QADkB,QAAQ,cAAc,QAAQ,cAAc,KAAK,QAAQ,eAAe,QAAQ,eAAe,MAC7F,QAAQ,cAAc,SAAS,MAAM;AAC3D;;;;;;;;AASA,SAAgB,kBAAkB,QAAmF;CACnH,MAAM,YAAY,WAAoB,KAAK;CAC3C,IAAI,YAAY;CAEhB,SAAS,SAAe;EACtB,YAAY;EACZ,MAAM,UAAU,MAAI,MAAM;EAC1B,MAAI,WAAW,UAAU,cAAc,OAAO,IAAI,KAAK;CACzD;;CAGA,SAAS,WAAiB;EACxB,IAAI,WACF;EACF,YAAY;EACZ,sBAAsB,MAAM;CAC9B;CAGA,UAAU,MAAM;CAChB,kBAAkB,QAAQ,QAAQ;CAElC,oBAAoB,QAAQ,UAAU;EAAE,WAAW;EAAM,SAAS;EAAM,YAAY;EAAM,iBAAiB,CAAC,YAAY,UAAU;CAAE,CAAC;CAErI,OAAO,eAA+B,MAAI,SAAS,IAAI,IAAI,KAAA,CAAU;AACvE"}
@@ -5,6 +5,14 @@ export interface UseStickyTableHeaderRefs {
5
5
  interface UseStickyTableHeaderReturn {
6
6
  stick: Readonly<Ref<boolean>>;
7
7
  }
8
+ /**
9
+ * Whether an element becomes the containing block of its `position: fixed`
10
+ * descendants. The individual `translate`, `rotate` and `scale` properties
11
+ * count like `transform`: Tailwind 4 writes its translate utilities to
12
+ * `translate`, so a dialog centred with `-translate-x-1/2` leaves `transform`
13
+ * at `none`.
14
+ */
15
+ export declare function createsFixedContainingBlock(style: CSSStyleDeclaration): boolean;
8
16
  /**
9
17
  * Setup sticky table header
10
18
  */
@@ -50,8 +50,18 @@ function findScrollParent(element) {
50
50
  parent = parent.parentElement;
51
51
  }
52
52
  }
53
+ function isSet(value) {
54
+ return value !== void 0 && value !== "" && value !== "none";
55
+ }
56
+ /**
57
+ * Whether an element becomes the containing block of its `position: fixed`
58
+ * descendants. The individual `translate`, `rotate` and `scale` properties
59
+ * count like `transform`: Tailwind 4 writes its translate utilities to
60
+ * `translate`, so a dialog centred with `-translate-x-1/2` leaves `transform`
61
+ * at `none`.
62
+ */
53
63
  function createsFixedContainingBlock(style) {
54
- return style.transform !== "none" || style.perspective !== "none" || style.filter !== "none" || style.backdropFilter !== void 0 && style.backdropFilter !== "none" || /transform|perspective|filter/.test(style.willChange) || /paint|layout|strict|content/.test(style.contain);
64
+ return isSet(style.transform) || isSet(style.translate) || isSet(style.rotate) || isSet(style.scale) || isSet(style.perspective) || isSet(style.filter) || isSet(style.backdropFilter) || /transform|translate|rotate|scale|perspective|filter/.test(style.willChange) || /paint|layout|strict|content/.test(style.contain);
55
65
  }
56
66
  /**
57
67
  * Where a `position: fixed` descendant's `top: 0; left: 0` lands in the viewport. That is the
@@ -101,6 +111,7 @@ function useStickyTableHeader(sticky = shallowRef(false), offsetTop, tableRefs)
101
111
  syncRafId = requestAnimationFrame(() => {
102
112
  syncRafId = null;
103
113
  applyColumnWidths(mainColumns, readColumnWidths(cloneColumns));
114
+ toggleStickyClass();
104
115
  });
105
116
  }
106
117
  function observeColumnResizes(cloneColumns, mainColumns) {
@@ -195,6 +206,6 @@ function useStickyTableHeader(sticky = shallowRef(false), offsetTop, tableRefs)
195
206
  return { stick: readonly(stick) };
196
207
  }
197
208
  //#endregion
198
- export { useStickyTableHeader };
209
+ export { createsFixedContainingBlock, useStickyTableHeader };
199
210
 
200
211
  //# sourceMappingURL=sticky-header.js.map