@leavepulse/ui 0.43.4 → 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 (337) hide show
  1. package/README.md +75 -7
  2. package/dist/assets/{LpCommandPalette-B-0Est4Z.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 -3
  103. package/dist/components/LpCommandPalette.vue.js +86 -72
  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/confirm.d.ts +25 -0
  198. package/dist/components/confirm.js +26 -0
  199. package/dist/components/countries.d.ts +6 -0
  200. package/dist/components/countries.js +7 -0
  201. package/dist/components/fileTree.js +16 -7
  202. package/dist/components/imageCrop.d.ts +42 -0
  203. package/dist/components/imageCrop.js +47 -0
  204. package/dist/components/link.d.ts +12 -0
  205. package/dist/components/link.js +19 -0
  206. package/dist/components/modalDepth.d.ts +18 -2
  207. package/dist/components/modalDepth.js +17 -5
  208. package/dist/components/otp.d.ts +5 -0
  209. package/dist/components/otp.js +10 -0
  210. package/dist/components/sidebar.d.ts +7 -1
  211. package/dist/components/tableSort.d.ts +8 -0
  212. package/dist/components/tableSort.js +31 -0
  213. package/dist/composables/useField.d.ts +34 -0
  214. package/dist/composables/useField.js +45 -0
  215. package/dist/composables/useFormDraft.d.ts +10 -3
  216. package/dist/composables/useFormDraft.js +9 -14
  217. package/dist/composables/useHotkeys.d.ts +17 -1
  218. package/dist/composables/useHotkeys.js +55 -18
  219. package/dist/composables/useInputFilter.d.ts +17 -0
  220. package/dist/composables/useInputFilter.js +47 -10
  221. package/dist/composables/useLpConfig.d.ts +24 -0
  222. package/dist/composables/useLpConfig.js +44 -0
  223. package/dist/composables/useRovingFocus.js +7 -0
  224. package/dist/composables/useTilt.d.ts +2 -2
  225. package/dist/composables/useTilt.js +5 -6
  226. package/dist/composables/useToast.d.ts +20 -0
  227. package/dist/composables/useToast.js +28 -4
  228. package/dist/composables/useZoomPan.d.ts +3 -3
  229. package/dist/composables/useZoomPan.js +4 -4
  230. package/dist/index.d.ts +32 -2
  231. package/dist/index.js +184 -137
  232. package/dist/layout/tree.d.ts +2 -0
  233. package/dist/layout/tree.js +16 -1
  234. package/dist/layout/useLayout.d.ts +15 -0
  235. package/dist/layout/useLayout.js +41 -14
  236. package/dist/theme/useTheme.d.ts +32 -9
  237. package/package.json +8 -7
  238. package/src/component-names.ts +5 -7
  239. package/src/components/ContextMenuItems.vue +88 -0
  240. package/src/components/LayoutCanvas.vue +26 -6
  241. package/src/components/LayoutNode.vue +19 -2
  242. package/src/components/LpActionBar.vue +30 -7
  243. package/src/components/LpAddressInput.vue +57 -41
  244. package/src/components/LpAlert.vue +16 -3
  245. package/src/components/LpAppShell.vue +45 -12
  246. package/src/components/LpAutocomplete.vue +46 -10
  247. package/src/components/LpAvatar.vue +19 -5
  248. package/src/components/LpBreadcrumbs.vue +65 -41
  249. package/src/components/LpButton.vue +59 -13
  250. package/src/components/LpChart.vue +272 -28
  251. package/src/components/LpCheckbox.vue +7 -3
  252. package/src/components/LpCodeBlock.vue +50 -14
  253. package/src/components/LpColorPicker.vue +39 -9
  254. package/src/components/LpCommandPalette.vue +34 -13
  255. package/src/components/LpConfirmDialog.vue +68 -17
  256. package/src/components/LpConfirmHost.vue +59 -0
  257. package/src/components/LpContextMenu.vue +14 -115
  258. package/src/components/LpDatePicker.vue +99 -68
  259. package/src/components/LpDrawer.vue +56 -19
  260. package/src/components/LpDropzone.vue +21 -4
  261. package/src/components/LpFileTree.vue +33 -7
  262. package/src/components/LpFormField.vue +30 -7
  263. package/src/components/LpImageEditor.vue +96 -57
  264. package/src/components/LpInput.vue +18 -7
  265. package/src/components/LpLightbox.vue +140 -29
  266. package/src/components/LpLink.vue +24 -13
  267. package/src/components/LpLogViewer.vue +86 -17
  268. package/src/components/LpMapPicker.vue +36 -13
  269. package/src/components/LpMediaGallery.vue +8 -2
  270. package/src/components/LpModal.vue +53 -27
  271. package/src/components/LpModalNav.vue +5 -2
  272. package/src/components/LpNotificationBell.vue +63 -17
  273. package/src/components/LpNumberField.vue +21 -6
  274. package/src/components/LpNumberFlow.vue +26 -10
  275. package/src/components/LpOtpInput.vue +43 -8
  276. package/src/components/LpPagination.vue +23 -7
  277. package/src/components/LpPasswordInput.vue +4 -1
  278. package/src/components/LpPhoneInput.vue +53 -27
  279. package/src/components/LpPie.vue +94 -15
  280. package/src/components/LpPopover.vue +3 -0
  281. package/src/components/LpProgress.vue +5 -3
  282. package/src/components/LpRadioGroup.vue +9 -2
  283. package/src/components/LpSegmented.vue +12 -2
  284. package/src/components/LpSelect.vue +0 -0
  285. package/src/components/LpShortcutsDialog.vue +8 -3
  286. package/src/components/LpSidebar.vue +14 -9
  287. package/src/components/LpSidebarNav.vue +17 -5
  288. package/src/components/LpSkeleton.vue +4 -1
  289. package/src/components/LpSlider.vue +7 -1
  290. package/src/components/LpSparkline.vue +11 -3
  291. package/src/components/LpSwitch.vue +30 -7
  292. package/src/components/LpTable.vue +91 -32
  293. package/src/components/LpTableOfContents.vue +50 -32
  294. package/src/components/LpTabs.vue +23 -6
  295. package/src/components/LpTextarea.vue +44 -6
  296. package/src/components/LpThemeProvider.vue +41 -0
  297. package/src/components/LpThemeSwitcher.vue +64 -17
  298. package/src/components/LpTilt.vue +2 -2
  299. package/src/components/LpTimeField.vue +14 -3
  300. package/src/components/LpToaster.vue +66 -26
  301. package/src/components/LpTooltip.vue +22 -5
  302. package/src/components/LpTooltipProvider.vue +24 -0
  303. package/src/components/LpTopologyCanvas.vue +12 -1
  304. package/src/components/LpUptimeBar.vue +46 -8
  305. package/src/components/chart.ts +77 -5
  306. package/src/components/codeHighlight.ts +111 -15
  307. package/src/components/color.ts +30 -9
  308. package/src/components/confirm.ts +58 -0
  309. package/src/components/countries.ts +12 -0
  310. package/src/components/fileTree.ts +19 -7
  311. package/src/components/imageCrop.ts +107 -0
  312. package/src/components/link.ts +34 -0
  313. package/src/components/modalDepth.ts +48 -6
  314. package/src/components/otp.ts +16 -0
  315. package/src/components/sidebar.ts +7 -1
  316. package/src/components/tableSort.ts +37 -0
  317. package/src/composables/useField.ts +79 -0
  318. package/src/composables/useFormDraft.ts +25 -20
  319. package/src/composables/useHotkeys.ts +89 -18
  320. package/src/composables/useInputFilter.ts +77 -14
  321. package/src/composables/useLpConfig.ts +87 -0
  322. package/src/composables/useRovingFocus.ts +11 -0
  323. package/src/composables/useTilt.ts +5 -6
  324. package/src/composables/useToast.ts +56 -4
  325. package/src/composables/useZoomPan.ts +6 -6
  326. package/src/index.ts +37 -2
  327. package/src/layout/tree.ts +30 -0
  328. package/src/layout/useLayout.ts +68 -18
  329. package/src/nuxt.ts +59 -17
  330. package/src/runtime/config.plugin.ts +14 -0
  331. package/src/runtime/theme.plugin.ts +42 -22
  332. package/src/theme/useTheme.ts +206 -100
  333. package/dist/chunks/LpBreadcrumbs-p4jHe_V-.js +0 -62
  334. package/dist/chunks/LpContextMenu-CwW1QXe_.js +0 -217
  335. package/dist/chunks/LpDatePicker-CwOToxGq.js +0 -164
  336. package/dist/chunks/LpFormField-CazBc2-a.js +0 -62
  337. package/dist/chunks/LpTextarea-t7F6gv8E.js +0 -47
@@ -1,6 +1,11 @@
1
1
  import { reactive } from "vue";
2
2
  let uid = 0;
3
3
  const nid = () => `lp-n${uid++}`;
4
+ function claimId(id) {
5
+ const n = /^lp-n(\d+)$/.exec(id);
6
+ if (n) uid = Math.max(uid, Number(n[1]) + 1);
7
+ return id;
8
+ }
4
9
  function leaf(block) {
5
10
  return { kind: "leaf", id: nid(), blocks: [block], active: 0, size: 1 };
6
11
  }
@@ -173,6 +178,14 @@ function deserializeLayout(data) {
173
178
  };
174
179
  return reactive(hydrate(data));
175
180
  }
181
+ function snapshotLayout(root) {
182
+ const strip = (n) => n.kind === "leaf" ? { kind: "leaf", id: n.id, blocks: [...n.blocks], active: n.active, size: n.size } : { kind: "split", id: n.id, dir: n.dir, size: n.size, children: n.children.map(strip) };
183
+ return JSON.stringify(strip(root));
184
+ }
185
+ function restoreLayout(snapshot) {
186
+ const hydrate = (n) => n.kind === "leaf" ? { kind: "leaf", id: claimId(n.id), blocks: [...n.blocks], active: n.active, size: n.size } : { kind: "split", id: claimId(n.id), dir: n.dir, size: n.size, children: n.children.map(hydrate) };
187
+ return reactive(hydrate(JSON.parse(snapshot)));
188
+ }
176
189
  export {
177
190
  addLeaf,
178
191
  addTab,
@@ -187,6 +200,8 @@ export {
187
200
  removeLeaf,
188
201
  reorderTab,
189
202
  resizeAt,
203
+ restoreLayout,
190
204
  serializeLayout,
191
- setActiveTab
205
+ setActiveTab,
206
+ snapshotLayout
192
207
  };
@@ -1,5 +1,18 @@
1
1
  import { type Ref } from "vue";
2
2
  import { type Side, type Split } from "./tree";
3
+ /** The edits a canvas performs, routed through history when a controller owns the tree. */
4
+ export interface LayoutController {
5
+ move: (movingId: string, targetId: string, side: Side) => void;
6
+ removeBlock: (leafId: string, index: number) => void;
7
+ reorderTab: (leafId: string, from: number, to: number) => void;
8
+ setActive: (leafId: string, index: number) => void;
9
+ resize: (parent: Split, index: number, deltaFraction: number) => void;
10
+ /** Bracket a continuous resize gesture so it records one history step. */
11
+ beginResize: () => void;
12
+ endResize: () => void;
13
+ }
14
+ /** The controller managing `tree`, if a useLayout() owns it. */
15
+ export declare function layoutController(tree: Split): LayoutController | undefined;
3
16
  export interface UseLayoutOptions {
4
17
  /** Initial block ids, left-to-right, when no saved layout is loaded. */
5
18
  initial: string[];
@@ -27,6 +40,8 @@ export interface UseLayout {
27
40
  remove: (leafId: string) => void;
28
41
  move: (movingId: string, targetId: string, side: Side) => void;
29
42
  resize: (parent: Split, index: number, deltaFraction: number) => void;
43
+ beginResize: () => void;
44
+ endResize: () => void;
30
45
  undo: () => void;
31
46
  redo: () => void;
32
47
  /** Replace the whole layout from a flat block list. */
@@ -1,5 +1,9 @@
1
- import { shallowRef, computed, ref } from "vue";
2
- import { makeLayout, countBlocks, serializeLayout, resizeAt, setActiveTab, deserializeLayout, moveLeaf, removeLeaf, removeBlock, reorderTab, addTab, addLeaf } from "./tree.js";
1
+ import { shallowRef, computed, ref, watch } from "vue";
2
+ import { makeLayout, countBlocks, serializeLayout, resizeAt, setActiveTab, snapshotLayout, restoreLayout, reorderTab, removeBlock, moveLeaf, deserializeLayout, removeLeaf, addTab, addLeaf } from "./tree.js";
3
+ const controllers = /* @__PURE__ */ new WeakMap();
4
+ function layoutController(tree) {
5
+ return controllers.get(tree);
6
+ }
3
7
  function useLayout(options) {
4
8
  const limit = options.historyLimit ?? 50;
5
9
  const layout = shallowRef(makeLayout(options.initial));
@@ -8,16 +12,36 @@ function useLayout(options) {
8
12
  const future = ref([]);
9
13
  const canUndo = computed(() => past.value.length > 0);
10
14
  const canRedo = computed(() => future.value.length > 0);
11
- const snapshot = () => JSON.stringify(serializeLayout(layout.value));
12
- function record(mutate) {
13
- const before = snapshot();
14
- mutate();
15
+ const snapshot = () => snapshotLayout(layout.value);
16
+ function push(before) {
17
+ if (before === snapshot()) return;
15
18
  past.value.push(before);
16
19
  if (past.value.length > limit) past.value.shift();
17
20
  future.value = [];
18
21
  }
22
+ function record(mutate) {
23
+ const before = snapshot();
24
+ mutate();
25
+ push(before);
26
+ }
27
+ let resizeStart = null;
28
+ const controller = {
29
+ move: (movingId, targetId, side) => record(() => moveLeaf(layout.value, movingId, targetId, side)),
30
+ removeBlock: (leafId, index) => record(() => removeBlock(layout.value, leafId, index)),
31
+ reorderTab: (leafId, from, to) => record(() => reorderTab(layout.value, leafId, from, to)),
32
+ setActive: (leafId, index) => setActiveTab(layout.value, leafId, index),
33
+ resize: (parent, index, delta) => resizeAt(parent, index, delta),
34
+ beginResize: () => {
35
+ resizeStart = snapshot();
36
+ },
37
+ endResize: () => {
38
+ if (resizeStart !== null) push(resizeStart);
39
+ resizeStart = null;
40
+ }
41
+ };
42
+ watch(layout, (tree) => controllers.set(tree, controller), { immediate: true, flush: "sync" });
19
43
  function restore(serialized) {
20
- layout.value = deserializeLayout(JSON.parse(serialized));
44
+ layout.value = restoreLayout(serialized);
21
45
  }
22
46
  return {
23
47
  layout,
@@ -26,15 +50,17 @@ function useLayout(options) {
26
50
  canRedo,
27
51
  add: (block, side) => record(() => addLeaf(layout.value, block, side)),
28
52
  addTab: (leafId, block) => record(() => addTab(layout.value, leafId, block)),
29
- setActive: (leafId, index) => setActiveTab(layout.value, leafId, index),
30
- reorderTab: (leafId, from, to) => record(() => reorderTab(layout.value, leafId, from, to)),
31
- removeBlock: (leafId, index) => record(() => removeBlock(layout.value, leafId, index)),
53
+ setActive: controller.setActive,
54
+ reorderTab: controller.reorderTab,
55
+ removeBlock: controller.removeBlock,
32
56
  remove: (leafId) => record(() => removeLeaf(layout.value, leafId)),
33
- move: (movingId, targetId, side) => record(() => moveLeaf(layout.value, movingId, targetId, side)),
57
+ move: controller.move,
34
58
  // Resize fires continuously during a divider drag; recording every frame
35
- // would flood history, so it mutates without a snapshot. (A consumer that
36
- // wants resize in history can wrap a single record() around the gesture.)
37
- resize: (parent, index, delta) => resizeAt(parent, index, delta),
59
+ // would flood history. The canvas brackets its gestures with
60
+ // beginResize/endResize, which records the whole drag as one step.
61
+ resize: controller.resize,
62
+ beginResize: controller.beginResize,
63
+ endResize: controller.endResize,
38
64
  undo: () => {
39
65
  const prev = past.value.pop();
40
66
  if (prev === void 0) return;
@@ -57,5 +83,6 @@ function useLayout(options) {
57
83
  };
58
84
  }
59
85
  export {
86
+ layoutController,
60
87
  useLayout
61
88
  };
@@ -1,5 +1,14 @@
1
1
  import { type ComputedRef, type DeepReadonly, type Ref } from "vue";
2
2
  import { type TokenSet } from "./tokens";
3
+ /** A light/dark pair followed by the operating system's colour scheme. */
4
+ export interface SystemThemes {
5
+ light: TokenSet;
6
+ dark: TokenSet;
7
+ }
8
+ /** Whether the OS asks for dark. True where it cannot be asked (server, old engines). */
9
+ export declare function prefersDarkScheme(): boolean;
10
+ /** Every CSS variable a theme sets, in application order. */
11
+ export declare function themeEntries(input: TokenSet): [string, string][];
3
12
  export declare function applyTheme(theme: TokenSet, root?: HTMLElement): void;
4
13
  /** Point a circular theme reveal expands from (viewport px). */
5
14
  export interface RevealOrigin {
@@ -11,31 +20,45 @@ export declare function serializeTheme(theme: TokenSet): string;
11
20
  export declare function themeToCssVars(theme: TokenSet): string;
12
21
  /** Render a theme as a complete CSS rule (default selector `:root`). */
13
22
  export declare function themeToCssRule(theme: TokenSet, selector?: string): string;
14
- /** Parse + shallow-validate a theme JSON; throws on a malformed shape. */
23
+ export declare function normalizeTheme(raw: Partial<TokenSet> & {
24
+ name?: string;
25
+ }): TokenSet;
26
+ /** Parse + validate a theme JSON; throws on a malformed shape. */
15
27
  export declare function parseTheme(json: string): TokenSet;
16
28
  export declare function bootstrapTheme(fallback: TokenSet): TokenSet;
17
29
  export declare function cacheTheme(theme: TokenSet): void;
30
+ /** Persist "follow the OS" with the pair it switches between. */
31
+ export declare function cacheSystemThemes(system: SystemThemes): void;
18
32
  export declare const THEME_COOKIE = "lp-theme";
19
33
  /**
20
34
  * Resolve a theme name (as read from the `lp-theme` cookie) against a set of
21
35
  * known themes. Returns `null` when the name is absent or unknown, so the caller
22
36
  * can fall back to its own default.
23
37
  *
24
- * Server-side counterpart to `cacheTheme`: pair it with `themeToCssRule` to
25
- * inline the right variables into `<head>` during SSR.
38
+ * Accepts both a raw and an already-decoded value: frameworks differ on whether
39
+ * their cookie helpers decode, and a name containing `%` must not throw.
40
+ * A "follow the OS" cookie resolves to its dark member unless `prefersDark` is
41
+ * false (pass it from the `Sec-CH-Prefers-Color-Scheme` request header).
26
42
  */
27
- export declare function themeFromCookie(cookieValue: string | null | undefined, themes: readonly TokenSet[]): TokenSet | null;
43
+ export declare function themeFromCookie(cookieValue: string | null | undefined, themes: readonly TokenSet[], options?: {
44
+ prefersDark?: boolean;
45
+ }): TokenSet | null;
28
46
  export interface UseTheme {
29
47
  /** The active theme, kept in sync by the kit. Null until hydrated (SSR/first
30
- * paint) — reactive, so a switcher binds straight to it. */
48
+ * paint), reactive, so a switcher binds straight to it. */
31
49
  current: DeepReadonly<Ref<TokenSet | null>>;
32
50
  /** The active theme's name (""="not yet hydrated"). */
33
51
  currentName: ComputedRef<string>;
34
- /** Set + persist a theme (with the circular reveal if `origin`/transition).
35
- * This is the one call a switcher needs — it updates `current` too. */
52
+ /** True while the theme follows the OS colour scheme (see setSystem). */
53
+ system: ComputedRef<boolean>;
54
+ /** Set + persist a theme (with the circular reveal). Stops following the OS. */
36
55
  setTheme: (theme: TokenSet, origin?: RevealOrigin) => void;
37
- /** Seed the fallback (used when nothing is cached) and hydrate now. Call once
38
- * at app start (client) — optional; the first useTheme() hydrates anyway. */
56
+ /**
57
+ * Follow the OS colour scheme, switching between `themes.light` and
58
+ * `themes.dark` as it changes. Persisted like any other choice.
59
+ */
60
+ setSystem: (themes: SystemThemes, origin?: RevealOrigin) => void;
61
+ /** Seed the fallback (used when nothing is cached) and hydrate now. */
39
62
  init: (fallback: TokenSet) => TokenSet;
40
63
  apply: (theme: TokenSet) => void;
41
64
  applyWithTransition: (theme: TokenSet, origin?: RevealOrigin) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.43.4",
3
+ "version": "0.44.0",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -28,10 +28,11 @@
28
28
  "./scrollbar.css": "./src/tokens/scrollbar.css",
29
29
  "./canvas.css": "./src/canvas.css",
30
30
  "./package.json": "./package.json",
31
- "./dist/*": {
31
+ "./dist/*.js": {
32
32
  "types": "./dist/*.d.ts",
33
- "default": "./dist/*"
34
- }
33
+ "default": "./dist/*.js"
34
+ },
35
+ "./dist/*": "./dist/*"
35
36
  },
36
37
  "publishConfig": {
37
38
  "registry": "https://registry.npmjs.org",
@@ -40,7 +41,7 @@
40
41
  "sideEffects": [
41
42
  "**/*.css",
42
43
  "**/icons.ts",
43
- "**/icons.js"
44
+ "**/chunks/LpIcon-*.js"
44
45
  ],
45
46
  "scripts": {
46
47
  "dev": "vite",
@@ -52,14 +53,14 @@
52
53
  "test": "bun test",
53
54
  "release": "npm version patch -m \"chore: release v%s\" && git push --follow-tags",
54
55
  "release:minor": "npm version minor -m \"chore: release v%s\" && git push --follow-tags",
55
- "release:major": "npm version major -m \"chore: release v%s\" && git push --follow-tags"
56
+ "release:major": "npm version major -m \"chore: release v%s\" && git push --follow-tags",
57
+ "check:pack": "node scripts/check-pack.mjs"
56
58
  },
57
59
  "peerDependencies": {
58
60
  "@iconify-json/lucide": "^1.2.0",
59
61
  "@iconify/vue": "^5.0.0",
60
62
  "@internationalized/date": "^3.5.0",
61
63
  "@nuxt/kit": "^3.0.0 || ^4.0.0",
62
- "@vueuse/core": "^12.0.0 || ^13.0.0 || ^14.0.0",
63
64
  "motion-v": "^2.2.0",
64
65
  "reka-ui": "^2.9.0",
65
66
  "tailwind-variants": "^3.0.0",
@@ -1,9 +1,8 @@
1
1
  /* AUTO-GENERATED by scripts/gen-component-names.mjs — do not edit.
2
2
  * The full list of kit component names, kept in sync with the component files
3
- * so the Nuxt module (./nuxt) auto-registers every component including new ones. */
3
+ * so the Nuxt module (./nuxt) auto-registers every exported component including new ones. */
4
4
  export const COMPONENT_NAMES = [
5
5
  "LayoutCanvas",
6
- "LayoutNode",
7
6
  "LpActionBar",
8
7
  "LpAddressInput",
9
8
  "LpAlert",
@@ -21,6 +20,7 @@ export const COMPONENT_NAMES = [
21
20
  "LpColorPicker",
22
21
  "LpCommandPalette",
23
22
  "LpConfirmDialog",
23
+ "LpConfirmHost",
24
24
  "LpContextMenu",
25
25
  "LpDatePicker",
26
26
  "LpDisclosure",
@@ -30,13 +30,11 @@ export const COMPONENT_NAMES = [
30
30
  "LpDropzone",
31
31
  "LpEmptyState",
32
32
  "LpFileTree",
33
- "LpFileTreeNode",
34
33
  "LpFormField",
35
34
  "LpIcon",
36
35
  "LpImageEditor",
37
36
  "LpInfraNode",
38
37
  "LpInput",
39
- "LpLaneNode",
40
38
  "LpLightbox",
41
39
  "LpLink",
42
40
  "LpLogViewer",
@@ -65,7 +63,6 @@ export const COMPONENT_NAMES = [
65
63
  "LpShift",
66
64
  "LpShortcutsDialog",
67
65
  "LpSidebar",
68
- "LpSidebarNav",
69
66
  "LpSkeleton",
70
67
  "LpSlider",
71
68
  "LpSparkline",
@@ -76,12 +73,13 @@ export const COMPONENT_NAMES = [
76
73
  "LpTableOfContents",
77
74
  "LpTabs",
78
75
  "LpTextarea",
76
+ "LpThemeProvider",
79
77
  "LpThemeSwitcher",
80
78
  "LpTilt",
81
79
  "LpTimeField",
82
80
  "LpToaster",
83
81
  "LpTooltip",
82
+ "LpTooltipProvider",
84
83
  "LpTopologyCanvas",
85
- "LpUptimeBar",
86
- "LpWaypointEdge"
84
+ "LpUptimeBar"
87
85
  ] as const
@@ -0,0 +1,88 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ * The rows of an LpContextMenu panel, rendering itself for every submenu so
4
+ * `children` nests to any depth. Internal: not exported.
5
+ */
6
+ import {
7
+ ContextMenuCheckboxItem,
8
+ ContextMenuItem,
9
+ ContextMenuPortal,
10
+ ContextMenuSeparator,
11
+ ContextMenuSub,
12
+ ContextMenuSubContent,
13
+ ContextMenuSubTrigger,
14
+ } from "reka-ui"
15
+ import type { ContextMenuItemDef } from "./LpContextMenu.vue"
16
+ import LpIcon from "./LpIcon.vue"
17
+
18
+ defineOptions({ name: "ContextMenuItems" })
19
+
20
+ defineProps<{
21
+ items: ContextMenuItemDef[]
22
+ panel: string
23
+ }>()
24
+
25
+ const ITEM =
26
+ "group/item flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-1.5 text-sm outline-none transition-[background-color,color] duration-[var(--duration-fast)] data-[disabled]:pointer-events-none data-[disabled]:opacity-45 data-[highlighted]:bg-brand-soft"
27
+ const ICON =
28
+ "transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-data-[highlighted]/item:translate-x-0.5"
29
+ </script>
30
+
31
+ <template>
32
+ <template v-for="(item, i) in items" :key="i">
33
+ <ContextMenuSeparator v-if="item.separatorBefore" class="my-1 h-px bg-line" />
34
+
35
+ <ContextMenuSub v-if="item.children?.length">
36
+ <ContextMenuSubTrigger
37
+ :class="[ITEM, 'justify-between text-ink data-[highlighted]:text-brand data-[state=open]:bg-brand-soft data-[state=open]:text-brand']"
38
+ :disabled="item.disabled"
39
+ >
40
+ <span class="flex items-center gap-2">
41
+ <LpIcon v-if="item.icon" :name="item.icon" :size="15" :class="ICON" />
42
+ {{ item.label }}
43
+ </span>
44
+ <LpIcon name="lucide:chevron-right" :size="14" class="text-muted" />
45
+ </ContextMenuSubTrigger>
46
+ <ContextMenuPortal>
47
+ <ContextMenuSubContent :class="panel" :side-offset="2" :align-offset="-4">
48
+ <ContextMenuItems :items="item.children" :panel="panel" />
49
+ </ContextMenuSubContent>
50
+ </ContextMenuPortal>
51
+ </ContextMenuSub>
52
+
53
+ <!-- Tick-box item: a real checkbox item, so assistive tech hears its state.
54
+ `@select.prevent` keeps the menu open, since these come in lists where
55
+ several are set in one visit. The tick occupies a fixed slot so
56
+ unticked rows stay aligned with ticked ones. -->
57
+ <ContextMenuCheckboxItem
58
+ v-else-if="item.checked !== undefined"
59
+ :model-value="item.checked"
60
+ :class="[ITEM, 'justify-between text-ink data-[highlighted]:text-brand']"
61
+ :disabled="item.disabled"
62
+ @select.prevent="item.onSelect?.()"
63
+ >
64
+ <span class="flex items-center gap-2">
65
+ <LpIcon
66
+ :name="item.checked ? 'lucide:check' : 'lucide:minus'"
67
+ :size="15"
68
+ :class="[ICON, item.checked ? '' : 'opacity-25']"
69
+ />
70
+ {{ item.label }}
71
+ </span>
72
+ <kbd v-if="item.shortcut" class="text-xs text-muted">{{ item.shortcut }}</kbd>
73
+ </ContextMenuCheckboxItem>
74
+
75
+ <ContextMenuItem
76
+ v-else
77
+ :class="[ITEM, 'justify-between', item.danger ? 'text-danger data-[highlighted]:bg-danger-soft data-[highlighted]:text-danger' : 'text-ink data-[highlighted]:text-brand']"
78
+ :disabled="item.disabled"
79
+ @select="item.onSelect?.()"
80
+ >
81
+ <span class="flex items-center gap-2">
82
+ <LpIcon v-if="item.icon" :name="item.icon" :size="15" :class="ICON" />
83
+ {{ item.label }}
84
+ </span>
85
+ <kbd v-if="item.shortcut" class="text-xs text-muted">{{ item.shortcut }}</kbd>
86
+ </ContextMenuItem>
87
+ </template>
88
+ </template>
@@ -22,7 +22,8 @@ import {
22
22
  type Split,
23
23
  } from "../layout/tree"
24
24
  import type { BlockRegistry } from "../layout/registry"
25
- import { ref } from "vue"
25
+ import { layoutController, type LayoutController } from "../layout/useLayout"
26
+ import { computed, ref } from "vue"
26
27
 
27
28
  const props = defineProps<{
28
29
  modelValue: Split
@@ -42,13 +43,30 @@ defineSlots<{
42
43
 
43
44
  const dragId = ref<string | null>(null)
44
45
 
46
+ // A tree owned by useLayout() is edited through its controller, so every edit
47
+ // lands in undo history; a bare tree is mutated directly as before.
48
+ const edits = computed<LayoutController>(() => {
49
+ const tree = props.modelValue
50
+ return (
51
+ layoutController(tree) ?? {
52
+ move: (movingId, targetId, side) => moveLeaf(tree, movingId, targetId, side),
53
+ removeBlock: (leafId, index) => removeBlock(tree, leafId, index),
54
+ reorderTab: (leafId, from, to) => reorderTab(tree, leafId, from, to),
55
+ setActive: (leafId, index) => setActiveTab(tree, leafId, index),
56
+ resize: (parent, index, delta) => resizeAt(parent, index, delta),
57
+ beginResize: () => {},
58
+ endResize: () => {},
59
+ }
60
+ )
61
+ })
62
+
45
63
  function onDrop(targetId: string, side: Side) {
46
- if (dragId.value) moveLeaf(props.modelValue, dragId.value, targetId, side)
64
+ if (dragId.value) edits.value.move(dragId.value, targetId, side)
47
65
  dragId.value = null
48
66
  }
49
67
 
50
68
  function onResize(parent: Split, index: number, delta: number) {
51
- resizeAt(parent, index, delta)
69
+ edits.value.resize(parent, index, delta)
52
70
  }
53
71
  </script>
54
72
 
@@ -63,10 +81,12 @@ function onResize(parent: Split, index: number, delta: number) {
63
81
  @dragstart="(id) => (dragId = id)"
64
82
  @dragend="dragId = null"
65
83
  @drop="onDrop"
66
- @set-active="(leafId, i) => setActiveTab(modelValue, leafId, i)"
67
- @remove-block="(leafId, i) => removeBlock(modelValue, leafId, i)"
68
- @reorder-tab="(leafId, from, to) => reorderTab(modelValue, leafId, from, to)"
84
+ @set-active="(leafId, i) => edits.setActive(leafId, i)"
85
+ @remove-block="(leafId, i) => edits.removeBlock(leafId, i)"
86
+ @reorder-tab="(leafId, from, to) => edits.reorderTab(leafId, from, to)"
69
87
  @resize="onResize"
88
+ @resize-start="edits.beginResize()"
89
+ @resize-end="edits.endResize()"
70
90
  >
71
91
  <template #block="slotProps">
72
92
  <slot name="block" v-bind="slotProps" />
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { Motion } from "motion-v"
3
- import { computed, ref } from "vue"
3
+ import { computed, onBeforeUnmount, ref } from "vue"
4
4
  import type { LayoutNode, Leaf, Side, Split } from "../layout/tree"
5
5
  import type { BlockRegistry } from "../layout/registry"
6
6
  import { blockTitle } from "../layout/registry"
@@ -21,6 +21,8 @@ const emit = defineEmits<{
21
21
  (e: "remove-block", leafId: string, index: number): void
22
22
  (e: "reorder-tab", leafId: string, from: number, to: number): void
23
23
  (e: "resize", parent: Split, index: number, deltaFraction: number): void
24
+ (e: "resize-start"): void
25
+ (e: "resize-end"): void
24
26
  }>()
25
27
 
26
28
  // Explicit slot shape so the recursive #block pass-through types cleanly.
@@ -97,10 +99,18 @@ function onDragOver(ev: DragEvent) {
97
99
  else hoverSide.value = "center"
98
100
  }
99
101
 
102
+ let stopSplitter: (() => void) | null = null
103
+ onBeforeUnmount(() => stopSplitter?.())
104
+
100
105
  function startSplitter(ev: PointerEvent, parent: Split, index: number) {
101
106
  ev.preventDefault()
102
- const host = (ev.currentTarget as HTMLElement).parentElement
107
+ const handle = ev.currentTarget as HTMLElement
108
+ const host = handle.parentElement
103
109
  if (!host) return
110
+ // Capture keeps the moves coming when the pointer crosses an iframe or leaves
111
+ // the window; pointercancel (a touch the browser took over) ends it too.
112
+ handle.setPointerCapture?.(ev.pointerId)
113
+ emit("resize-start")
104
114
  const rect = host.getBoundingClientRect()
105
115
  const horizontal = parent.dir === "row"
106
116
  const span = horizontal ? rect.width : rect.height
@@ -114,9 +124,14 @@ function startSplitter(ev: PointerEvent, parent: Split, index: number) {
114
124
  const up = () => {
115
125
  window.removeEventListener("pointermove", move)
116
126
  window.removeEventListener("pointerup", up)
127
+ window.removeEventListener("pointercancel", up)
128
+ stopSplitter = null
129
+ emit("resize-end")
117
130
  }
131
+ stopSplitter = up
118
132
  window.addEventListener("pointermove", move)
119
133
  window.addEventListener("pointerup", up)
134
+ window.addEventListener("pointercancel", up)
120
135
  }
121
136
  </script>
122
137
 
@@ -141,6 +156,8 @@ function startSplitter(ev: PointerEvent, parent: Split, index: number) {
141
156
  @remove-block="(l, idx) => emit('remove-block', l, idx)"
142
157
  @reorder-tab="(l, from, to) => emit('reorder-tab', l, from, to)"
143
158
  @resize="(p, idx, d) => emit('resize', p, idx, d)"
159
+ @resize-start="emit('resize-start')"
160
+ @resize-end="emit('resize-end')"
144
161
  >
145
162
  <template #block="slotProps">
146
163
  <slot name="block" v-bind="slotProps" />
@@ -20,7 +20,8 @@
20
20
  * name rather than `alertdialog` — it is a landmark you can Tab into, not a
21
21
  * thing demanding an answer before anything else can happen.
22
22
  */
23
- import { computed, nextTick, ref, useSlots, watch } from "vue"
23
+ import { computed, onBeforeUnmount, ref, useSlots, watch } from "vue"
24
+ import { useLabels } from "../composables/useLpConfig"
24
25
  import { useBarStack } from "./actionBar"
25
26
  import { CLOSE_ICON } from "./dropdown"
26
27
  import LpButton from "./LpButton.vue"
@@ -85,6 +86,7 @@ const props = withDefaults(
85
86
  maxWidth?: string
86
87
  /** Accessible name for the landmark. Falls back to `title`. */
87
88
  ariaLabel?: string
89
+ labels?: Partial<{ dismiss: string }>
88
90
  }>(),
89
91
  {
90
92
  open: true,
@@ -100,6 +102,7 @@ const emit = defineEmits<{
100
102
  }>()
101
103
 
102
104
  const slots = useSlots()
105
+ const text = useLabels("actionBar", { dismiss: "Dismiss" }, () => props.labels)
103
106
 
104
107
  // The last action is the primary one unless the caller has said otherwise; the
105
108
  // rest default to `ghost` so a bar with three buttons still has one obvious
@@ -154,19 +157,39 @@ const stackOffset = computed(() => {
154
157
  return offset()
155
158
  })
156
159
 
160
+ /*
161
+ * The height is watched, not measured once: a description that wraps after a
162
+ * rotation, or content swapped while the bar is up, changes it, and the bars
163
+ * above would overlap this one.
164
+ */
165
+ let observer: ResizeObserver | null = null
166
+
157
167
  watch(
158
168
  () => props.open,
159
- async (isOpen) => {
169
+ (isOpen) => {
170
+ // Nothing on the server ever unregisters, so registering there would leak
171
+ // one slot per rendered request into every later page.
172
+ if (typeof window === "undefined") return
160
173
  if (!isOpen) {
161
174
  unregister()
162
175
  return
163
176
  }
164
177
  register()
165
- await nextTick()
166
- if (root.value) setHeight(root.value.getBoundingClientRect().height)
167
178
  },
168
179
  { immediate: true },
169
180
  )
181
+
182
+ watch(root, (el) => {
183
+ observer?.disconnect()
184
+ observer = null
185
+ if (!el) return
186
+ setHeight(el.getBoundingClientRect().height)
187
+ if (typeof ResizeObserver === "undefined") return
188
+ observer = new ResizeObserver(() => setHeight(el.getBoundingClientRect().height))
189
+ observer.observe(el)
190
+ })
191
+
192
+ onBeforeUnmount(() => observer?.disconnect())
170
193
  </script>
171
194
 
172
195
  <template>
@@ -194,9 +217,9 @@ watch(
194
217
  ref="root"
195
218
  role="region"
196
219
  :aria-label="ariaLabel ?? title"
197
- class="pointer-events-none fixed inset-x-0 bottom-0 z-(--z-modal) flex justify-center transition-[translate] duration-[var(--duration-medium)] ease-[var(--ease-emphasized)] motion-reduce:transition-none"
220
+ class="pointer-events-none fixed inset-x-0 bottom-0 flex justify-center transition-[translate] duration-[var(--duration-medium)] ease-[var(--ease-emphasized)] motion-reduce:transition-none"
198
221
  :class="width === 'full' ? '' : 'p-3 sm:p-4'"
199
- :style="{ translate: `0 -${stackOffset}px` }"
222
+ :style="{ translate: `0 -${stackOffset}px`, zIndex: 'calc(var(--z-overlay) - 1)' }"
200
223
  >
201
224
  <div
202
225
  class="lp-skin-panel pointer-events-auto flex items-center gap-3 border-solid bg-surface-overlay"
@@ -261,7 +284,7 @@ watch(
261
284
  <button
262
285
  v-if="dismissible"
263
286
  type="button"
264
- aria-label="Dismiss"
287
+ :aria-label="text.dismiss"
265
288
  class="group -mr-1 flex size-8 shrink-0 items-center justify-center rounded-control text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
266
289
  @click="dismiss"
267
290
  >