@leavepulse/ui 0.44.0 → 0.45.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 (271) hide show
  1. package/README.md +49 -3
  2. package/dist/assets/{LpCommandPalette-DCP5g2ik.css → LpCommandPalette-HzAA4gri.css} +21 -21
  3. package/dist/assets/{LpRollingText-CR1mb0Vt.css → LpRollingText-BGnE6q9j.css} +3 -0
  4. package/dist/assets/{LpShortcutsDialog-ChUyU7oV.css → LpShortcutsDialog-CqYX3yy6.css} +28 -7
  5. package/dist/assets/{LpTable-DDQNdf86.css → LpTable-CPwWxTJS.css} +49 -9
  6. package/dist/chunks/{ContextMenuItems-CrfeNPWT.js → ContextMenuItems-BtKU7074.js} +2 -1
  7. package/dist/chunks/{LayoutCanvas-D-jPOAvg.js → LayoutCanvas-BkaGXTjx.js} +1 -1
  8. package/dist/chunks/{LayoutNode-D8Xy9N-o.js → LayoutNode-B3_9E1Xa.js} +9 -5
  9. package/dist/chunks/{LpActionBar-BbwCF6l5.js → LpActionBar-CnfFVIwC.js} +3 -3
  10. package/dist/chunks/{LpAddressInput-BrOqkZqg.js → LpAddressInput-CJOYGZlI.js} +7 -7
  11. package/dist/chunks/{LpAlert-DkOIjsbq.js → LpAlert-B6ilrM7T.js} +1 -1
  12. package/dist/chunks/{LpAppShell-BUKPcHco.js → LpAppShell-B07Lf48e.js} +4 -4
  13. package/dist/chunks/{LpAutocomplete-DxV3Y9h4.js → LpAutocomplete-C8IgSMsi.js} +2 -1
  14. package/dist/chunks/{LpAvatar-ClBbEYZN.js → LpAvatar-C1XStdOo.js} +3 -3
  15. package/dist/chunks/{LpBreadcrumbs-B8IJgFIc.js → LpBreadcrumbs-CQJd73Gm.js} +1 -1
  16. package/dist/chunks/{LpButton-kqJBHIIb.js → LpButton-CKSwC6Mu.js} +1 -1
  17. package/dist/chunks/{LpCalendar-B7Ko5RdR.js → LpCalendar-DrA2McTU.js} +1 -1
  18. package/dist/chunks/{LpCard-CpRByR0q.js → LpCard-C2IvucmX.js} +1 -1
  19. package/dist/chunks/{LpCheckbox-DpqchzH4.js → LpCheckbox-Bkj13CbW.js} +1 -1
  20. package/dist/chunks/{LpCodeBlock-DeHJPq7p.js → LpCodeBlock-ca7ANFyW.js} +1 -1
  21. package/dist/chunks/{LpColorPicker-Bhf1qxhB.js → LpColorPicker-zz4VGkG4.js} +46 -20
  22. package/dist/chunks/{LpConfirmDialog-CIUOSnWo.js → LpConfirmDialog-BTVAR4tF.js} +3 -3
  23. package/dist/chunks/{LpConfirmHost-CDm0Wtpv.js → LpConfirmHost-Ci_uCHgQ.js} +1 -1
  24. package/dist/chunks/{LpContextMenu-DQw5dGfG.js → LpContextMenu-BqkAYa9X.js} +2 -1
  25. package/dist/chunks/{LpDatePicker-Drirx1AK.js → LpDatePicker-BrdFtSTy.js} +16 -10
  26. package/dist/chunks/LpDateRangePicker-Drd7EbIz.js +210 -0
  27. package/dist/chunks/{LpDisclosure-ChQYmjlR.js → LpDisclosure-Cc23-JOT.js} +1 -1
  28. package/dist/chunks/{LpDrawer-bScx-KMO.js → LpDrawer-CfRhHwmE.js} +3 -3
  29. package/dist/chunks/{LpDropdownMenu-DJpoqRCb.js → LpDropdownMenu-DdJGQzPn.js} +2 -1
  30. package/dist/chunks/{LpEmptyState-Dsdqz1Wj.js → LpEmptyState-BJbZXC8D.js} +1 -1
  31. package/dist/chunks/LpFieldNotice-wtieLUTj.js +64 -0
  32. package/dist/chunks/{LpFileTree-CSI_5SK2.js → LpFileTree-CNurOvkV.js} +3 -3
  33. package/dist/chunks/{LpFileTreeNode-B4MiSQX2.js → LpFileTreeNode-D_qRRdsH.js} +3 -3
  34. package/dist/chunks/{LpFormField-CIk5qosh.js → LpFormField-CtVYj6tO.js} +29 -15
  35. package/dist/chunks/{LpIcon-CCnX5_2j.js → LpIcon-DYtaid0K.js} +20 -6
  36. package/dist/chunks/{LpInput-C0e8y8_d.js → LpInput-D-YGQbEv.js} +39 -16
  37. package/dist/chunks/{LpLightbox-Mr0W_6HK.js → LpLightbox-DSDahF23.js} +1 -1
  38. package/dist/chunks/{LpLogViewer-YBOaFnc_.js → LpLogViewer-KypbgOyo.js} +252 -58
  39. package/dist/chunks/{LpMapPicker-NUzS9e37.js → LpMapPicker-Cc84LaM3.js} +1 -1
  40. package/dist/chunks/{LpMediaGallery-DfJmMW0I.js → LpMediaGallery-YKr-cQPN.js} +2 -2
  41. package/dist/chunks/{LpModal-DPwGKBPv.js → LpModal-BzTVeNqe.js} +3 -3
  42. package/dist/chunks/{LpModalNav-DPg5H9f3.js → LpModalNav-CjABSGCu.js} +14 -8
  43. package/dist/chunks/{LpNotificationBell-t5yn-siX.js → LpNotificationBell-CCwyJP5C.js} +26 -14
  44. package/dist/chunks/{LpNumberField-DWnQKktS.js → LpNumberField-B77O-z28.js} +1 -1
  45. package/dist/chunks/{LpPagination-DFngX7Xo.js → LpPagination-BcDlo3r_.js} +1 -1
  46. package/dist/chunks/{LpPasswordInput-CWVDuBJ5.js → LpPasswordInput-eKzQhbQP.js} +2 -2
  47. package/dist/chunks/{LpPhoneInput-CD-ZTeSd.js → LpPhoneInput-BCbrrhgE.js} +126 -23
  48. package/dist/chunks/{LpPie-BLco31Nx.js → LpPie-BEJ2SfI6.js} +9 -10
  49. package/dist/chunks/{LpPopover-p_e_shN3.js → LpPopover-DCKeqiVn.js} +1 -0
  50. package/dist/chunks/LpRangeCalendar-DZuaRV0W.js +331 -0
  51. package/dist/chunks/{LpRollingText-CM5gkSeF.js → LpRollingText-DB7upf-5.js} +1 -1
  52. package/dist/chunks/{LpScrollArea-D8xyKXcF.js → LpScrollArea-Bry1fdOM.js} +14 -2
  53. package/dist/chunks/{LpSection-aM6J_u51.js → LpSection-tKEH0jYf.js} +1 -1
  54. package/dist/chunks/{LpSegmented-Dejd7cIx.js → LpSegmented-C09EEam2.js} +1 -1
  55. package/dist/chunks/{LpSelect-DrRD9I7L.js → LpSelect-DNIL6MB9.js} +20 -23
  56. package/dist/chunks/{LpSidebar-BgwUNWpJ.js → LpSidebar-Bwws_X29.js} +3 -3
  57. package/dist/chunks/{LpSidebarNav-Bqxz8aWT.js → LpSidebarNav-kx-LdqNU.js} +3 -3
  58. package/dist/chunks/{LpStat-dXtyrdX9.js → LpStat-B-7udBRP.js} +1 -1
  59. package/dist/chunks/{LpStepper-Bk-XaQYU.js → LpStepper-C7XiVKZj.js} +34 -19
  60. package/dist/chunks/{LpSwitch-B1aLKulj.js → LpSwitch-OrcOJX9m.js} +1 -1
  61. package/dist/chunks/LpTableFilter-BV8PpLM9.js +158 -0
  62. package/dist/chunks/{LpTabs-_qMVaZJ6.js → LpTabs-29iW1XOx.js} +1 -1
  63. package/dist/chunks/LpTagsInput-D1SuqbhB.js +300 -0
  64. package/dist/chunks/{LpTextarea-yP8hGedR.js → LpTextarea-uuScWo6-.js} +1 -1
  65. package/dist/chunks/{LpThemeProvider-Wjm7Tuit.js → LpThemeProvider-Be4r2TQX.js} +1 -1
  66. package/dist/chunks/{LpThemeSwitcher-CKLc10KZ.js → LpThemeSwitcher-B4aVPbNC.js} +19 -22
  67. package/dist/chunks/{LpTimeField-C5HRgS5H.js → LpTimeField-Cqe7fXnh.js} +1 -1
  68. package/dist/chunks/{LpToaster--oT6lfhL.js → LpToaster-DTtWuKvq.js} +76 -9
  69. package/dist/chunks/{LpTooltip-CBdzeKNf.js → LpTooltip-CXVKcOXT.js} +3 -2
  70. package/dist/chunks/{LpTopologyCanvas-DocsLal3.js → LpTopologyCanvas-6ozNrQKh.js} +2 -1
  71. package/dist/chunks/{LpUptimeBar-DLtxOMRy.js → LpUptimeBar-ugcv25xj.js} +2 -2
  72. package/dist/chunks/icons-CkA0njzm.js +43 -0
  73. package/dist/chunks/{useTheme-C8JHUlPM.js → useTheme-DJm1JSsY.js} +47 -50
  74. package/dist/component-names.d.ts +1 -1
  75. package/dist/components/ContextMenuItems.vue.js +1 -1
  76. package/dist/components/LayoutCanvas.vue.js +1 -1
  77. package/dist/components/LayoutNode.vue.js +1 -1
  78. package/dist/components/LpActionBar.vue.js +1 -1
  79. package/dist/components/LpAddressInput.vue.js +1 -1
  80. package/dist/components/LpAlert.vue.js +1 -1
  81. package/dist/components/LpAppShell.vue.js +1 -1
  82. package/dist/components/LpAutocomplete.vue.js +1 -1
  83. package/dist/components/LpAvatar.vue.js +1 -1
  84. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  85. package/dist/components/LpButton.vue.d.ts +2 -2
  86. package/dist/components/LpButton.vue.js +1 -1
  87. package/dist/components/LpCalendar.vue.js +1 -1
  88. package/dist/components/LpCard.vue.js +1 -1
  89. package/dist/components/LpChart.vue.d.ts +16 -1
  90. package/dist/components/LpChart.vue.js +63 -5
  91. package/dist/components/LpCheckbox.vue.js +1 -1
  92. package/dist/components/LpCodeBlock.vue.js +1 -1
  93. package/dist/components/LpColorPicker.vue.d.ts +2 -2
  94. package/dist/components/LpColorPicker.vue.js +1 -1
  95. package/dist/components/LpCommandPalette.vue.d.ts +6 -0
  96. package/dist/components/LpCommandPalette.vue.js +62 -11
  97. package/dist/components/LpConfirmDialog.vue.js +1 -1
  98. package/dist/components/LpConfirmHost.vue.js +1 -1
  99. package/dist/components/LpContextMenu.vue.js +1 -1
  100. package/dist/components/LpDatePicker.vue.js +1 -1
  101. package/dist/components/LpDateRangePicker.vue.d.ts +60 -0
  102. package/dist/components/LpDateRangePicker.vue.js +4 -0
  103. package/dist/components/LpDisclosure.vue.js +1 -1
  104. package/dist/components/LpDrawer.vue.js +1 -1
  105. package/dist/components/LpDropdownMenu.vue.js +1 -1
  106. package/dist/components/LpDropzone.vue.js +1 -1
  107. package/dist/components/LpEmptyState.vue.js +1 -1
  108. package/dist/components/LpFieldNotice.vue.d.ts +8 -0
  109. package/dist/components/LpFieldNotice.vue.js +4 -0
  110. package/dist/components/LpFileTree.vue.js +1 -1
  111. package/dist/components/LpFileTreeNode.vue.js +1 -1
  112. package/dist/components/LpFormField.vue.d.ts +4 -1
  113. package/dist/components/LpFormField.vue.js +1 -1
  114. package/dist/components/LpIcon.vue.d.ts +0 -1
  115. package/dist/components/LpIcon.vue.js +1 -1
  116. package/dist/components/LpImageEditor.vue.js +2 -2
  117. package/dist/components/LpInput.vue.d.ts +19 -0
  118. package/dist/components/LpInput.vue.js +1 -1
  119. package/dist/components/LpLightbox.vue.js +1 -1
  120. package/dist/components/LpLogViewer.vue.d.ts +52 -1
  121. package/dist/components/LpLogViewer.vue.js +1 -1
  122. package/dist/components/LpMapPicker.vue.js +1 -1
  123. package/dist/components/LpMediaGallery.vue.js +1 -1
  124. package/dist/components/LpModal.vue.js +1 -1
  125. package/dist/components/LpModalNav.vue.js +1 -1
  126. package/dist/components/LpNotificationBell.vue.js +1 -1
  127. package/dist/components/LpNumberField.vue.js +1 -1
  128. package/dist/components/LpPagination.vue.js +1 -1
  129. package/dist/components/LpPasswordInput.vue.js +1 -1
  130. package/dist/components/LpPhoneInput.vue.d.ts +13 -0
  131. package/dist/components/LpPhoneInput.vue.js +1 -1
  132. package/dist/components/LpPie.vue.js +1 -1
  133. package/dist/components/LpPopover.vue.js +1 -1
  134. package/dist/components/LpProgress.vue.d.ts +1 -1
  135. package/dist/components/LpRangeCalendar.vue.d.ts +51 -0
  136. package/dist/components/LpRangeCalendar.vue.js +4 -0
  137. package/dist/components/LpRollingText.vue.d.ts +1 -1
  138. package/dist/components/LpRollingText.vue.js +1 -1
  139. package/dist/components/LpScrollArea.vue.d.ts +2 -2
  140. package/dist/components/LpScrollArea.vue.js +1 -1
  141. package/dist/components/LpSection.vue.js +1 -1
  142. package/dist/components/LpSegmented.vue.js +1 -1
  143. package/dist/components/LpSelect.vue.js +1 -1
  144. package/dist/components/LpShortcutsDialog.vue.d.ts +6 -0
  145. package/dist/components/LpShortcutsDialog.vue.js +56 -14
  146. package/dist/components/LpSidebar.vue.js +1 -1
  147. package/dist/components/LpSidebarNav.vue.js +1 -1
  148. package/dist/components/LpStat.vue.js +1 -1
  149. package/dist/components/LpStepper.vue.js +1 -1
  150. package/dist/components/LpSwitch.vue.js +1 -1
  151. package/dist/components/LpTable.vue.d.ts +84 -2
  152. package/dist/components/LpTable.vue.js +481 -107
  153. package/dist/components/LpTableFilter.vue.d.ts +31 -0
  154. package/dist/components/LpTableFilter.vue.js +4 -0
  155. package/dist/components/LpTabs.vue.js +1 -1
  156. package/dist/components/LpTagsInput.vue.d.ts +77 -0
  157. package/dist/components/LpTagsInput.vue.js +4 -0
  158. package/dist/components/LpTextarea.vue.js +1 -1
  159. package/dist/components/LpThemeProvider.vue.js +1 -1
  160. package/dist/components/LpThemeSwitcher.vue.d.ts +2 -2
  161. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  162. package/dist/components/LpTimeField.vue.js +1 -1
  163. package/dist/components/LpToaster.vue.d.ts +2 -0
  164. package/dist/components/LpToaster.vue.js +1 -1
  165. package/dist/components/LpTooltip.vue.js +1 -1
  166. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  167. package/dist/components/LpUptimeBar.vue.js +1 -1
  168. package/dist/components/chart.js +3 -2
  169. package/dist/components/commandSearch.js +3 -0
  170. package/dist/components/dateRange.d.ts +43 -0
  171. package/dist/components/dateRange.js +76 -0
  172. package/dist/components/exportData.d.ts +58 -0
  173. package/dist/components/exportData.js +165 -0
  174. package/dist/components/logSearch.d.ts +18 -0
  175. package/dist/components/logSearch.js +36 -0
  176. package/dist/components/phone.d.ts +41 -0
  177. package/dist/components/phone.js +80 -0
  178. package/dist/components/tableFilter.d.ts +36 -0
  179. package/dist/components/tableFilter.js +84 -0
  180. package/dist/components/tags.d.ts +20 -0
  181. package/dist/components/tags.js +37 -0
  182. package/dist/composables/useField.d.ts +4 -0
  183. package/dist/composables/useFieldNotice.d.ts +17 -0
  184. package/dist/composables/useFieldNotice.js +28 -0
  185. package/dist/composables/useForm.d.ts +53 -0
  186. package/dist/composables/useForm.js +207 -0
  187. package/dist/composables/useHotkeys.d.ts +65 -5
  188. package/dist/composables/useHotkeys.js +170 -9
  189. package/dist/composables/useInputMask.d.ts +59 -0
  190. package/dist/composables/useInputMask.js +170 -0
  191. package/dist/composables/useLocale.d.ts +2 -0
  192. package/dist/composables/useLocale.js +9 -0
  193. package/dist/composables/usePaintSelect.d.ts +22 -0
  194. package/dist/composables/usePaintSelect.js +169 -0
  195. package/dist/composables/useShift.js +7 -2
  196. package/dist/icons-all.d.ts +1 -0
  197. package/dist/icons-all.js +3 -0
  198. package/dist/icons.d.ts +14 -1
  199. package/dist/index.d.ts +29 -3
  200. package/dist/index.js +195 -138
  201. package/dist/kit-icons.d.ts +2 -0
  202. package/dist/theme/backdrop.d.ts +12 -0
  203. package/dist/theme/presets.d.ts +0 -2
  204. package/package.json +13 -4
  205. package/src/component-names.ts +4 -0
  206. package/src/components/ContextMenuItems.vue +1 -1
  207. package/src/components/LayoutNode.vue +11 -2
  208. package/src/components/LpActionBar.vue +1 -1
  209. package/src/components/LpAutocomplete.vue +1 -0
  210. package/src/components/LpAvatar.vue +2 -2
  211. package/src/components/LpButton.vue +1 -1
  212. package/src/components/LpChart.vue +80 -1
  213. package/src/components/LpColorPicker.vue +34 -1
  214. package/src/components/LpCommandPalette.vue +82 -8
  215. package/src/components/LpContextMenu.vue +1 -1
  216. package/src/components/LpDatePicker.vue +11 -4
  217. package/src/components/LpDateRangePicker.vue +226 -0
  218. package/src/components/LpDrawer.vue +1 -1
  219. package/src/components/LpDropdownMenu.vue +1 -0
  220. package/src/components/LpFieldNotice.vue +65 -0
  221. package/src/components/LpFormField.vue +33 -4
  222. package/src/components/LpIcon.vue +22 -4
  223. package/src/components/LpInput.vue +46 -6
  224. package/src/components/LpLogViewer.vue +225 -22
  225. package/src/components/LpModal.vue +2 -2
  226. package/src/components/LpModalNav.vue +13 -2
  227. package/src/components/LpNotificationBell.vue +15 -7
  228. package/src/components/LpPhoneInput.vue +179 -20
  229. package/src/components/LpPie.vue +8 -5
  230. package/src/components/LpPopover.vue +1 -0
  231. package/src/components/LpRangeCalendar.vue +365 -0
  232. package/src/components/LpRollingText.vue +3 -0
  233. package/src/components/LpScrollArea.vue +22 -2
  234. package/src/components/LpSelect.vue +0 -0
  235. package/src/components/LpShortcutsDialog.vue +77 -8
  236. package/src/components/LpStepper.vue +23 -10
  237. package/src/components/LpSwitch.vue +1 -1
  238. package/src/components/LpTable.vue +584 -65
  239. package/src/components/LpTableFilter.vue +175 -0
  240. package/src/components/LpTagsInput.vue +364 -0
  241. package/src/components/LpThemeSwitcher.vue +19 -23
  242. package/src/components/LpToaster.vue +91 -5
  243. package/src/components/LpTooltip.vue +9 -2
  244. package/src/components/LpTopologyCanvas.vue +7 -1
  245. package/src/components/chart.ts +5 -3
  246. package/src/components/commandSearch.ts +6 -0
  247. package/src/components/dateRange.ts +134 -0
  248. package/src/components/exportData.ts +228 -0
  249. package/src/components/logSearch.ts +46 -0
  250. package/src/components/phone.ts +140 -0
  251. package/src/components/tableFilter.ts +122 -0
  252. package/src/components/tags.ts +62 -0
  253. package/src/composables/useField.ts +4 -0
  254. package/src/composables/useFieldNotice.ts +59 -0
  255. package/src/composables/useForm.ts +314 -0
  256. package/src/composables/useHotkeys.ts +269 -13
  257. package/src/composables/useInputMask.ts +254 -0
  258. package/src/composables/useLocale.ts +14 -0
  259. package/src/composables/usePaintSelect.ts +243 -0
  260. package/src/composables/useShift.ts +13 -2
  261. package/src/iconSubset.d.mts +10 -0
  262. package/src/iconSubset.mjs +51 -0
  263. package/src/icons-all.ts +10 -0
  264. package/src/icons.ts +59 -8
  265. package/src/index.ts +37 -3
  266. package/src/kit-icons.ts +5 -0
  267. package/src/nuxt.ts +30 -2
  268. package/src/runtime/theme.plugin.ts +2 -2
  269. package/src/theme/backdrop.ts +69 -0
  270. package/src/theme/presets.ts +6 -64
  271. package/src/tokens/tokens.css +53 -4
@@ -1,7 +1,18 @@
1
1
  import { type MaybeRefOrGetter } from "vue";
2
- export interface HotkeyBinding {
3
- /** Layout-produced key, compared case-insensitively ("l", "f2", "escape"). */
2
+ /** One key of a sequence: a bare key ("g") or a full chord. */
3
+ export interface HotkeyStep {
4
4
  key: string;
5
+ code?: string;
6
+ ctrl?: boolean;
7
+ shift?: boolean;
8
+ alt?: boolean;
9
+ }
10
+ export interface HotkeyBinding {
11
+ /**
12
+ * Layout-produced key, compared case-insensitively ("l", "f2", "escape").
13
+ * Required unless the binding is a `sequence`.
14
+ */
15
+ key?: string;
5
16
  /**
6
17
  * Physical key position ("KeyL", "F2", "Digit1"). Matches on its own, which
7
18
  * is what makes non-Latin layouts work.
@@ -11,6 +22,16 @@ export interface HotkeyBinding {
11
22
  ctrl?: boolean;
12
23
  shift?: boolean;
13
24
  alt?: boolean;
25
+ /**
26
+ * Keys pressed one after another: `"g i"`, or steps for chords and physical
27
+ * positions (`[{ key: "g", code: "KeyG" }, { key: "i", code: "KeyI" }]`).
28
+ * The string form splits on spaces, so a Space step needs the array form.
29
+ */
30
+ sequence?: string | (string | HotkeyStep)[];
31
+ /** How long a sequence waits for its next key, in ms. Default 1000. */
32
+ timeout?: number;
33
+ /** Extra condition checked on every press; `false` leaves the key alone. */
34
+ when?: (event: KeyboardEvent) => boolean;
14
35
  /**
15
36
  * Runs even while a text field has focus. Only for shortcuts that mean
16
37
  * something there — Escape, and the palette the user reaches from anywhere.
@@ -48,14 +69,19 @@ export interface HotkeyHelp {
48
69
  /**
49
70
  * Written the way it reads, "+"-separated. Defaults to one built from the
50
71
  * binding; set it where the binding is not the whole story, such as the
51
- * second key of a chord ("G+D") or a key that needs Shift to type ("?").
72
+ * key that needs Shift to type ("?"). Sequences read with spaces: "G I".
52
73
  */
53
74
  combo?: string;
54
75
  }
55
76
  export interface ShortcutEntry {
56
- /** Rendered one <kbd> per "+"-separated key, so write it the way it reads. */
77
+ /**
78
+ * Rendered one <kbd> per "+"-separated key, so write it the way it reads.
79
+ * Spaces separate the steps of a sequence ("G I", "Ctrl+K Ctrl+S").
80
+ */
57
81
  combo: string;
58
82
  label: string;
83
+ /** Another mounted binding answers the same keys at the same priority. */
84
+ conflict?: boolean;
59
85
  }
60
86
  export interface ShortcutGroup {
61
87
  title: string;
@@ -77,7 +103,15 @@ export declare function useAppleKeyboard(): import("vue").Ref<boolean, boolean>;
77
103
  * Meta as one modifier, so only the label may differ — never the binding.
78
104
  */
79
105
  export declare function appleModifierLabel(key: string, apple?: boolean): string;
80
- export declare function matchesHotkey(event: KeyboardEvent, binding: HotkeyBinding): boolean;
106
+ type KeyLike = Pick<KeyboardEvent, "key" | "code" | "ctrlKey" | "metaKey" | "shiftKey" | "altKey">;
107
+ export declare function matchesHotkey(event: KeyLike, binding: HotkeyBinding | HotkeyStep): boolean;
108
+ /** The keys a binding waits for, in order: one step for a plain binding. */
109
+ export declare function hotkeySteps(binding: HotkeyBinding): HotkeyStep[];
110
+ /**
111
+ * Where a run of presses stands against a binding: "full" when they spell it
112
+ * out, "prefix" when more keys would, otherwise null.
113
+ */
114
+ export declare function sequenceProgress(presses: KeyLike[], binding: HotkeyBinding): "full" | "prefix" | null;
81
115
  /**
82
116
  * Bind a list of hotkeys for as long as the calling component is mounted.
83
117
  *
@@ -102,7 +136,32 @@ export interface UseHotkeysOptions {
102
136
  */
103
137
  scope?: MaybeRefOrGetter<HTMLElement | null | undefined>;
104
138
  }
139
+ /**
140
+ * The keys of a sequence typed so far ("G" after the first key of "g i"),
141
+ * empty when nothing is pending. For a "G…" hint while the next key is awaited.
142
+ */
143
+ export declare const pendingHotkey: import("vue").ComputedRef<string[]>;
105
144
  export declare function useHotkeys(bindings: MaybeRefOrGetter<HotkeyBinding[]>, options?: UseHotkeysOptions): void;
145
+ export interface HotkeyConflict {
146
+ /** The keys both answer, written as in the shortcut reference. */
147
+ combo: string;
148
+ /** `help` labels of the clashing bindings; unlabelled ones are omitted. */
149
+ labels: string[];
150
+ priority: number;
151
+ }
152
+ /**
153
+ * Pure part of `hotkeyConflicts`: bindings from different `useHotkeys` calls
154
+ * answering the same keys at the same priority within the same scope. Within
155
+ * one call a repeat is deliberate (the first may decline), and a binding with
156
+ * `when` is conditional by design, so neither counts.
157
+ */
158
+ export declare function findHotkeyConflicts(groups: {
159
+ bindings: HotkeyBinding[];
160
+ priority: number;
161
+ scope: unknown;
162
+ }[]): HotkeyConflict[];
163
+ /** Bindings among the mounted ones that answer the same keys. */
164
+ export declare const hotkeyConflicts: import("vue").ComputedRef<HotkeyConflict[]>;
106
165
  /**
107
166
  * The shortcuts that work right now, grouped for `LpShortcutsDialog`: every
108
167
  * binding with `help` from every mounted `useHotkeys`. A screen that binds its
@@ -115,3 +174,4 @@ export declare const activeShortcuts: import("vue").ComputedRef<ShortcutGroup[]>
115
174
  * when it lists both its own entries and the live ones.
116
175
  */
117
176
  export declare function mergeShortcutGroups(...lists: ShortcutGroup[][]): ShortcutGroup[];
177
+ export {};
@@ -1,4 +1,4 @@
1
- import { shallowReactive, computed, toValue, getCurrentInstance, onMounted, onBeforeUnmount, ref } from "vue";
1
+ import { shallowReactive, ref, computed, toValue, getCurrentInstance, onMounted, onBeforeUnmount } from "vue";
2
2
  import { topModalElement } from "../components/modalDepth.js";
3
3
  const isAppleKeyboard = typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/.test(navigator.platform || "");
4
4
  function useAppleKeyboard() {
@@ -32,6 +32,7 @@ function isTextEntry(target) {
32
32
  }
33
33
  const MODIFIER_KEYS = /* @__PURE__ */ new Set(["control", "meta", "shift", "alt"]);
34
34
  function matchesHotkey(event, binding) {
35
+ if (binding.key === void 0) return false;
35
36
  const key = binding.key.toLowerCase();
36
37
  const ctrlHeld = event.ctrlKey || event.metaKey;
37
38
  const wantsCtrl = Boolean(binding.ctrl) || key === "control" || key === "meta";
@@ -43,6 +44,24 @@ function matchesHotkey(event, binding) {
43
44
  if (MODIFIER_KEYS.has(key)) return event.key.toLowerCase() === key;
44
45
  return event.key.toLowerCase() === key || binding.code !== void 0 && event.code === binding.code;
45
46
  }
47
+ function hotkeySteps(binding) {
48
+ const raw = binding.sequence;
49
+ if (raw === void 0) {
50
+ if (binding.key === void 0) return [];
51
+ const { key, code, ctrl, shift, alt } = binding;
52
+ return [{ key, code, ctrl, shift, alt }];
53
+ }
54
+ const list = typeof raw === "string" ? raw.trim().split(/\s+/) : raw;
55
+ return list.map((step) => typeof step === "string" ? { key: step } : step);
56
+ }
57
+ function sequenceProgress(presses, binding) {
58
+ const steps = hotkeySteps(binding);
59
+ if (!steps.length || presses.length > steps.length) return null;
60
+ for (let i = 0; i < presses.length; i++) {
61
+ if (!matchesHotkey(presses[i], steps[i])) return null;
62
+ }
63
+ return presses.length === steps.length ? "full" : "prefix";
64
+ }
46
65
  const STATE_KEY = /* @__PURE__ */ Symbol.for("@leavepulse/ui:hotkeys");
47
66
  const g = globalThis;
48
67
  const state = g[STATE_KEY] ??= {
@@ -66,13 +85,23 @@ function insideOf(entry, layer) {
66
85
  const node = scope ?? entry.host();
67
86
  return !!node && layer.contains(node);
68
87
  }
69
- function onKeydown(event) {
70
- if (event.isComposing || event.defaultPrevented) return;
88
+ const DEFAULT_TIMEOUT = 1e3;
89
+ const pressed = [];
90
+ let held = [];
91
+ let holdTimer;
92
+ const pendingKeys = ref([]);
93
+ const pendingHotkey = computed(() => pendingKeys.value);
94
+ function snapshot(event) {
95
+ const { key, code, ctrlKey, metaKey, shiftKey, altKey } = event;
96
+ return { key, code, ctrlKey, metaKey, shiftKey, altKey };
97
+ }
98
+ function eligible(event) {
71
99
  const inTextEntry = isTextEntry(event.target);
72
100
  const blocker = event.key === "Escape" ? topLayer() : topModalElement();
73
101
  const entries = [...mounted].sort(
74
102
  (a, b) => Number(toValue(b.priority) || 0) - Number(toValue(a.priority) || 0) || b.order - a.order
75
103
  );
104
+ const out = [];
76
105
  for (const entry of entries) {
77
106
  if (toValue(entry.enabled) === false || !inScope(event.target, entry.scope)) continue;
78
107
  const covered = !!blocker && !insideOf(entry, blocker);
@@ -80,12 +109,73 @@ function onKeydown(event) {
80
109
  if (inTextEntry && !binding.allowInInput) continue;
81
110
  if (event.repeat && binding.repeat === false) continue;
82
111
  if (covered && !binding.overLayers) continue;
83
- if (!matchesHotkey(event, binding)) continue;
84
- if (binding.handler(event) === false) continue;
112
+ if (binding.when && !binding.when(event)) continue;
113
+ out.push(binding);
114
+ }
115
+ }
116
+ return out;
117
+ }
118
+ function run(candidates) {
119
+ for (const { binding, event } of candidates) {
120
+ if (binding.handler(event) !== false) return true;
121
+ }
122
+ return false;
123
+ }
124
+ function resetSequence() {
125
+ clearTimeout(holdTimer);
126
+ holdTimer = void 0;
127
+ pressed.length = 0;
128
+ held = [];
129
+ pendingKeys.value = [];
130
+ }
131
+ function flushHeld() {
132
+ const waiting = held;
133
+ resetSequence();
134
+ run(waiting);
135
+ }
136
+ function onKeydown(event) {
137
+ if (event.isComposing || event.defaultPrevented) return;
138
+ if (pressed.length && MODIFIER_KEYS.has(event.key.toLowerCase())) return;
139
+ if (pressed.length && event.key === "Escape") {
140
+ resetSequence();
141
+ }
142
+ const presses = [...pressed, snapshot(event)];
143
+ const bindings = eligible(event);
144
+ const full = [];
145
+ let waitFor = 0;
146
+ for (const binding of bindings) {
147
+ const progress = sequenceProgress(presses, binding);
148
+ if (progress === "full") full.push({ binding, event });
149
+ else if (progress === "prefix") waitFor = Math.max(waitFor, binding.timeout ?? DEFAULT_TIMEOUT);
150
+ }
151
+ if (waitFor) {
152
+ clearTimeout(holdTimer);
153
+ pressed.push(snapshot(event));
154
+ held = full;
155
+ pendingKeys.value = pressed.map(pressLabel);
156
+ holdTimer = setTimeout(flushHeld, waitFor);
157
+ event.preventDefault();
158
+ return;
159
+ }
160
+ if (full.length) {
161
+ resetSequence();
162
+ if (run(full)) {
85
163
  event.preventDefault();
86
164
  return;
87
165
  }
88
166
  }
167
+ if (pressed.length) {
168
+ flushHeld();
169
+ onKeydown(event);
170
+ }
171
+ }
172
+ function pressLabel(press) {
173
+ return chordOf({
174
+ key: press.key,
175
+ ctrl: press.ctrlKey || press.metaKey,
176
+ alt: press.altKey,
177
+ shift: press.shiftKey && press.key.length !== 1
178
+ });
89
179
  }
90
180
  function useHotkeys(bindings, options = {}) {
91
181
  const instance = getCurrentInstance();
@@ -102,8 +192,10 @@ function useHotkeys(bindings, options = {}) {
102
192
  };
103
193
  onMounted(() => {
104
194
  mounted.add(entry);
195
+ warnConflicts();
105
196
  if (!state.listening) {
106
197
  window.addEventListener("keydown", onKeydown);
198
+ window.addEventListener("blur", resetSequence);
107
199
  state.listening = true;
108
200
  }
109
201
  });
@@ -111,7 +203,9 @@ function useHotkeys(bindings, options = {}) {
111
203
  mounted.delete(entry);
112
204
  if (!mounted.size && state.listening) {
113
205
  window.removeEventListener("keydown", onKeydown);
206
+ window.removeEventListener("blur", resetSequence);
114
207
  state.listening = false;
208
+ resetSequence();
115
209
  }
116
210
  });
117
211
  }
@@ -127,20 +221,82 @@ const KEY_NAMES = {
127
221
  arrowleft: "←",
128
222
  arrowright: "→"
129
223
  };
224
+ function chordOf(step) {
225
+ const key = step.key.toLowerCase();
226
+ const name = KEY_NAMES[key] ?? (key.length === 1 || /^f\d+$/.test(key) ? key.toUpperCase() : step.key);
227
+ return [step.ctrl && "Ctrl", step.alt && "Alt", step.shift && "Shift", name].filter(Boolean).join("+");
228
+ }
130
229
  function comboOf(binding) {
131
- const key = binding.key.toLowerCase();
132
- const name = KEY_NAMES[key] ?? (key.length === 1 || /^f\d+$/.test(key) ? key.toUpperCase() : binding.key);
133
- return [binding.ctrl && "Ctrl", binding.alt && "Alt", binding.shift && "Shift", name].filter(Boolean).join("+");
230
+ return hotkeySteps(binding).map(chordOf).join(" ");
231
+ }
232
+ function signatureOf(binding) {
233
+ return hotkeySteps(binding).map((step) => {
234
+ const key = step.key.toLowerCase();
235
+ const symbol = key.length === 1 && key === key.toUpperCase();
236
+ const shift = step.shift === void 0 && symbol ? "*" : String(Boolean(step.shift));
237
+ return `${Boolean(step.ctrl)}:${Boolean(step.alt)}:${shift}:${key}`;
238
+ }).join(" ");
239
+ }
240
+ function findHotkeyConflicts(groups) {
241
+ const scopeIds = /* @__PURE__ */ new Map();
242
+ const seen = /* @__PURE__ */ new Map();
243
+ groups.forEach((group, owner) => {
244
+ if (!scopeIds.has(group.scope)) scopeIds.set(group.scope, scopeIds.size);
245
+ for (const binding of group.bindings) {
246
+ if (binding.when || !hotkeySteps(binding).length) continue;
247
+ const id = `${group.priority}|${scopeIds.get(group.scope)}|${signatureOf(binding)}`;
248
+ const slot = seen.get(id) ?? {
249
+ combo: binding.help?.combo ?? comboOf(binding),
250
+ labels: [],
251
+ owners: /* @__PURE__ */ new Set(),
252
+ priority: group.priority
253
+ };
254
+ slot.owners.add(owner);
255
+ if (binding.help && !slot.labels.includes(binding.help.label)) slot.labels.push(binding.help.label);
256
+ seen.set(id, slot);
257
+ }
258
+ });
259
+ return [...seen.values()].filter((slot) => slot.owners.size > 1).map(({ combo, labels, priority }) => ({ combo, labels, priority }));
260
+ }
261
+ const hotkeyConflicts = computed(
262
+ () => findHotkeyConflicts(
263
+ [...mounted].filter((entry) => toValue(entry.enabled) !== false).map((entry) => ({
264
+ bindings: entry.read(),
265
+ priority: Number(toValue(entry.priority) || 0),
266
+ scope: toValue(entry.scope) ?? null
267
+ }))
268
+ )
269
+ );
270
+ const warned = /* @__PURE__ */ new Set();
271
+ function isDev() {
272
+ try {
273
+ return process.env.NODE_ENV !== "production";
274
+ } catch {
275
+ return false;
276
+ }
277
+ }
278
+ function warnConflicts() {
279
+ if (!isDev()) return;
280
+ for (const conflict of hotkeyConflicts.value) {
281
+ const id = `${conflict.priority}|${conflict.combo}`;
282
+ if (warned.has(id)) continue;
283
+ warned.add(id);
284
+ console.warn(
285
+ `[@leavepulse/ui] ${conflict.combo} is bound more than once at priority ${conflict.priority}` + (conflict.labels.length ? ` (${conflict.labels.join(", ")})` : "") + "; only the most recently mounted one runs unless it declines."
286
+ );
287
+ }
134
288
  }
135
289
  const activeShortcuts = computed(() => {
136
290
  const groups = /* @__PURE__ */ new Map();
291
+ const clashing = new Set(hotkeyConflicts.value.map((c) => c.combo));
137
292
  for (const entry of mounted) {
138
293
  if (toValue(entry.enabled) === false) continue;
139
294
  for (const binding of entry.read()) {
140
295
  if (!binding.help) continue;
141
296
  const combo = binding.help.combo ?? comboOf(binding);
142
297
  const entries = groups.get(binding.help.group) ?? [];
143
- if (!entries.some((e) => e.combo === combo)) entries.push({ combo, label: binding.help.label });
298
+ const conflict = clashing.has(combo) || void 0;
299
+ if (!entries.some((e) => e.combo === combo)) entries.push({ combo, label: binding.help.label, conflict });
144
300
  groups.set(binding.help.group, entries);
145
301
  }
146
302
  }
@@ -162,9 +318,14 @@ function mergeShortcutGroups(...lists) {
162
318
  export {
163
319
  activeShortcuts,
164
320
  appleModifierLabel,
321
+ findHotkeyConflicts,
322
+ hotkeyConflicts,
323
+ hotkeySteps,
165
324
  isAppleKeyboard,
166
325
  matchesHotkey,
167
326
  mergeShortcutGroups,
327
+ pendingHotkey,
328
+ sequenceProgress,
168
329
  useAppleKeyboard,
169
330
  useHotkeys
170
331
  };
@@ -0,0 +1,59 @@
1
+ import { type MaybeRefOrGetter } from "vue";
2
+ export type MaskToken = RegExp | {
3
+ pattern: RegExp;
4
+ transform?: (ch: string) => string;
5
+ };
6
+ export type MaskTokens = Record<string, MaskToken>;
7
+ export type MaskSpec = string | readonly string[] | ((raw: string) => string);
8
+ export declare const DEFAULT_MASK_TOKENS: MaskTokens;
9
+ type Slot = {
10
+ literal: string;
11
+ } | {
12
+ test: (ch: string) => boolean;
13
+ transform: (ch: string) => string;
14
+ };
15
+ export interface MaskResult {
16
+ masked: string;
17
+ raw: string;
18
+ /** Caret offset in `masked`. */
19
+ caret: number;
20
+ /** Every token of the mask is filled. */
21
+ complete: boolean;
22
+ }
23
+ export interface MaskOptions {
24
+ mask: MaskSpec;
25
+ tokens?: MaskTokens;
26
+ /**
27
+ * Caret position: an offset into `input`, or with `fromRaw` the number of raw
28
+ * characters before it. Defaults to the end.
29
+ */
30
+ caret?: number;
31
+ /** `input` holds raw characters only; literals in it are never consumed as mask literals. */
32
+ fromRaw?: boolean;
33
+ }
34
+ export declare function parseMask(mask: string, tokens?: MaskTokens): Slot[];
35
+ /** Format `input` through a mask and map the caret into the result. */
36
+ export declare function applyMask(input: string, options: MaskOptions): MaskResult;
37
+ /**
38
+ * The value after deleting one accepted character next to a collapsed caret,
39
+ * skipping over literals, or null when there is nothing to delete that way.
40
+ */
41
+ export declare function maskDelete(value: string, caret: number, direction: "backward" | "forward", options: Omit<MaskOptions, "caret" | "fromRaw">): MaskResult | null;
42
+ export interface UseInputMaskOptions {
43
+ mask: MaybeRefOrGetter<MaskSpec | undefined>;
44
+ tokens?: MaybeRefOrGetter<MaskTokens | undefined>;
45
+ /** Called with every value the user produces. */
46
+ onChange?: (masked: string, raw: string, complete: boolean) => void;
47
+ }
48
+ /**
49
+ * Bind `onBeforeInput`, `onInput` and `onCompositionEnd` to the input, and show
50
+ * `format(model).masked` as its value.
51
+ */
52
+ export declare function useInputMask(options: UseInputMaskOptions): {
53
+ active: import("vue").ComputedRef<boolean>;
54
+ format: (value: string | null | undefined) => MaskResult;
55
+ onBeforeInput: (event: InputEvent) => void;
56
+ onInput: (event: Event) => void;
57
+ onCompositionEnd: (event: CompositionEvent) => void;
58
+ };
59
+ export {};
@@ -0,0 +1,170 @@
1
+ import { computed, toValue } from "vue";
2
+ const DEFAULT_MASK_TOKENS = {
3
+ "0": /\d/,
4
+ a: new RegExp("\\p{L}", "u"),
5
+ A: { pattern: new RegExp("\\p{L}", "u"), transform: (ch) => ch.toLocaleUpperCase() },
6
+ "*": /[\p{L}\d]/u
7
+ };
8
+ function tester(token) {
9
+ const pattern = token instanceof RegExp ? token : token.pattern;
10
+ const re = new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, ""));
11
+ const transform = token instanceof RegExp ? void 0 : token.transform;
12
+ return { test: (ch) => re.test(ch), transform: transform ?? ((ch) => ch) };
13
+ }
14
+ function parseMask(mask, tokens = DEFAULT_MASK_TOKENS) {
15
+ const slots = [];
16
+ const chars = Array.from(mask);
17
+ for (let i = 0; i < chars.length; i++) {
18
+ const ch = chars[i];
19
+ if (ch === "\\" && i + 1 < chars.length) {
20
+ slots.push({ literal: chars[++i] });
21
+ continue;
22
+ }
23
+ const token = tokens[ch];
24
+ slots.push(token ? tester(token) : { literal: ch });
25
+ }
26
+ return slots;
27
+ }
28
+ function run(slots, input, caret, fromRaw) {
29
+ const tokenStarts = [];
30
+ const tokenEnds = [];
31
+ const tokenOffsets = [];
32
+ let out = "";
33
+ let raw = "";
34
+ let pending = "";
35
+ let si = 0;
36
+ let offset = 0;
37
+ let before = 0;
38
+ let overflow = false;
39
+ for (const ch of input) {
40
+ const at2 = offset;
41
+ offset += ch.length;
42
+ if (si >= slots.length) {
43
+ if (slots.some((s) => "test" in s && s.test(ch))) overflow = true;
44
+ continue;
45
+ }
46
+ while (si < slots.length) {
47
+ const slot = slots[si];
48
+ if ("literal" in slot) {
49
+ pending += slot.literal;
50
+ si++;
51
+ if (!fromRaw && slot.literal === ch) break;
52
+ continue;
53
+ }
54
+ if (slot.test(ch)) {
55
+ const value = slot.transform(ch);
56
+ out += pending;
57
+ pending = "";
58
+ tokenStarts.push(out.length);
59
+ out += value;
60
+ tokenEnds.push(out.length);
61
+ raw += value;
62
+ tokenOffsets.push(at2);
63
+ si++;
64
+ if (fromRaw ? raw.length <= caret : at2 < caret) before++;
65
+ }
66
+ break;
67
+ }
68
+ }
69
+ const rest = slots.slice(si);
70
+ const complete = !rest.some((s) => "test" in s);
71
+ if (complete && out) out += pending + rest.map((s) => "literal" in s ? s.literal : "").join("");
72
+ const at = before === 0 ? tokenStarts[0] ?? 0 : tokenEnds[before - 1];
73
+ return {
74
+ masked: out,
75
+ raw,
76
+ caret: complete && before === tokenEnds.length ? out.length : at,
77
+ complete,
78
+ overflow,
79
+ tokenOffsets
80
+ };
81
+ }
82
+ function resolve(options, input, caret) {
83
+ const tokens = options.tokens ?? DEFAULT_MASK_TOKENS;
84
+ const fromRaw = !!options.fromRaw;
85
+ const { mask } = options;
86
+ if (typeof mask === "string") return run(parseMask(mask, tokens), input, caret, fromRaw);
87
+ if (typeof mask === "function") {
88
+ let current = mask("");
89
+ let result = run(parseMask(current, tokens), input, caret, fromRaw);
90
+ for (let i = 0; i < 4; i++) {
91
+ const next = mask(result.raw);
92
+ if (next === current) break;
93
+ current = next;
94
+ result = run(parseMask(current, tokens), input, caret, fromRaw);
95
+ }
96
+ return result;
97
+ }
98
+ let last;
99
+ for (const m of mask) {
100
+ last = run(parseMask(m, tokens), input, caret, fromRaw);
101
+ if (!last.overflow) return last;
102
+ }
103
+ return last ?? run([], input, caret, fromRaw);
104
+ }
105
+ function applyMask(input, options) {
106
+ const caret = options.caret ?? (options.fromRaw ? Number.POSITIVE_INFINITY : input.length);
107
+ const { masked, raw, caret: at, complete } = resolve(options, input, caret);
108
+ return { masked, raw, caret: at, complete };
109
+ }
110
+ function maskDelete(value, caret, direction, options) {
111
+ const { tokenOffsets, raw } = resolve(options, value, caret);
112
+ const index = direction === "backward" ? tokenOffsets.filter((o) => o < caret).length - 1 : tokenOffsets.findIndex((o) => o >= caret);
113
+ if (index < 0) return null;
114
+ const chars = Array.from(raw);
115
+ chars.splice(index, 1);
116
+ return applyMask(chars.join(""), { ...options, caret: index, fromRaw: true });
117
+ }
118
+ function useInputMask(options) {
119
+ const spec = computed(() => {
120
+ const mask = toValue(options.mask);
121
+ return mask === void 0 || mask === "" ? void 0 : { mask, tokens: toValue(options.tokens) };
122
+ });
123
+ function format(value) {
124
+ const s = spec.value;
125
+ const text = value ?? "";
126
+ return s ? applyMask(text, s) : { masked: text, raw: text, caret: text.length, complete: true };
127
+ }
128
+ function commit(el, result) {
129
+ if (el.value !== result.masked) el.value = result.masked;
130
+ if (el.ownerDocument.activeElement === el) {
131
+ try {
132
+ el.setSelectionRange(result.caret, result.caret);
133
+ } catch {
134
+ }
135
+ }
136
+ options.onChange?.(result.masked, result.raw, result.complete);
137
+ }
138
+ function onBeforeInput(event) {
139
+ const s = spec.value;
140
+ const el = event.target;
141
+ if (!s || event.isComposing || !(el instanceof HTMLInputElement)) return;
142
+ const backward = event.inputType === "deleteContentBackward";
143
+ if (!backward && event.inputType !== "deleteContentForward") return;
144
+ const start = el.selectionStart;
145
+ if (start === null || start !== el.selectionEnd) return;
146
+ event.preventDefault();
147
+ const result = maskDelete(el.value, start, backward ? "backward" : "forward", s);
148
+ if (result) commit(el, result);
149
+ }
150
+ function reformat(el) {
151
+ const s = spec.value;
152
+ if (!s) return;
153
+ commit(el, applyMask(el.value, { ...s, caret: el.selectionStart ?? el.value.length }));
154
+ }
155
+ function onInput(event) {
156
+ if (event.isComposing || !(event.target instanceof HTMLInputElement)) return;
157
+ reformat(event.target);
158
+ }
159
+ function onCompositionEnd(event) {
160
+ if (event.target instanceof HTMLInputElement) reformat(event.target);
161
+ }
162
+ return { active: computed(() => !!spec.value), format, onBeforeInput, onInput, onCompositionEnd };
163
+ }
164
+ export {
165
+ DEFAULT_MASK_TOKENS,
166
+ applyMask,
167
+ maskDelete,
168
+ parseMask,
169
+ useInputMask
170
+ };
@@ -0,0 +1,2 @@
1
+ import { type ComputedRef, type MaybeRefOrGetter } from "vue";
2
+ export declare function useLocale(own?: MaybeRefOrGetter<string | undefined>): ComputedRef<string | undefined>;
@@ -0,0 +1,9 @@
1
+ import { computed, toValue } from "vue";
2
+ import { injectConfigProviderContext } from "reka-ui";
3
+ function useLocale(own) {
4
+ const context = injectConfigProviderContext({});
5
+ return computed(() => toValue(own) || context.locale?.value || void 0);
6
+ }
7
+ export {
8
+ useLocale
9
+ };
@@ -0,0 +1,22 @@
1
+ import { type MaybeRefOrGetter, type Ref } from "vue";
2
+ import { type HotkeyHelp } from "./useHotkeys";
3
+ export interface UsePaintSelectOptions<K> {
4
+ /** The element that holds the items. */
5
+ root: Ref<HTMLElement | null>;
6
+ selection: () => readonly K[];
7
+ set: (keys: K[]) => void;
8
+ /** Turn painting off without unmounting (e.g. selection mode is off). */
9
+ enabled?: MaybeRefOrGetter<boolean>;
10
+ /** Map an item's `data-paint-key` back to its key. Defaults to the string itself. */
11
+ resolve?: (raw: string) => K | undefined;
12
+ /** Every key, for Ctrl+A while the focus is inside `root`. Omit to leave Ctrl+A alone. */
13
+ selectAll?: () => K[];
14
+ /** How Ctrl+A is listed in the shortcuts reference. */
15
+ help?: MaybeRefOrGetter<HotkeyHelp | undefined>;
16
+ }
17
+ export declare function usePaintSelect<K = string>(options: UsePaintSelectOptions<K>): {
18
+ onPointerDown: (event: PointerEvent, key: K, behaviour?: {
19
+ hold?: number;
20
+ }) => void;
21
+ painting: Readonly<Ref<boolean, boolean>>;
22
+ };