@leavepulse/ui 0.43.3 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/README.md +75 -7
  2. package/dist/assets/{LpCommandPalette-DH7sPcKP.css → LpCommandPalette-DCP5g2ik.css} +21 -21
  3. package/dist/assets/{LpDropzone-BBoWvi-J.css → LpDropzone-i4BdMi1H.css} +12 -12
  4. package/dist/assets/{LpImageEditor-Ct7Y3aNu.css → LpImageEditor-CbMA82g8.css} +6 -6
  5. package/dist/assets/{LpShortcutsDialog-C6YoQ8_M.css → LpShortcutsDialog-ChUyU7oV.css} +7 -7
  6. package/dist/assets/{LpSkeleton-D7xU8IPV.css → LpSkeleton-H3Z5dEIk.css} +4 -1
  7. package/dist/assets/{LpTable-BgPLJOXa.css → LpTable-DDQNdf86.css} +9 -9
  8. package/dist/chunks/ContextMenuItems-CrfeNPWT.js +123 -0
  9. package/dist/chunks/{LayoutCanvas-Dd20boUG.js → LayoutCanvas-D-jPOAvg.js} +26 -9
  10. package/dist/chunks/{LayoutNode-DGpZA9LX.js → LayoutNode-D8Xy9N-o.js} +20 -8
  11. package/dist/chunks/{LpActionBar-BlvtGB2P.js → LpActionBar-BbwCF6l5.js} +24 -10
  12. package/dist/chunks/{LpAddressInput-B-Zvxsn7.js → LpAddressInput-BrOqkZqg.js} +53 -48
  13. package/dist/chunks/{LpAlert-BAYHDSS1.js → LpAlert-DkOIjsbq.js} +15 -8
  14. package/dist/chunks/{LpAppShell-BGQVIY-3.js → LpAppShell-BUKPcHco.js} +51 -24
  15. package/dist/chunks/{LpAutocomplete-DExlIZe_.js → LpAutocomplete-DxV3Y9h4.js} +50 -23
  16. package/dist/chunks/{LpAvatar-B3-xAOH3.js → LpAvatar-ClBbEYZN.js} +17 -8
  17. package/dist/chunks/LpBreadcrumbs-B8IJgFIc.js +95 -0
  18. package/dist/chunks/{LpButton-5VXXCjwF.js → LpButton-kqJBHIIb.js} +44 -14
  19. package/dist/chunks/{LpCard-BhI1N_vL.js → LpCard-CpRByR0q.js} +1 -1
  20. package/dist/chunks/{LpCheckbox-CVccKYaH.js → LpCheckbox-DpqchzH4.js} +7 -3
  21. package/dist/chunks/{LpCodeBlock-Bo0B4TFR.js → LpCodeBlock-DeHJPq7p.js} +48 -16
  22. package/dist/chunks/{LpColorPicker-BlXea7LM.js → LpColorPicker-Bhf1qxhB.js} +32 -9
  23. package/dist/chunks/{LpConfirmDialog-Q-bxff8z.js → LpConfirmDialog-CIUOSnWo.js} +58 -16
  24. package/dist/chunks/LpConfirmHost-CDm0Wtpv.js +49 -0
  25. package/dist/chunks/LpContextMenu-DQw5dGfG.js +68 -0
  26. package/dist/chunks/LpDatePicker-Drirx1AK.js +191 -0
  27. package/dist/chunks/{LpDrawer-BZ6k3hvo.js → LpDrawer-bScx-KMO.js} +63 -26
  28. package/dist/chunks/{LpFileTree-_5vFBgaK.js → LpFileTree-CSI_5SK2.js} +23 -8
  29. package/dist/chunks/{LpFileTreeNode-BdcIYKIE.js → LpFileTreeNode-B4MiSQX2.js} +2 -2
  30. package/dist/chunks/LpFormField-CIk5qosh.js +87 -0
  31. package/dist/chunks/{LpInput-9EXWUM9B.js → LpInput-C0e8y8_d.js} +26 -11
  32. package/dist/chunks/{LpLightbox-2sYVMY-S.js → LpLightbox-Mr0W_6HK.js} +147 -60
  33. package/dist/chunks/{LpLink-DCND-zyu.js → LpLink-Krkjhe_y.js} +15 -6
  34. package/dist/chunks/{LpLogViewer-CaRy5MeS.js → LpLogViewer-YBOaFnc_.js} +68 -34
  35. package/dist/chunks/{LpMapPicker-ul4oYno6.js → LpMapPicker-NUzS9e37.js} +38 -13
  36. package/dist/chunks/{LpMediaGallery-FF-NquIo.js → LpMediaGallery-DfJmMW0I.js} +6 -3
  37. package/dist/chunks/{LpModal-Bt0pQCI_.js → LpModal-DPwGKBPv.js} +49 -26
  38. package/dist/chunks/{LpModalNav-YaifHrXC.js → LpModalNav-DPg5H9f3.js} +9 -6
  39. package/dist/chunks/{LpNotificationBell-DyGGsMAM.js → LpNotificationBell-t5yn-siX.js} +58 -24
  40. package/dist/chunks/{LpNumberField-CWuX08iX.js → LpNumberField-DWnQKktS.js} +20 -7
  41. package/dist/chunks/{LpNumberFlow-amWYaeuA.js → LpNumberFlow-z4WPZcZu.js} +22 -12
  42. package/dist/chunks/{LpOtpInput-y2v-pmiN.js → LpOtpInput-CziPF3Oq.js} +37 -8
  43. package/dist/chunks/{LpPagination-5FxjmmB5.js → LpPagination-DFngX7Xo.js} +30 -19
  44. package/dist/chunks/{LpPasswordInput--sjYCkLS.js → LpPasswordInput-CWVDuBJ5.js} +10 -7
  45. package/dist/chunks/{LpPhoneInput-BDSKVl_z.js → LpPhoneInput-CD-ZTeSd.js} +57 -33
  46. package/dist/chunks/{LpPie-D6OnzwlJ.js → LpPie-BLco31Nx.js} +78 -21
  47. package/dist/chunks/{LpPopover-8268UmlC.js → LpPopover-p_e_shN3.js} +4 -2
  48. package/dist/chunks/{LpProgress-Btq5W4Ut.js → LpProgress-CBU1iI69.js} +5 -4
  49. package/dist/chunks/{LpRadioGroup-xPIuVDO_.js → LpRadioGroup-DmEW0l78.js} +9 -2
  50. package/dist/chunks/{LpSegmented-fV0Sk92p.js → LpSegmented-Dejd7cIx.js} +7 -3
  51. package/dist/chunks/{LpSelect-ANIVtrpM.js → LpSelect-DrRD9I7L.js} +40 -18
  52. package/dist/chunks/{LpSidebar-ClBUwoEz.js → LpSidebar-BgwUNWpJ.js} +30 -22
  53. package/dist/chunks/{LpSidebarNav-llOcs_Fn.js → LpSidebarNav-Bqxz8aWT.js} +14 -9
  54. package/dist/chunks/{LpSkeleton-CTI-vBgW.js → LpSkeleton-BB6XIV-w.js} +1 -1
  55. package/dist/chunks/{LpSlider-BwNIBkAK.js → LpSlider-Cuzmrd7p.js} +7 -2
  56. package/dist/chunks/{LpSparkline-C6xQ2dwv.js → LpSparkline-CB80N8SJ.js} +11 -4
  57. package/dist/chunks/{LpStat-CQaW8R--.js → LpStat-dXtyrdX9.js} +1 -1
  58. package/dist/chunks/{LpSwitch-Blizvqyt.js → LpSwitch-B1aLKulj.js} +40 -26
  59. package/dist/chunks/{LpTableOfContents-CjvxPMpU.js → LpTableOfContents-BUcAfGPQ.js} +36 -41
  60. package/dist/chunks/{LpTabs-Cefe_lys.js → LpTabs-_qMVaZJ6.js} +19 -5
  61. package/dist/chunks/LpTextarea-yP8hGedR.js +77 -0
  62. package/dist/chunks/LpThemeProvider-Wjm7Tuit.js +35 -0
  63. package/dist/chunks/LpThemeSwitcher-CKLc10KZ.js +169 -0
  64. package/dist/chunks/{LpTilt-DuyKbpkA.js → LpTilt-B7KHX1zH.js} +2 -2
  65. package/dist/chunks/{LpTimeField-K6nAGboX.js → LpTimeField-C5HRgS5H.js} +13 -7
  66. package/dist/chunks/{LpToaster-gDB1EaU3.js → LpToaster--oT6lfhL.js} +57 -28
  67. package/dist/chunks/{LpTooltip-hCLPAqMG.js → LpTooltip-CBdzeKNf.js} +13 -6
  68. package/dist/chunks/LpTooltipProvider-CftjV7Xc.js +25 -0
  69. package/dist/chunks/{LpTopologyCanvas-Cm1L39uL.js → LpTopologyCanvas-DocsLal3.js} +6 -1
  70. package/dist/chunks/{LpUptimeBar-DvAMno0S.js → LpUptimeBar-DLtxOMRy.js} +43 -14
  71. package/dist/chunks/{LpThemeSwitcher-Cue-myST.js → useTheme-C8JHUlPM.js} +173 -215
  72. package/dist/component-names.d.ts +1 -1
  73. package/dist/components/ContextMenuItems.vue.d.ts +8 -0
  74. package/dist/components/ContextMenuItems.vue.js +4 -0
  75. package/dist/components/LayoutCanvas.vue.js +1 -1
  76. package/dist/components/LayoutNode.vue.d.ts +4 -0
  77. package/dist/components/LayoutNode.vue.js +1 -1
  78. package/dist/components/LpActionBar.vue.d.ts +3 -0
  79. package/dist/components/LpActionBar.vue.js +1 -1
  80. package/dist/components/LpAddressInput.vue.d.ts +1 -1
  81. package/dist/components/LpAddressInput.vue.js +1 -1
  82. package/dist/components/LpAlert.vue.d.ts +9 -0
  83. package/dist/components/LpAlert.vue.js +1 -1
  84. package/dist/components/LpAppShell.vue.d.ts +10 -0
  85. package/dist/components/LpAppShell.vue.js +1 -1
  86. package/dist/components/LpAutocomplete.vue.d.ts +31 -6
  87. package/dist/components/LpAutocomplete.vue.js +1 -1
  88. package/dist/components/LpAvatar.vue.d.ts +3 -3
  89. package/dist/components/LpAvatar.vue.js +1 -1
  90. package/dist/components/LpBadge.vue.d.ts +2 -2
  91. package/dist/components/LpBreadcrumbs.vue.d.ts +13 -9
  92. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  93. package/dist/components/LpButton.vue.d.ts +35 -98
  94. package/dist/components/LpButton.vue.js +1 -1
  95. package/dist/components/LpCard.vue.js +1 -1
  96. package/dist/components/LpChart.vue.d.ts +31 -2
  97. package/dist/components/LpChart.vue.js +317 -95
  98. package/dist/components/LpCheckbox.vue.js +1 -1
  99. package/dist/components/LpCodeBlock.vue.d.ts +2 -0
  100. package/dist/components/LpCodeBlock.vue.js +1 -1
  101. package/dist/components/LpColorPicker.vue.js +1 -1
  102. package/dist/components/LpCommandPalette.vue.d.ts +8 -10
  103. package/dist/components/LpCommandPalette.vue.js +88 -78
  104. package/dist/components/LpConfirmDialog.vue.d.ts +15 -3
  105. package/dist/components/LpConfirmDialog.vue.js +1 -1
  106. package/dist/components/LpConfirmHost.vue.d.ts +9 -0
  107. package/dist/components/LpConfirmHost.vue.js +4 -0
  108. package/dist/components/LpContextMenu.vue.d.ts +3 -5
  109. package/dist/components/LpContextMenu.vue.js +1 -1
  110. package/dist/components/LpDatePicker.vue.d.ts +24 -6
  111. package/dist/components/LpDatePicker.vue.js +1 -1
  112. package/dist/components/LpDrawer.vue.d.ts +10 -0
  113. package/dist/components/LpDrawer.vue.js +1 -1
  114. package/dist/components/LpDropzone.vue.js +22 -6
  115. package/dist/components/LpFileTree.vue.d.ts +11 -2
  116. package/dist/components/LpFileTree.vue.js +1 -1
  117. package/dist/components/LpFileTreeNode.vue.js +1 -1
  118. package/dist/components/LpFormField.vue.d.ts +9 -0
  119. package/dist/components/LpFormField.vue.js +1 -1
  120. package/dist/components/LpImageEditor.vue.js +92 -57
  121. package/dist/components/LpInput.vue.d.ts +3 -3
  122. package/dist/components/LpInput.vue.js +1 -1
  123. package/dist/components/LpLightbox.vue.d.ts +17 -0
  124. package/dist/components/LpLightbox.vue.js +1 -1
  125. package/dist/components/LpLink.vue.d.ts +11 -11
  126. package/dist/components/LpLink.vue.js +1 -1
  127. package/dist/components/LpLogViewer.vue.d.ts +20 -2
  128. package/dist/components/LpLogViewer.vue.js +1 -1
  129. package/dist/components/LpMapPicker.vue.d.ts +4 -4
  130. package/dist/components/LpMapPicker.vue.js +1 -1
  131. package/dist/components/LpMediaGallery.vue.js +1 -1
  132. package/dist/components/LpModal.vue.d.ts +16 -6
  133. package/dist/components/LpModal.vue.js +1 -1
  134. package/dist/components/LpModalNav.vue.d.ts +0 -1
  135. package/dist/components/LpModalNav.vue.js +1 -1
  136. package/dist/components/LpNotificationBell.vue.d.ts +11 -2
  137. package/dist/components/LpNotificationBell.vue.js +1 -1
  138. package/dist/components/LpNumberField.vue.d.ts +2 -0
  139. package/dist/components/LpNumberField.vue.js +1 -1
  140. package/dist/components/LpNumberFlow.vue.js +1 -1
  141. package/dist/components/LpOtpInput.vue.js +1 -1
  142. package/dist/components/LpPagination.vue.d.ts +2 -0
  143. package/dist/components/LpPagination.vue.js +1 -1
  144. package/dist/components/LpPasswordInput.vue.js +1 -1
  145. package/dist/components/LpPhoneInput.vue.js +1 -1
  146. package/dist/components/LpPie.vue.d.ts +21 -2
  147. package/dist/components/LpPie.vue.js +1 -1
  148. package/dist/components/LpPopover.vue.d.ts +2 -0
  149. package/dist/components/LpPopover.vue.js +1 -1
  150. package/dist/components/LpProgress.vue.js +1 -1
  151. package/dist/components/LpRadioGroup.vue.js +1 -1
  152. package/dist/components/LpSection.vue.d.ts +1 -1
  153. package/dist/components/LpSegmented.vue.d.ts +5 -0
  154. package/dist/components/LpSegmented.vue.js +1 -1
  155. package/dist/components/LpSelect.vue.d.ts +1 -0
  156. package/dist/components/LpSelect.vue.js +1 -1
  157. package/dist/components/LpShortcutsDialog.vue.d.ts +1 -1
  158. package/dist/components/LpShortcutsDialog.vue.js +11 -8
  159. package/dist/components/LpSidebar.vue.d.ts +6 -2
  160. package/dist/components/LpSidebar.vue.js +1 -1
  161. package/dist/components/LpSidebarNav.vue.js +1 -1
  162. package/dist/components/LpSkeleton.vue.js +1 -1
  163. package/dist/components/LpSlider.vue.js +1 -1
  164. package/dist/components/LpSparkline.vue.d.ts +1 -0
  165. package/dist/components/LpSparkline.vue.js +1 -1
  166. package/dist/components/LpStat.vue.js +1 -1
  167. package/dist/components/LpSwitch.vue.js +1 -1
  168. package/dist/components/LpTable.vue.d.ts +23 -1
  169. package/dist/components/LpTable.vue.js +74 -36
  170. package/dist/components/LpTableOfContents.vue.d.ts +4 -2
  171. package/dist/components/LpTableOfContents.vue.js +1 -1
  172. package/dist/components/LpTabs.vue.d.ts +2 -0
  173. package/dist/components/LpTabs.vue.js +1 -1
  174. package/dist/components/LpTextarea.vue.d.ts +19 -6
  175. package/dist/components/LpTextarea.vue.js +1 -1
  176. package/dist/components/LpThemeProvider.vue.d.ts +22 -0
  177. package/dist/components/LpThemeProvider.vue.js +4 -0
  178. package/dist/components/LpThemeSwitcher.vue.d.ts +13 -0
  179. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  180. package/dist/components/LpTilt.vue.js +1 -1
  181. package/dist/components/LpTimeField.vue.js +1 -1
  182. package/dist/components/LpToaster.vue.d.ts +6 -1
  183. package/dist/components/LpToaster.vue.js +1 -1
  184. package/dist/components/LpTooltip.vue.d.ts +1 -2
  185. package/dist/components/LpTooltip.vue.js +1 -1
  186. package/dist/components/LpTooltipProvider.vue.d.ts +22 -0
  187. package/dist/components/LpTooltipProvider.vue.js +4 -0
  188. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  189. package/dist/components/LpUptimeBar.vue.d.ts +4 -0
  190. package/dist/components/LpUptimeBar.vue.js +1 -1
  191. package/dist/components/chart.d.ts +32 -1
  192. package/dist/components/chart.js +39 -4
  193. package/dist/components/codeHighlight.d.ts +6 -0
  194. package/dist/components/codeHighlight.js +92 -20
  195. package/dist/components/color.d.ts +1 -1
  196. package/dist/components/color.js +20 -9
  197. package/dist/components/commandSearch.d.ts +0 -1
  198. package/dist/components/commandSearch.js +1 -17
  199. package/dist/components/confirm.d.ts +25 -0
  200. package/dist/components/confirm.js +26 -0
  201. package/dist/components/countries.d.ts +6 -0
  202. package/dist/components/countries.js +7 -0
  203. package/dist/components/fileTree.js +16 -7
  204. package/dist/components/imageCrop.d.ts +42 -0
  205. package/dist/components/imageCrop.js +47 -0
  206. package/dist/components/link.d.ts +12 -0
  207. package/dist/components/link.js +19 -0
  208. package/dist/components/modalDepth.d.ts +18 -2
  209. package/dist/components/modalDepth.js +17 -5
  210. package/dist/components/otp.d.ts +5 -0
  211. package/dist/components/otp.js +10 -0
  212. package/dist/components/sidebar.d.ts +7 -1
  213. package/dist/components/tableSort.d.ts +8 -0
  214. package/dist/components/tableSort.js +31 -0
  215. package/dist/composables/useField.d.ts +34 -0
  216. package/dist/composables/useField.js +45 -0
  217. package/dist/composables/useFormDraft.d.ts +10 -3
  218. package/dist/composables/useFormDraft.js +9 -14
  219. package/dist/composables/useHotkeys.d.ts +17 -1
  220. package/dist/composables/useHotkeys.js +55 -18
  221. package/dist/composables/useInputFilter.d.ts +17 -0
  222. package/dist/composables/useInputFilter.js +47 -10
  223. package/dist/composables/useLpConfig.d.ts +24 -0
  224. package/dist/composables/useLpConfig.js +44 -0
  225. package/dist/composables/useRovingFocus.js +7 -0
  226. package/dist/composables/useTilt.d.ts +2 -2
  227. package/dist/composables/useTilt.js +5 -6
  228. package/dist/composables/useToast.d.ts +20 -0
  229. package/dist/composables/useToast.js +28 -4
  230. package/dist/composables/useZoomPan.d.ts +3 -3
  231. package/dist/composables/useZoomPan.js +4 -4
  232. package/dist/index.d.ts +33 -3
  233. package/dist/index.js +185 -139
  234. package/dist/layout/tree.d.ts +2 -0
  235. package/dist/layout/tree.js +16 -1
  236. package/dist/layout/useLayout.d.ts +15 -0
  237. package/dist/layout/useLayout.js +41 -14
  238. package/dist/theme/useTheme.d.ts +32 -9
  239. package/package.json +8 -7
  240. package/src/component-names.ts +5 -7
  241. package/src/components/ContextMenuItems.vue +88 -0
  242. package/src/components/LayoutCanvas.vue +26 -6
  243. package/src/components/LayoutNode.vue +19 -2
  244. package/src/components/LpActionBar.vue +30 -7
  245. package/src/components/LpAddressInput.vue +57 -41
  246. package/src/components/LpAlert.vue +16 -3
  247. package/src/components/LpAppShell.vue +45 -12
  248. package/src/components/LpAutocomplete.vue +46 -10
  249. package/src/components/LpAvatar.vue +19 -5
  250. package/src/components/LpBreadcrumbs.vue +65 -41
  251. package/src/components/LpButton.vue +59 -13
  252. package/src/components/LpChart.vue +272 -28
  253. package/src/components/LpCheckbox.vue +7 -3
  254. package/src/components/LpCodeBlock.vue +50 -14
  255. package/src/components/LpColorPicker.vue +39 -9
  256. package/src/components/LpCommandPalette.vue +36 -25
  257. package/src/components/LpConfirmDialog.vue +68 -17
  258. package/src/components/LpConfirmHost.vue +59 -0
  259. package/src/components/LpContextMenu.vue +14 -115
  260. package/src/components/LpDatePicker.vue +99 -68
  261. package/src/components/LpDrawer.vue +56 -19
  262. package/src/components/LpDropzone.vue +21 -4
  263. package/src/components/LpFileTree.vue +33 -7
  264. package/src/components/LpFormField.vue +30 -7
  265. package/src/components/LpImageEditor.vue +96 -57
  266. package/src/components/LpInput.vue +18 -7
  267. package/src/components/LpLightbox.vue +140 -29
  268. package/src/components/LpLink.vue +24 -13
  269. package/src/components/LpLogViewer.vue +86 -17
  270. package/src/components/LpMapPicker.vue +36 -13
  271. package/src/components/LpMediaGallery.vue +8 -2
  272. package/src/components/LpModal.vue +53 -27
  273. package/src/components/LpModalNav.vue +5 -2
  274. package/src/components/LpNotificationBell.vue +63 -17
  275. package/src/components/LpNumberField.vue +21 -6
  276. package/src/components/LpNumberFlow.vue +26 -10
  277. package/src/components/LpOtpInput.vue +43 -8
  278. package/src/components/LpPagination.vue +23 -7
  279. package/src/components/LpPasswordInput.vue +4 -1
  280. package/src/components/LpPhoneInput.vue +53 -27
  281. package/src/components/LpPie.vue +94 -15
  282. package/src/components/LpPopover.vue +3 -0
  283. package/src/components/LpProgress.vue +5 -3
  284. package/src/components/LpRadioGroup.vue +9 -2
  285. package/src/components/LpSegmented.vue +12 -2
  286. package/src/components/LpSelect.vue +0 -0
  287. package/src/components/LpShortcutsDialog.vue +8 -3
  288. package/src/components/LpSidebar.vue +14 -9
  289. package/src/components/LpSidebarNav.vue +17 -5
  290. package/src/components/LpSkeleton.vue +4 -1
  291. package/src/components/LpSlider.vue +7 -1
  292. package/src/components/LpSparkline.vue +11 -3
  293. package/src/components/LpSwitch.vue +30 -7
  294. package/src/components/LpTable.vue +91 -32
  295. package/src/components/LpTableOfContents.vue +50 -32
  296. package/src/components/LpTabs.vue +23 -6
  297. package/src/components/LpTextarea.vue +44 -6
  298. package/src/components/LpThemeProvider.vue +41 -0
  299. package/src/components/LpThemeSwitcher.vue +64 -17
  300. package/src/components/LpTilt.vue +2 -2
  301. package/src/components/LpTimeField.vue +14 -3
  302. package/src/components/LpToaster.vue +66 -26
  303. package/src/components/LpTooltip.vue +22 -5
  304. package/src/components/LpTooltipProvider.vue +24 -0
  305. package/src/components/LpTopologyCanvas.vue +12 -1
  306. package/src/components/LpUptimeBar.vue +46 -8
  307. package/src/components/chart.ts +77 -5
  308. package/src/components/codeHighlight.ts +111 -15
  309. package/src/components/color.ts +30 -9
  310. package/src/components/commandSearch.ts +0 -17
  311. package/src/components/confirm.ts +58 -0
  312. package/src/components/countries.ts +12 -0
  313. package/src/components/fileTree.ts +19 -7
  314. package/src/components/imageCrop.ts +107 -0
  315. package/src/components/link.ts +34 -0
  316. package/src/components/modalDepth.ts +48 -6
  317. package/src/components/otp.ts +16 -0
  318. package/src/components/sidebar.ts +7 -1
  319. package/src/components/tableSort.ts +37 -0
  320. package/src/composables/useField.ts +79 -0
  321. package/src/composables/useFormDraft.ts +25 -20
  322. package/src/composables/useHotkeys.ts +89 -18
  323. package/src/composables/useInputFilter.ts +77 -14
  324. package/src/composables/useLpConfig.ts +87 -0
  325. package/src/composables/useRovingFocus.ts +11 -0
  326. package/src/composables/useTilt.ts +5 -6
  327. package/src/composables/useToast.ts +56 -4
  328. package/src/composables/useZoomPan.ts +6 -6
  329. package/src/index.ts +38 -3
  330. package/src/layout/tree.ts +30 -0
  331. package/src/layout/useLayout.ts +68 -18
  332. package/src/nuxt.ts +59 -17
  333. package/src/runtime/config.plugin.ts +14 -0
  334. package/src/runtime/theme.plugin.ts +42 -22
  335. package/src/theme/useTheme.ts +206 -100
  336. package/dist/chunks/LpBreadcrumbs-p4jHe_V-.js +0 -62
  337. package/dist/chunks/LpContextMenu-CwW1QXe_.js +0 -217
  338. package/dist/chunks/LpDatePicker-CwOToxGq.js +0 -164
  339. package/dist/chunks/LpFormField-CazBc2-a.js +0 -62
  340. package/dist/chunks/LpTextarea-t7F6gv8E.js +0 -47
@@ -16,18 +16,41 @@ export default { inheritAttrs: false }
16
16
  * eased box, so the thing being typed into never moves.
17
17
  */
18
18
  import { Label } from "reka-ui"
19
- import { useId } from "vue"
19
+ import { computed, useId, useSlots } from "vue"
20
20
  import LpShift from "./LpShift.vue"
21
+ import { fieldMessageId, provideField } from "../composables/useField"
21
22
  import { useMergedAttrs } from "../composables/useMergedClass"
22
23
 
23
- defineProps<{
24
+ const props = defineProps<{
24
25
  label?: string
25
26
  hint?: string
26
27
  error?: string
27
28
  required?: boolean
29
+ /** Id of the control; generated when unset. The label points at it. */
30
+ id?: string
31
+ invalid?: boolean
32
+ disabled?: boolean
28
33
  }>()
29
34
 
30
- const id = useId()
35
+ const generated = useId()
36
+ const id = computed(() => props.id ?? generated)
37
+ const messageId = computed(() => fieldMessageId(id.value))
38
+ const slots = useSlots()
39
+ const hasMessage = computed(() => !!props.error || !!props.hint || !!slots.hint)
40
+
41
+ // The control inside reads its id, description and states from here, so the
42
+ // label, the message line and aria-invalid stay tied to it without the
43
+ // consumer threading ids by hand.
44
+ provideField(
45
+ computed(() => ({
46
+ id: id.value,
47
+ labelId: props.label ? `${id.value}-label` : undefined,
48
+ describedBy: hasMessage.value ? messageId.value : undefined,
49
+ invalid: !!props.invalid || !!props.error,
50
+ required: !!props.required,
51
+ disabled: !!props.disabled,
52
+ })),
53
+ )
31
54
 
32
55
  // The field stacks label and control with a fixed gap; a denser form needs that gap smaller.
33
56
  const { class: rootClass, attrs: rest } = useMergedAttrs(
@@ -44,12 +67,12 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
44
67
  <div
45
68
  :class="rootClass"
46
69
  v-bind="rest">
47
- <Label v-if="label" :for="id" class="text-sm font-medium text-ink">
70
+ <Label v-if="label" :id="`${id}-label`" :for="id" class="text-sm font-medium text-ink">
48
71
  {{ label }}
49
72
  <span v-if="required" class="text-danger">*</span>
50
73
  </Label>
51
74
 
52
- <slot :id="id" />
75
+ <slot :id="id" :describedBy="hasMessage ? messageId : undefined" />
53
76
 
54
77
  <!-- Swapping hint→error changes the text but not the height, so the eased
55
78
  box only ever moves when a message appears or clears.
@@ -60,12 +83,12 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
60
83
  leave every message-less field a row taller than before — and would be
61
84
  a step the easing could not cover, since it exists even at height 0. -->
62
85
  <LpShift axis="height">
63
- <p v-if="error" class="pt-1.5 text-xs text-danger">{{ error }}</p>
86
+ <p v-if="error" :id="messageId" class="pt-1.5 text-xs text-danger">{{ error }}</p>
64
87
  <!-- The hint is a slot as well as a prop: most are a sentence, but some
65
88
  carry a link or a unit in <code>, and a prop can only be text. Callers
66
89
  had been reaching for `#hint` on the assumption it existed — it
67
90
  rendered nothing, so the explanation silently went missing. -->
68
- <p v-else-if="hint || $slots.hint" class="pt-1.5 text-xs text-muted">
91
+ <p v-else-if="hint || $slots.hint" :id="messageId" class="pt-1.5 text-xs text-muted">
69
92
  <slot name="hint">{{ hint }}</slot>
70
93
  </p>
71
94
  </LpShift>
@@ -24,6 +24,8 @@ export default { inheritAttrs: false }
24
24
  * already know from every avatar cropper.
25
25
  */
26
26
  import { computed, onBeforeUnmount, ref, shallowRef, watch } from "vue"
27
+ import { useLabels } from "../composables/useLpConfig"
28
+ import { coverScale, cropGeometry, isSideways } from "./imageCrop"
27
29
  import { useHotkeys } from "../composables/useHotkeys"
28
30
  import { useReveal } from "../composables/useReveal"
29
31
  import { useZoomPan } from "../composables/useZoomPan"
@@ -81,15 +83,18 @@ const props = withDefaults(
81
83
  // mount. An editor inside a dialog that opens a minute later still draws.
82
84
  const { el: revealAnchor, revealed } = useReveal({ immediate: () => !props.animate })
83
85
 
84
- const text = computed(() => ({
85
- rotateLeft: "Rotate left",
86
- rotateRight: "Rotate right",
87
- zoomIn: "Zoom in",
88
- zoomOut: "Zoom out",
89
- reset: "Reset",
90
- canvas: "Crop area — arrows pan, +/− zoom, R rotates, Alt+wheel turns freely",
91
- ...props.labels,
92
- }))
86
+ const text = useLabels(
87
+ "imageEditor",
88
+ {
89
+ rotateLeft: "Rotate left",
90
+ rotateRight: "Rotate right",
91
+ zoomIn: "Zoom in",
92
+ zoomOut: "Zoom out",
93
+ reset: "Reset",
94
+ canvas: "Crop area — arrows pan, +/− zoom, R rotates, Alt+wheel turns freely",
95
+ },
96
+ () => props.labels,
97
+ )
93
98
 
94
99
  const ASPECTS: Record<string, number | null> = {
95
100
  free: null,
@@ -105,7 +110,24 @@ const rotation = ref(0)
105
110
  const viewport = ref<HTMLElement | null>(null)
106
111
  const zoom = useZoomPan({ min: 1, max: 6 })
107
112
 
108
- watch(viewport, (el) => zoom.setViewport(el))
113
+ const frameSize = ref<{ width: number; height: number } | null>(null)
114
+ let frameObserver: ResizeObserver | null = null
115
+
116
+ watch(viewport, (el) => {
117
+ zoom.setViewport(el)
118
+ frameObserver?.disconnect()
119
+ frameObserver = null
120
+ if (!el) return
121
+ // Fractional, not clientWidth: a rounded-down frame left a hairline of
122
+ // background showing along the edges of a turned picture.
123
+ frameSize.value = { width: el.clientWidth, height: el.clientHeight }
124
+ if (typeof ResizeObserver !== "undefined") {
125
+ frameObserver = new ResizeObserver(([entry]) => {
126
+ frameSize.value = { width: entry.contentRect.width, height: entry.contentRect.height }
127
+ })
128
+ frameObserver.observe(el)
129
+ }
130
+ })
109
131
 
110
132
  /** Which way up it ends, 0-270, whatever the accumulated angle. */
111
133
  const upright = computed(() => ((rotation.value % 360) + 360) % 360)
@@ -114,14 +136,24 @@ const upright = computed(() => ((rotation.value % 360) + 360) % 360)
114
136
  * back to where it started is not an edit, however far the dial travelled. */
115
137
  const edited = computed(() => upright.value !== 0 || zoom.scale.value !== 1)
116
138
 
139
+ // Each file gets a generation: a slow decode of an earlier file must neither
140
+ // replace the current picture nor leak the bitmap it produced.
141
+ let generation = 0
142
+
117
143
  watch(
118
144
  () => props.file,
119
145
  async (file) => {
120
146
  revoke()
121
147
  rotation.value = 0
122
148
  zoom.reset()
149
+ const mine = ++generation
123
150
  objectUrl.value = URL.createObjectURL(file)
124
- source.value = await decode(file, objectUrl.value)
151
+ const decoded = await decode(file, objectUrl.value)
152
+ if (mine !== generation) {
153
+ if (decoded instanceof ImageBitmap) decoded.close()
154
+ return
155
+ }
156
+ source.value = decoded
125
157
  },
126
158
  { immediate: true },
127
159
  )
@@ -172,7 +204,11 @@ function revoke() {
172
204
  source.value = null
173
205
  }
174
206
 
175
- onBeforeUnmount(revoke)
207
+ onBeforeUnmount(() => {
208
+ generation++
209
+ revoke()
210
+ frameObserver?.disconnect()
211
+ })
176
212
 
177
213
  function rotate(by: number) {
178
214
  // Accumulated, never wrapped to 0-359. Normalising made the fourth quarter
@@ -218,7 +254,9 @@ const boxRatio = computed(() => {
218
254
  if (ratio) return ratio
219
255
  const size = sourceSize.value
220
256
  if (!size) return 1
221
- const turned = upright.value % 180 !== 0
257
+ // Snapped to the nearest quarter: a few degrees of levelling must not flip a
258
+ // landscape frame to portrait.
259
+ const turned = isSideways(rotation.value)
222
260
  const w = turned ? size.height : size.width
223
261
  const h = turned ? size.width : size.height
224
262
  return w / h
@@ -226,7 +264,31 @@ const boxRatio = computed(() => {
226
264
 
227
265
  const frameStyle = computed(() => ({ aspectRatio: String(boxRatio.value) }))
228
266
 
267
+ /*
268
+ * The picture is sized to cover the frame AFTER its turn, and centred. Filling
269
+ * the frame with object-cover and then rotating the element left empty bands
270
+ * on any non-square frame turned sideways, and at a free angle bare corners.
271
+ */
272
+ const sized = computed(() => {
273
+ const size = sourceSize.value
274
+ const frame = frameSize.value
275
+ if (!size || !frame || !frame.width || !frame.height) return null
276
+ const cover = coverScale(size.width, size.height, frame.width, frame.height, rotation.value)
277
+ const width = size.width * cover
278
+ const height = size.height * cover
279
+ return {
280
+ width: `${width}px`,
281
+ height: `${height}px`,
282
+ left: "50%",
283
+ top: "50%",
284
+ marginLeft: `${-width / 2}px`,
285
+ marginTop: `${-height / 2}px`,
286
+ maxWidth: "none",
287
+ }
288
+ })
289
+
229
290
  const imageStyle = computed(() => ({
291
+ ...sized.value,
230
292
  ...zoom.style.value,
231
293
  // Rotation sits innermost, after the composable's scale and translate, so a
232
294
  // drag still moves the picture the way the pointer went once it is turned.
@@ -254,51 +316,31 @@ async function exportImage(): Promise<Blob | null> {
254
316
  if (!picture || !size || !frame) return null
255
317
  if (!edited.value && props.aspect === "free") return null
256
318
 
257
- // Work in "upright space": the picture as the person sees it, already
258
- // turned. The rotation is re-applied once, when drawing, which keeps the
259
- // maths free of quarter-turn special cases.
260
- const turned = upright.value % 180 !== 0
261
- const uprightW = turned ? size.height : size.width
262
- const uprightH = turned ? size.width : size.height
263
-
264
- // object-cover: the picture fills the frame and the overflow is what panning
265
- // reaches. One frame pixel is this many upright pixels.
266
- const cover = Math.max(frame.clientWidth / uprightW, frame.clientHeight / uprightH)
267
- const shown = cover * zoom.scale.value
268
- const cropW = Math.min(uprightW, frame.clientWidth / shown)
269
- const cropH = Math.min(uprightH, frame.clientHeight / shown)
270
-
271
- // `offset` is in content pixels — the same space the cover scale maps from —
272
- // so it converts with `cover`, not with the zoomed scale.
273
- const centreX = clamp(
274
- uprightW / 2 - zoom.offset.value.x / cover,
275
- cropW / 2,
276
- uprightW - cropW / 2,
277
- )
278
- const centreY = clamp(
279
- uprightH / 2 - zoom.offset.value.y / cover,
280
- cropH / 2,
281
- uprightH - cropH / 2,
282
- )
283
-
284
- const scale = Math.min(1, props.maxEdge / Math.max(cropW, cropH))
319
+ // The crop in upright space, from the same geometry the preview is drawn
320
+ // with, then the whole source drawn through one transform: no per-quarter
321
+ // special cases, and a free angle comes out as it looked on screen.
322
+ const crop = cropGeometry({
323
+ sourceWidth: size.width,
324
+ sourceHeight: size.height,
325
+ frameWidth: frameSize.value?.width ?? frame.clientWidth,
326
+ frameHeight: frameSize.value?.height ?? frame.clientHeight,
327
+ rotation: rotation.value,
328
+ scale: zoom.scale.value,
329
+ offset: zoom.offset.value,
330
+ })
331
+
332
+ const scale = Math.min(1, props.maxEdge / Math.max(crop.width, crop.height))
285
333
  const canvas = document.createElement("canvas")
286
- canvas.width = Math.max(1, Math.round(cropW * scale))
287
- canvas.height = Math.max(1, Math.round(cropH * scale))
334
+ canvas.width = Math.max(1, Math.round(crop.width * scale))
335
+ canvas.height = Math.max(1, Math.round(crop.height * scale))
288
336
  const ctx = canvas.getContext("2d")
289
337
  if (!ctx) return null
290
338
 
291
339
  ctx.translate(canvas.width / 2, canvas.height / 2)
292
- ctx.rotate((upright.value * Math.PI) / 180)
293
340
  ctx.scale(scale, scale)
294
-
295
- // Back to source coordinates for the read: a quarter turn swaps the axes.
296
- const readW = turned ? cropH : cropW
297
- const readH = turned ? cropW : cropH
298
- const readX = (turned ? centreY : centreX) - readW / 2
299
- const readY = (turned ? centreX : centreY) - readH / 2
300
-
301
- ctx.drawImage(picture, readX, readY, readW, readH, -readW / 2, -readH / 2, readW, readH)
341
+ ctx.translate(-crop.centre.x, -crop.centre.y)
342
+ ctx.rotate((rotation.value * Math.PI) / 180)
343
+ ctx.drawImage(picture, -size.width / 2, -size.height / 2)
302
344
 
303
345
  return await new Promise<Blob | null>((resolve) =>
304
346
  canvas.toBlob(resolve, outputType.value, props.quality),
@@ -312,10 +354,6 @@ const outputType = computed(() => {
312
354
  return ENCODABLE.has(props.file.type) ? props.file.type : "image/png"
313
355
  })
314
356
 
315
- function clamp(value: number, low: number, high: number) {
316
- return Math.min(Math.max(value, low), Math.max(low, high))
317
- }
318
-
319
357
  /*
320
358
  * The wheel.
321
359
  *
@@ -425,8 +463,9 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
425
463
  :src="objectUrl"
426
464
  alt=""
427
465
  draggable="false"
428
- class="lp-editor__image absolute inset-0 size-full select-none object-cover motion-reduce:transition-none"
466
+ class="lp-editor__image absolute select-none motion-reduce:transition-none"
429
467
  :class="[
468
+ sized ? '' : 'inset-0 size-full object-cover',
430
469
  zoom.panning.value ? 'cursor-grabbing' : 'cursor-grab',
431
470
  animate && !revealed ? 'lp-editor__image--waiting' : '',
432
471
  ]"
@@ -5,6 +5,7 @@ export default { inheritAttrs: false }
5
5
  <script setup lang="ts">
6
6
  import { tv, type VariantProps } from "tailwind-variants"
7
7
  import { computed, ref, useAttrs, useSlots } from "vue"
8
+ import { useFieldControl } from "../composables/useField"
8
9
  import { useInputFilter } from "../composables/useInputFilter"
9
10
  import { mergeClass } from "../composables/useMergedClass"
10
11
  import LpFormField from "./LpFormField.vue"
@@ -78,7 +79,7 @@ const props = withDefaults(
78
79
 
79
80
  defineEmits<{ (e: "update:modelValue", value: string): void }>()
80
81
 
81
- const { isInvalid, onBeforeInput, onPaste } = useInputFilter({
82
+ const { isInvalid, onBeforeInput, onInput: filterInput, onCompositionEnd, onPaste } = useInputFilter({
82
83
  pattern: () => props.pattern,
83
84
  restrict: () => props.restrict,
84
85
  })
@@ -93,9 +94,15 @@ const hasTrailing = computed(() => !!slots.trailing)
93
94
  const hasField = computed(
94
95
  () => !!props.label || !!props.hint || !!props.error,
95
96
  )
96
- const isInvalidState = computed(
97
- () => props.invalid || !!props.error || patternInvalid.value,
98
- )
97
+ const field = useFieldControl(() => ({
98
+ wrapped: hasField.value,
99
+ hasMessage: !!props.error || !!props.hint,
100
+ id: typeof attrs.id === "string" ? attrs.id : undefined,
101
+ invalid: props.invalid || !!props.error || patternInvalid.value,
102
+ required: props.required,
103
+ disabled: props.disabled,
104
+ }))
105
+ const isInvalidState = computed(() => field.value.invalid)
99
106
  const shellClass = computed(() =>
100
107
  shell({ size: props.size, invalid: isInvalidState.value }),
101
108
  )
@@ -126,6 +133,7 @@ defineExpose({
126
133
  :hint="hasField ? hint : undefined"
127
134
  :error="hasField ? error : undefined"
128
135
  :required="hasField ? required : undefined"
136
+ :id="hasField ? field.id : undefined"
129
137
  :class="hasField ? rootClass : undefined"
130
138
  :style="hasField ? rootStyle : undefined"
131
139
  >
@@ -140,12 +148,14 @@ defineExpose({
140
148
  <input
141
149
  ref="inputEl"
142
150
  v-bind="inputAttrs"
151
+ :id="field.id"
143
152
  :type="type"
144
153
  :value="modelValue"
145
154
  :placeholder="placeholder"
146
- :disabled="disabled"
147
- :required="required || undefined"
155
+ :disabled="field.disabled"
156
+ :required="field.required || undefined"
148
157
  :aria-invalid="isInvalidState || undefined"
158
+ :aria-describedby="field.describedBy"
149
159
  :class="[
150
160
  'lp-autofill min-w-0 flex-1 self-stretch bg-transparent outline-none placeholder:text-muted',
151
161
  hasLeading ? 'pl-2' : padX[size ?? 'md'],
@@ -153,7 +163,8 @@ defineExpose({
153
163
  ]"
154
164
  @beforeinput="onBeforeInput"
155
165
  @paste="onPaste"
156
- @input="$emit('update:modelValue', ($event.target as HTMLInputElement).value)"
166
+ @compositionend="onCompositionEnd"
167
+ @input="filterInput($event); $emit('update:modelValue', ($event.target as HTMLInputElement).value)"
157
168
  />
158
169
  <slot name="trailing" />
159
170
  </div>