@leavepulse/ui 0.43.4 → 0.45.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 (437) hide show
  1. package/README.md +121 -7
  2. package/dist/assets/{LpCommandPalette-B-0Est4Z.css → LpCommandPalette-nGNnzXqQ.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/{LpRollingText-CR1mb0Vt.css → LpRollingText-BGnE6q9j.css} +3 -0
  6. package/dist/assets/{LpShortcutsDialog-C6YoQ8_M.css → LpShortcutsDialog-CqYX3yy6.css} +28 -7
  7. package/dist/assets/{LpSkeleton-D7xU8IPV.css → LpSkeleton-H3Z5dEIk.css} +4 -1
  8. package/dist/assets/{LpTable-BgPLJOXa.css → LpTable-CPwWxTJS.css} +49 -9
  9. package/dist/chunks/ContextMenuItems-BtKU7074.js +124 -0
  10. package/dist/chunks/{LayoutCanvas-Dd20boUG.js → LayoutCanvas-BkaGXTjx.js} +26 -9
  11. package/dist/chunks/{LayoutNode-DGpZA9LX.js → LayoutNode-B3_9E1Xa.js} +28 -12
  12. package/dist/chunks/{LpActionBar-BlvtGB2P.js → LpActionBar-CnfFVIwC.js} +26 -12
  13. package/dist/chunks/{LpAddressInput-B-Zvxsn7.js → LpAddressInput-BwX8uo4_.js} +54 -49
  14. package/dist/chunks/{LpAlert-BAYHDSS1.js → LpAlert-B6ilrM7T.js} +16 -9
  15. package/dist/chunks/{LpAppShell-BGQVIY-3.js → LpAppShell-C53_RaGF.js} +53 -26
  16. package/dist/chunks/{LpAutocomplete-DExlIZe_.js → LpAutocomplete-C8IgSMsi.js} +52 -24
  17. package/dist/chunks/{LpAvatar-B3-xAOH3.js → LpAvatar-C1XStdOo.js} +19 -10
  18. package/dist/chunks/LpBreadcrumbs-CQJd73Gm.js +95 -0
  19. package/dist/chunks/{LpButton-5VXXCjwF.js → LpButton-CKSwC6Mu.js} +45 -15
  20. package/dist/chunks/{LpCalendar-B7Ko5RdR.js → LpCalendar-DrA2McTU.js} +1 -1
  21. package/dist/chunks/{LpCard-BhI1N_vL.js → LpCard-C2IvucmX.js} +1 -1
  22. package/dist/chunks/{LpCheckbox-CVccKYaH.js → LpCheckbox-Bkj13CbW.js} +8 -4
  23. package/dist/chunks/{LpCodeBlock-Bo0B4TFR.js → LpCodeBlock-ca7ANFyW.js} +49 -17
  24. package/dist/chunks/{LpColorPicker-BlXea7LM.js → LpColorPicker-zz4VGkG4.js} +75 -26
  25. package/dist/chunks/{LpConfirmDialog-Q-bxff8z.js → LpConfirmDialog-DCw5jR1t.js} +59 -17
  26. package/dist/chunks/LpConfirmHost-CbywMpMP.js +49 -0
  27. package/dist/chunks/LpContextMenu-BqkAYa9X.js +69 -0
  28. package/dist/chunks/LpDatePicker-DuusgT69.js +195 -0
  29. package/dist/chunks/LpDateRangePicker-DHeuznfC.js +208 -0
  30. package/dist/chunks/{LpDisclosure-ChQYmjlR.js → LpDisclosure-Cc23-JOT.js} +1 -1
  31. package/dist/chunks/{LpDrawer-BZ6k3hvo.js → LpDrawer-Dt4aB4Fr.js} +65 -28
  32. package/dist/chunks/{LpDropdownMenu-DJpoqRCb.js → LpDropdownMenu-DdJGQzPn.js} +2 -1
  33. package/dist/chunks/{LpEmptyState-Dsdqz1Wj.js → LpEmptyState-BJbZXC8D.js} +1 -1
  34. package/dist/chunks/LpFieldNotice-wtieLUTj.js +64 -0
  35. package/dist/chunks/{LpFileTree-_5vFBgaK.js → LpFileTree-CNurOvkV.js} +25 -10
  36. package/dist/chunks/{LpFileTreeNode-BdcIYKIE.js → LpFileTreeNode-D_qRRdsH.js} +3 -3
  37. package/dist/chunks/LpFormField-CtVYj6tO.js +101 -0
  38. package/dist/chunks/{LpIcon-CCnX5_2j.js → LpIcon-DYtaid0K.js} +20 -6
  39. package/dist/chunks/{LpInput-9EXWUM9B.js → LpInput-D-YGQbEv.js} +58 -20
  40. package/dist/chunks/{LpLightbox-2sYVMY-S.js → LpLightbox-DSDahF23.js} +148 -61
  41. package/dist/chunks/{LpLink-DCND-zyu.js → LpLink-Krkjhe_y.js} +15 -6
  42. package/dist/chunks/{LpLogViewer-CaRy5MeS.js → LpLogViewer-KypbgOyo.js} +300 -72
  43. package/dist/chunks/{LpMapPicker-ul4oYno6.js → LpMapPicker-Cc84LaM3.js} +39 -14
  44. package/dist/chunks/{LpMediaGallery-FF-NquIo.js → LpMediaGallery-YKr-cQPN.js} +7 -4
  45. package/dist/chunks/{LpModal-Bt0pQCI_.js → LpModal-DMMDQHAu.js} +52 -29
  46. package/dist/chunks/{LpModalNav-YaifHrXC.js → LpModalNav-BVPDyIup.js} +10 -7
  47. package/dist/chunks/{LpNotificationBell-DyGGsMAM.js → LpNotificationBell-CCwyJP5C.js} +79 -33
  48. package/dist/chunks/{LpNumberField-CWuX08iX.js → LpNumberField-B77O-z28.js} +21 -8
  49. package/dist/chunks/{LpNumberFlow-amWYaeuA.js → LpNumberFlow-z4WPZcZu.js} +22 -12
  50. package/dist/chunks/{LpOtpInput-y2v-pmiN.js → LpOtpInput-CziPF3Oq.js} +37 -8
  51. package/dist/chunks/{LpPagination-5FxjmmB5.js → LpPagination-BcDlo3r_.js} +31 -20
  52. package/dist/chunks/{LpPasswordInput--sjYCkLS.js → LpPasswordInput-eKzQhbQP.js} +11 -8
  53. package/dist/chunks/LpPhoneInput-BCbrrhgE.js +336 -0
  54. package/dist/chunks/{LpPie-D6OnzwlJ.js → LpPie-BEJ2SfI6.js} +84 -28
  55. package/dist/chunks/{LpPopover-8268UmlC.js → LpPopover-DCKeqiVn.js} +5 -2
  56. package/dist/chunks/{LpProgress-Btq5W4Ut.js → LpProgress-CBU1iI69.js} +5 -4
  57. package/dist/chunks/{LpRadioGroup-xPIuVDO_.js → LpRadioGroup-DmEW0l78.js} +9 -2
  58. package/dist/chunks/LpRangeCalendar-DZuaRV0W.js +331 -0
  59. package/dist/chunks/{LpRollingText-CM5gkSeF.js → LpRollingText-DB7upf-5.js} +1 -1
  60. package/dist/chunks/{LpScrollArea-D8xyKXcF.js → LpScrollArea-Bry1fdOM.js} +14 -2
  61. package/dist/chunks/{LpSection-aM6J_u51.js → LpSection-tKEH0jYf.js} +1 -1
  62. package/dist/chunks/{LpSegmented-fV0Sk92p.js → LpSegmented-C09EEam2.js} +8 -4
  63. package/dist/chunks/{LpSelect-ANIVtrpM.js → LpSelect-DNIL6MB9.js} +50 -31
  64. package/dist/chunks/{LpSidebar-ClBUwoEz.js → LpSidebar-CQNB0-yU.js} +30 -22
  65. package/dist/chunks/{LpSidebarNav-llOcs_Fn.js → LpSidebarNav-kx-LdqNU.js} +16 -11
  66. package/dist/chunks/{LpSkeleton-CTI-vBgW.js → LpSkeleton-BB6XIV-w.js} +1 -1
  67. package/dist/chunks/{LpSlider-BwNIBkAK.js → LpSlider-Cuzmrd7p.js} +7 -2
  68. package/dist/chunks/{LpSparkline-C6xQ2dwv.js → LpSparkline-CB80N8SJ.js} +11 -4
  69. package/dist/chunks/{LpStat-CQaW8R--.js → LpStat-B-7udBRP.js} +2 -2
  70. package/dist/chunks/{LpStepper-Bk-XaQYU.js → LpStepper-C7XiVKZj.js} +34 -19
  71. package/dist/chunks/{LpSwitch-Blizvqyt.js → LpSwitch-OrcOJX9m.js} +41 -27
  72. package/dist/chunks/LpTableFilter-BV8PpLM9.js +158 -0
  73. package/dist/chunks/{LpTableOfContents-CjvxPMpU.js → LpTableOfContents-BUcAfGPQ.js} +36 -41
  74. package/dist/chunks/{LpTabs-Cefe_lys.js → LpTabs-29iW1XOx.js} +20 -6
  75. package/dist/chunks/LpTagsInput-D1SuqbhB.js +300 -0
  76. package/dist/chunks/LpTextarea-uuScWo6-.js +77 -0
  77. package/dist/chunks/LpThemeProvider-Be4r2TQX.js +35 -0
  78. package/dist/chunks/LpThemeSwitcher-B4aVPbNC.js +166 -0
  79. package/dist/chunks/{LpTilt-DuyKbpkA.js → LpTilt-B7KHX1zH.js} +2 -2
  80. package/dist/chunks/{LpTimeField-K6nAGboX.js → LpTimeField-Cqe7fXnh.js} +14 -8
  81. package/dist/chunks/{LpToaster-gDB1EaU3.js → LpToaster-DpJvoCOh.js} +94 -31
  82. package/dist/chunks/{LpTooltip-hCLPAqMG.js → LpTooltip-CXVKcOXT.js} +14 -6
  83. package/dist/chunks/LpTooltipProvider-CftjV7Xc.js +25 -0
  84. package/dist/chunks/{LpTopologyCanvas-Cm1L39uL.js → LpTopologyCanvas-6ozNrQKh.js} +8 -2
  85. package/dist/chunks/{LpUptimeBar-DvAMno0S.js → LpUptimeBar-ugcv25xj.js} +44 -15
  86. package/dist/chunks/icons-CkA0njzm.js +43 -0
  87. package/dist/chunks/{LpThemeSwitcher-Cue-myST.js → useTheme-DJm1JSsY.js} +215 -260
  88. package/dist/component-names.d.ts +1 -1
  89. package/dist/components/ContextMenuItems.vue.d.ts +8 -0
  90. package/dist/components/ContextMenuItems.vue.js +4 -0
  91. package/dist/components/LayoutCanvas.vue.js +1 -1
  92. package/dist/components/LayoutNode.vue.d.ts +4 -0
  93. package/dist/components/LayoutNode.vue.js +1 -1
  94. package/dist/components/LpActionBar.vue.d.ts +3 -0
  95. package/dist/components/LpActionBar.vue.js +1 -1
  96. package/dist/components/LpAddressInput.vue.d.ts +1 -1
  97. package/dist/components/LpAddressInput.vue.js +1 -1
  98. package/dist/components/LpAlert.vue.d.ts +9 -0
  99. package/dist/components/LpAlert.vue.js +1 -1
  100. package/dist/components/LpAppShell.vue.d.ts +10 -0
  101. package/dist/components/LpAppShell.vue.js +1 -1
  102. package/dist/components/LpAutocomplete.vue.d.ts +31 -6
  103. package/dist/components/LpAutocomplete.vue.js +1 -1
  104. package/dist/components/LpAvatar.vue.d.ts +3 -3
  105. package/dist/components/LpAvatar.vue.js +1 -1
  106. package/dist/components/LpBadge.vue.d.ts +2 -2
  107. package/dist/components/LpBreadcrumbs.vue.d.ts +13 -9
  108. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  109. package/dist/components/LpButton.vue.d.ts +35 -98
  110. package/dist/components/LpButton.vue.js +1 -1
  111. package/dist/components/LpCalendar.vue.js +1 -1
  112. package/dist/components/LpCard.vue.js +1 -1
  113. package/dist/components/LpChart.vue.d.ts +47 -3
  114. package/dist/components/LpChart.vue.js +377 -97
  115. package/dist/components/LpCheckbox.vue.js +1 -1
  116. package/dist/components/LpCodeBlock.vue.d.ts +2 -0
  117. package/dist/components/LpCodeBlock.vue.js +1 -1
  118. package/dist/components/LpColorPicker.vue.d.ts +2 -2
  119. package/dist/components/LpColorPicker.vue.js +1 -1
  120. package/dist/components/LpCommandPalette.vue.d.ts +14 -3
  121. package/dist/components/LpCommandPalette.vue.js +142 -77
  122. package/dist/components/LpConfirmDialog.vue.d.ts +15 -3
  123. package/dist/components/LpConfirmDialog.vue.js +1 -1
  124. package/dist/components/LpConfirmHost.vue.d.ts +9 -0
  125. package/dist/components/LpConfirmHost.vue.js +4 -0
  126. package/dist/components/LpContextMenu.vue.d.ts +3 -5
  127. package/dist/components/LpContextMenu.vue.js +1 -1
  128. package/dist/components/LpDatePicker.vue.d.ts +24 -6
  129. package/dist/components/LpDatePicker.vue.js +1 -1
  130. package/dist/components/LpDateRangePicker.vue.d.ts +60 -0
  131. package/dist/components/LpDateRangePicker.vue.js +4 -0
  132. package/dist/components/LpDisclosure.vue.js +1 -1
  133. package/dist/components/LpDrawer.vue.d.ts +10 -0
  134. package/dist/components/LpDrawer.vue.js +1 -1
  135. package/dist/components/LpDropdownMenu.vue.js +1 -1
  136. package/dist/components/LpDropzone.vue.js +23 -7
  137. package/dist/components/LpEmptyState.vue.js +1 -1
  138. package/dist/components/LpFieldNotice.vue.d.ts +8 -0
  139. package/dist/components/LpFieldNotice.vue.js +4 -0
  140. package/dist/components/LpFileTree.vue.d.ts +11 -2
  141. package/dist/components/LpFileTree.vue.js +1 -1
  142. package/dist/components/LpFileTreeNode.vue.js +1 -1
  143. package/dist/components/LpFormField.vue.d.ts +13 -1
  144. package/dist/components/LpFormField.vue.js +1 -1
  145. package/dist/components/LpIcon.vue.d.ts +0 -1
  146. package/dist/components/LpIcon.vue.js +1 -1
  147. package/dist/components/LpImageEditor.vue.js +93 -58
  148. package/dist/components/LpInput.vue.d.ts +22 -3
  149. package/dist/components/LpInput.vue.js +1 -1
  150. package/dist/components/LpLightbox.vue.d.ts +17 -0
  151. package/dist/components/LpLightbox.vue.js +1 -1
  152. package/dist/components/LpLink.vue.d.ts +11 -11
  153. package/dist/components/LpLink.vue.js +1 -1
  154. package/dist/components/LpLogViewer.vue.d.ts +72 -3
  155. package/dist/components/LpLogViewer.vue.js +1 -1
  156. package/dist/components/LpMapPicker.vue.d.ts +4 -4
  157. package/dist/components/LpMapPicker.vue.js +1 -1
  158. package/dist/components/LpMediaGallery.vue.js +1 -1
  159. package/dist/components/LpModal.vue.d.ts +16 -6
  160. package/dist/components/LpModal.vue.js +1 -1
  161. package/dist/components/LpModalNav.vue.d.ts +0 -1
  162. package/dist/components/LpModalNav.vue.js +1 -1
  163. package/dist/components/LpNotificationBell.vue.d.ts +11 -2
  164. package/dist/components/LpNotificationBell.vue.js +1 -1
  165. package/dist/components/LpNumberField.vue.d.ts +2 -0
  166. package/dist/components/LpNumberField.vue.js +1 -1
  167. package/dist/components/LpNumberFlow.vue.js +1 -1
  168. package/dist/components/LpOtpInput.vue.js +1 -1
  169. package/dist/components/LpPagination.vue.d.ts +2 -0
  170. package/dist/components/LpPagination.vue.js +1 -1
  171. package/dist/components/LpPasswordInput.vue.js +1 -1
  172. package/dist/components/LpPhoneInput.vue.d.ts +13 -0
  173. package/dist/components/LpPhoneInput.vue.js +1 -1
  174. package/dist/components/LpPie.vue.d.ts +21 -2
  175. package/dist/components/LpPie.vue.js +1 -1
  176. package/dist/components/LpPopover.vue.d.ts +2 -0
  177. package/dist/components/LpPopover.vue.js +1 -1
  178. package/dist/components/LpProgress.vue.d.ts +1 -1
  179. package/dist/components/LpProgress.vue.js +1 -1
  180. package/dist/components/LpRadioGroup.vue.js +1 -1
  181. package/dist/components/LpRangeCalendar.vue.d.ts +51 -0
  182. package/dist/components/LpRangeCalendar.vue.js +4 -0
  183. package/dist/components/LpRollingText.vue.d.ts +1 -1
  184. package/dist/components/LpRollingText.vue.js +1 -1
  185. package/dist/components/LpScrollArea.vue.d.ts +2 -2
  186. package/dist/components/LpScrollArea.vue.js +1 -1
  187. package/dist/components/LpSection.vue.d.ts +1 -1
  188. package/dist/components/LpSection.vue.js +1 -1
  189. package/dist/components/LpSegmented.vue.d.ts +5 -0
  190. package/dist/components/LpSegmented.vue.js +1 -1
  191. package/dist/components/LpSelect.vue.d.ts +1 -0
  192. package/dist/components/LpSelect.vue.js +1 -1
  193. package/dist/components/LpShortcutsDialog.vue.d.ts +7 -1
  194. package/dist/components/LpShortcutsDialog.vue.js +60 -15
  195. package/dist/components/LpSidebar.vue.d.ts +6 -2
  196. package/dist/components/LpSidebar.vue.js +1 -1
  197. package/dist/components/LpSidebarNav.vue.js +1 -1
  198. package/dist/components/LpSkeleton.vue.js +1 -1
  199. package/dist/components/LpSlider.vue.js +1 -1
  200. package/dist/components/LpSparkline.vue.d.ts +1 -0
  201. package/dist/components/LpSparkline.vue.js +1 -1
  202. package/dist/components/LpStat.vue.js +1 -1
  203. package/dist/components/LpStepper.vue.js +1 -1
  204. package/dist/components/LpSwitch.vue.js +1 -1
  205. package/dist/components/LpTable.vue.d.ts +107 -3
  206. package/dist/components/LpTable.vue.js +540 -128
  207. package/dist/components/LpTableFilter.vue.d.ts +31 -0
  208. package/dist/components/LpTableFilter.vue.js +4 -0
  209. package/dist/components/LpTableOfContents.vue.d.ts +4 -2
  210. package/dist/components/LpTableOfContents.vue.js +1 -1
  211. package/dist/components/LpTabs.vue.d.ts +2 -0
  212. package/dist/components/LpTabs.vue.js +1 -1
  213. package/dist/components/LpTagsInput.vue.d.ts +77 -0
  214. package/dist/components/LpTagsInput.vue.js +4 -0
  215. package/dist/components/LpTextarea.vue.d.ts +19 -6
  216. package/dist/components/LpTextarea.vue.js +1 -1
  217. package/dist/components/LpThemeProvider.vue.d.ts +22 -0
  218. package/dist/components/LpThemeProvider.vue.js +4 -0
  219. package/dist/components/LpThemeSwitcher.vue.d.ts +15 -2
  220. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  221. package/dist/components/LpTilt.vue.js +1 -1
  222. package/dist/components/LpTimeField.vue.js +1 -1
  223. package/dist/components/LpToaster.vue.d.ts +6 -1
  224. package/dist/components/LpToaster.vue.js +1 -1
  225. package/dist/components/LpTooltip.vue.d.ts +1 -2
  226. package/dist/components/LpTooltip.vue.js +1 -1
  227. package/dist/components/LpTooltipProvider.vue.d.ts +22 -0
  228. package/dist/components/LpTooltipProvider.vue.js +4 -0
  229. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  230. package/dist/components/LpUptimeBar.vue.d.ts +4 -0
  231. package/dist/components/LpUptimeBar.vue.js +1 -1
  232. package/dist/components/chart.d.ts +32 -1
  233. package/dist/components/chart.js +42 -6
  234. package/dist/components/codeHighlight.d.ts +6 -0
  235. package/dist/components/codeHighlight.js +92 -20
  236. package/dist/components/color.d.ts +1 -1
  237. package/dist/components/color.js +20 -9
  238. package/dist/components/commandSearch.js +3 -0
  239. package/dist/components/confirm.d.ts +25 -0
  240. package/dist/components/confirm.js +26 -0
  241. package/dist/components/countries.d.ts +6 -0
  242. package/dist/components/countries.js +7 -0
  243. package/dist/components/dateRange.d.ts +43 -0
  244. package/dist/components/dateRange.js +76 -0
  245. package/dist/components/exportData.d.ts +58 -0
  246. package/dist/components/exportData.js +165 -0
  247. package/dist/components/fileTree.js +16 -7
  248. package/dist/components/imageCrop.d.ts +42 -0
  249. package/dist/components/imageCrop.js +47 -0
  250. package/dist/components/link.d.ts +12 -0
  251. package/dist/components/link.js +19 -0
  252. package/dist/components/logSearch.d.ts +18 -0
  253. package/dist/components/logSearch.js +36 -0
  254. package/dist/components/modalDepth.d.ts +18 -2
  255. package/dist/components/modalDepth.js +17 -5
  256. package/dist/components/otp.d.ts +5 -0
  257. package/dist/components/otp.js +10 -0
  258. package/dist/components/phone.d.ts +41 -0
  259. package/dist/components/phone.js +80 -0
  260. package/dist/components/sidebar.d.ts +7 -1
  261. package/dist/components/tableFilter.d.ts +36 -0
  262. package/dist/components/tableFilter.js +84 -0
  263. package/dist/components/tableSort.d.ts +8 -0
  264. package/dist/components/tableSort.js +31 -0
  265. package/dist/components/tags.d.ts +20 -0
  266. package/dist/components/tags.js +37 -0
  267. package/dist/composables/useField.d.ts +38 -0
  268. package/dist/composables/useField.js +45 -0
  269. package/dist/composables/useFieldNotice.d.ts +17 -0
  270. package/dist/composables/useFieldNotice.js +28 -0
  271. package/dist/composables/useForm.d.ts +53 -0
  272. package/dist/composables/useForm.js +207 -0
  273. package/dist/composables/useFormDraft.d.ts +10 -3
  274. package/dist/composables/useFormDraft.js +9 -14
  275. package/dist/composables/useHotkeys.d.ts +81 -5
  276. package/dist/composables/useHotkeys.js +223 -25
  277. package/dist/composables/useInputFilter.d.ts +17 -0
  278. package/dist/composables/useInputFilter.js +47 -10
  279. package/dist/composables/useInputMask.d.ts +59 -0
  280. package/dist/composables/useInputMask.js +170 -0
  281. package/dist/composables/useLpConfig.d.ts +24 -0
  282. package/dist/composables/useLpConfig.js +44 -0
  283. package/dist/composables/usePaintSelect.d.ts +22 -0
  284. package/dist/composables/usePaintSelect.js +169 -0
  285. package/dist/composables/useRovingFocus.js +7 -0
  286. package/dist/composables/useShift.js +7 -2
  287. package/dist/composables/useTilt.d.ts +2 -2
  288. package/dist/composables/useTilt.js +5 -6
  289. package/dist/composables/useToast.d.ts +20 -0
  290. package/dist/composables/useToast.js +28 -4
  291. package/dist/composables/useZoomPan.d.ts +3 -3
  292. package/dist/composables/useZoomPan.js +4 -4
  293. package/dist/icons-all.d.ts +1 -0
  294. package/dist/icons-all.js +3 -0
  295. package/dist/icons.d.ts +14 -1
  296. package/dist/index.d.ts +60 -4
  297. package/dist/index.js +245 -141
  298. package/dist/kit-icons.d.ts +2 -0
  299. package/dist/layout/tree.d.ts +2 -0
  300. package/dist/layout/tree.js +16 -1
  301. package/dist/layout/useLayout.d.ts +15 -0
  302. package/dist/layout/useLayout.js +41 -14
  303. package/dist/theme/backdrop.d.ts +12 -0
  304. package/dist/theme/presets.d.ts +0 -2
  305. package/dist/theme/useTheme.d.ts +32 -9
  306. package/package.json +18 -8
  307. package/src/component-names.ts +9 -7
  308. package/src/components/ContextMenuItems.vue +88 -0
  309. package/src/components/LayoutCanvas.vue +26 -6
  310. package/src/components/LayoutNode.vue +30 -4
  311. package/src/components/LpActionBar.vue +31 -8
  312. package/src/components/LpAddressInput.vue +57 -41
  313. package/src/components/LpAlert.vue +16 -3
  314. package/src/components/LpAppShell.vue +45 -12
  315. package/src/components/LpAutocomplete.vue +47 -10
  316. package/src/components/LpAvatar.vue +20 -6
  317. package/src/components/LpBreadcrumbs.vue +65 -41
  318. package/src/components/LpButton.vue +60 -14
  319. package/src/components/LpChart.vue +351 -28
  320. package/src/components/LpCheckbox.vue +7 -3
  321. package/src/components/LpCodeBlock.vue +50 -14
  322. package/src/components/LpColorPicker.vue +73 -10
  323. package/src/components/LpCommandPalette.vue +115 -20
  324. package/src/components/LpConfirmDialog.vue +68 -17
  325. package/src/components/LpConfirmHost.vue +59 -0
  326. package/src/components/LpContextMenu.vue +15 -116
  327. package/src/components/LpDatePicker.vue +104 -68
  328. package/src/components/LpDateRangePicker.vue +224 -0
  329. package/src/components/LpDrawer.vue +56 -19
  330. package/src/components/LpDropdownMenu.vue +1 -0
  331. package/src/components/LpDropzone.vue +21 -4
  332. package/src/components/LpFieldNotice.vue +65 -0
  333. package/src/components/LpFileTree.vue +33 -7
  334. package/src/components/LpFormField.vue +59 -7
  335. package/src/components/LpIcon.vue +22 -4
  336. package/src/components/LpImageEditor.vue +96 -57
  337. package/src/components/LpInput.vue +62 -11
  338. package/src/components/LpLightbox.vue +140 -29
  339. package/src/components/LpLink.vue +24 -13
  340. package/src/components/LpLogViewer.vue +311 -39
  341. package/src/components/LpMapPicker.vue +36 -13
  342. package/src/components/LpMediaGallery.vue +8 -2
  343. package/src/components/LpModal.vue +54 -28
  344. package/src/components/LpModalNav.vue +5 -2
  345. package/src/components/LpNotificationBell.vue +77 -23
  346. package/src/components/LpNumberField.vue +21 -6
  347. package/src/components/LpNumberFlow.vue +26 -10
  348. package/src/components/LpOtpInput.vue +43 -8
  349. package/src/components/LpPagination.vue +23 -7
  350. package/src/components/LpPasswordInput.vue +4 -1
  351. package/src/components/LpPhoneInput.vue +224 -39
  352. package/src/components/LpPie.vue +102 -20
  353. package/src/components/LpPopover.vue +4 -0
  354. package/src/components/LpProgress.vue +5 -3
  355. package/src/components/LpRadioGroup.vue +9 -2
  356. package/src/components/LpRangeCalendar.vue +365 -0
  357. package/src/components/LpRollingText.vue +3 -0
  358. package/src/components/LpScrollArea.vue +22 -2
  359. package/src/components/LpSegmented.vue +12 -2
  360. package/src/components/LpSelect.vue +0 -0
  361. package/src/components/LpShortcutsDialog.vue +83 -9
  362. package/src/components/LpSidebar.vue +14 -9
  363. package/src/components/LpSidebarNav.vue +17 -5
  364. package/src/components/LpSkeleton.vue +4 -1
  365. package/src/components/LpSlider.vue +7 -1
  366. package/src/components/LpSparkline.vue +11 -3
  367. package/src/components/LpStepper.vue +23 -10
  368. package/src/components/LpSwitch.vue +31 -8
  369. package/src/components/LpTable.vue +668 -90
  370. package/src/components/LpTableFilter.vue +175 -0
  371. package/src/components/LpTableOfContents.vue +50 -32
  372. package/src/components/LpTabs.vue +23 -6
  373. package/src/components/LpTagsInput.vue +364 -0
  374. package/src/components/LpTextarea.vue +44 -6
  375. package/src/components/LpThemeProvider.vue +41 -0
  376. package/src/components/LpThemeSwitcher.vue +76 -33
  377. package/src/components/LpTilt.vue +2 -2
  378. package/src/components/LpTimeField.vue +14 -3
  379. package/src/components/LpToaster.vue +115 -27
  380. package/src/components/LpTooltip.vue +29 -5
  381. package/src/components/LpTooltipProvider.vue +24 -0
  382. package/src/components/LpTopologyCanvas.vue +19 -2
  383. package/src/components/LpUptimeBar.vue +46 -8
  384. package/src/components/chart.ts +82 -8
  385. package/src/components/codeHighlight.ts +111 -15
  386. package/src/components/color.ts +30 -9
  387. package/src/components/commandSearch.ts +6 -0
  388. package/src/components/confirm.ts +58 -0
  389. package/src/components/countries.ts +12 -0
  390. package/src/components/dateRange.ts +134 -0
  391. package/src/components/exportData.ts +228 -0
  392. package/src/components/fileTree.ts +19 -7
  393. package/src/components/imageCrop.ts +107 -0
  394. package/src/components/link.ts +34 -0
  395. package/src/components/logSearch.ts +46 -0
  396. package/src/components/modalDepth.ts +48 -6
  397. package/src/components/otp.ts +16 -0
  398. package/src/components/phone.ts +140 -0
  399. package/src/components/sidebar.ts +7 -1
  400. package/src/components/tableFilter.ts +122 -0
  401. package/src/components/tableSort.ts +37 -0
  402. package/src/components/tags.ts +62 -0
  403. package/src/composables/useField.ts +83 -0
  404. package/src/composables/useFieldNotice.ts +59 -0
  405. package/src/composables/useForm.ts +314 -0
  406. package/src/composables/useFormDraft.ts +25 -20
  407. package/src/composables/useHotkeys.ts +355 -28
  408. package/src/composables/useInputFilter.ts +77 -14
  409. package/src/composables/useInputMask.ts +254 -0
  410. package/src/composables/useLpConfig.ts +87 -0
  411. package/src/composables/usePaintSelect.ts +243 -0
  412. package/src/composables/useRovingFocus.ts +11 -0
  413. package/src/composables/useShift.ts +13 -2
  414. package/src/composables/useTilt.ts +5 -6
  415. package/src/composables/useToast.ts +56 -4
  416. package/src/composables/useZoomPan.ts +6 -6
  417. package/src/iconSubset.d.mts +10 -0
  418. package/src/iconSubset.mjs +51 -0
  419. package/src/icons-all.ts +10 -0
  420. package/src/icons.ts +59 -8
  421. package/src/index.ts +73 -4
  422. package/src/kit-icons.ts +5 -0
  423. package/src/layout/tree.ts +30 -0
  424. package/src/layout/useLayout.ts +68 -18
  425. package/src/nuxt.ts +87 -17
  426. package/src/runtime/config.plugin.ts +14 -0
  427. package/src/runtime/theme.plugin.ts +43 -23
  428. package/src/theme/backdrop.ts +69 -0
  429. package/src/theme/presets.ts +6 -64
  430. package/src/theme/useTheme.ts +206 -100
  431. package/src/tokens/tokens.css +46 -0
  432. package/dist/chunks/LpBreadcrumbs-p4jHe_V-.js +0 -62
  433. package/dist/chunks/LpContextMenu-CwW1QXe_.js +0 -217
  434. package/dist/chunks/LpDatePicker-CwOToxGq.js +0 -164
  435. package/dist/chunks/LpFormField-CazBc2-a.js +0 -62
  436. package/dist/chunks/LpPhoneInput-BDSKVl_z.js +0 -209
  437. package/dist/chunks/LpTextarea-t7F6gv8E.js +0 -47
@@ -0,0 +1,224 @@
1
+ <script lang="ts">
2
+ export default { inheritAttrs: false }
3
+ </script>
4
+
5
+ <script setup lang="ts">
6
+ /*
7
+ * Range field + popover: presets on one side, a range calendar on the other.
8
+ * The model is `{ start, end }` of ISO days, like LpDatePicker's single day.
9
+ * Picking the second end or a preset fills the field and closes the popover.
10
+ * Two months sit side by side where there is room, one on a phone.
11
+ */
12
+ import { PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from "reka-ui"
13
+ import { computed, onBeforeUnmount, onMounted, ref, useAttrs } from "vue"
14
+ import { useFieldControl } from "../composables/useField"
15
+ import { useLabels } from "../composables/useLpConfig"
16
+ import { mergeClass } from "../composables/useMergedClass"
17
+ import {
18
+ clampRange,
19
+ type DateRange,
20
+ type DateRangePreset,
21
+ defaultRangePresets,
22
+ formatRange,
23
+ normalizeRange,
24
+ sameRange,
25
+ } from "./dateRange"
26
+ import { POPOVER_PANEL } from "./dropdown"
27
+ import LpIcon from "./LpIcon.vue"
28
+ import LpRollingText from "./LpRollingText.vue"
29
+ import LpRangeCalendar, { type RangeChange } from "./LpRangeCalendar.vue"
30
+
31
+ export interface DateRangePickerLabels extends Record<string, string> {
32
+ placeholder: string
33
+ clear: string
34
+ presets: string
35
+ today: string
36
+ last7: string
37
+ last30: string
38
+ thisMonth: string
39
+ lastMonth: string
40
+ }
41
+
42
+ const props = withDefaults(
43
+ defineProps<{
44
+ /** Selected range (v-model). */
45
+ modelValue?: DateRange
46
+ placeholder?: string
47
+ min?: string
48
+ max?: string
49
+ isDisabled?: (iso: string) => boolean
50
+ isDateUnavailable?: (iso: string) => boolean
51
+ /** Longest allowed range, in days. */
52
+ maximumDays?: number
53
+ /** Shortcut ranges; an empty list hides them. Defaults to the built-in set. */
54
+ presets?: DateRangePreset[]
55
+ clearable?: boolean
56
+ disabled?: boolean
57
+ invalid?: boolean
58
+ size?: "sm" | "md" | "lg"
59
+ locale?: string
60
+ /** Intl format for the displayed ends. */
61
+ format?: Intl.DateTimeFormatOptions
62
+ labels?: Partial<DateRangePickerLabels>
63
+ }>(),
64
+ { size: "md" },
65
+ )
66
+
67
+ const emit = defineEmits<{
68
+ (e: "update:modelValue", value: DateRange | undefined): void
69
+ }>()
70
+
71
+ const text = useLabels<DateRangePickerLabels>(
72
+ "dateRangePicker",
73
+ {
74
+ placeholder: "Pick a date range",
75
+ clear: "Clear",
76
+ presets: "Presets",
77
+ today: "Today",
78
+ last7: "Last 7 days",
79
+ last30: "Last 30 days",
80
+ thisMonth: "This month",
81
+ lastMonth: "Last month",
82
+ },
83
+ () => props.labels,
84
+ )
85
+
86
+ const open = ref(false)
87
+ const value = computed(() => normalizeRange(props.modelValue))
88
+ const display = computed(() => (value.value ? formatRange(value.value, props.locale, props.format) : ""))
89
+
90
+ const presetList = computed<DateRangePreset[]>(() => {
91
+ return props.presets ?? defaultRangePresets(text.value)
92
+ })
93
+
94
+ /*
95
+ * A range picked on the calendar keeps the popover open: the field already
96
+ * shows it, and the user can still pull either end before clicking away.
97
+ * Closing on the second click left no moment to adjust. A preset is a whole
98
+ * answer, so it closes.
99
+ */
100
+ function pick(range: DateRange, change?: RangeChange) {
101
+ emit("update:modelValue", range)
102
+ if (!change) open.value = false
103
+ }
104
+
105
+ function pickPreset(preset: DateRangePreset) {
106
+ const range = clampRange(preset.range(), props.min, props.max)
107
+ if (range) pick(range)
108
+ }
109
+
110
+ function presetFits(preset: DateRangePreset): boolean {
111
+ return !!clampRange(preset.range(), props.min, props.max)
112
+ }
113
+
114
+ const wide = ref(false)
115
+ let query: MediaQueryList | undefined
116
+ const onQuery = () => {
117
+ wide.value = !!query?.matches
118
+ }
119
+ onMounted(() => {
120
+ query = window.matchMedia("(min-width: 640px)")
121
+ onQuery()
122
+ query.addEventListener("change", onQuery)
123
+ })
124
+ onBeforeUnmount(() => query?.removeEventListener("change", onQuery))
125
+
126
+ const shellSize = {
127
+ sm: "h-(--size-control-sm) text-xs",
128
+ md: "h-(--size-control-md) text-sm",
129
+ lg: "h-(--size-control-lg) text-sm",
130
+ }
131
+
132
+ const attrs = useAttrs()
133
+ const field = useFieldControl(() => ({
134
+ wrapped: false,
135
+ id: typeof attrs.id === "string" ? attrs.id : undefined,
136
+ invalid: props.invalid,
137
+ disabled: props.disabled,
138
+ }))
139
+ const showClear = computed(() => props.clearable && !!value.value && !field.value.disabled)
140
+ </script>
141
+
142
+ <template>
143
+ <div :class="mergeClass('relative w-full', attrs.class as string)" :style="attrs.style as never">
144
+ <PopoverRoot v-model:open="open">
145
+ <PopoverTrigger as-child>
146
+ <button
147
+ v-bind="{ ...attrs, class: undefined, style: undefined }"
148
+ :id="field.id"
149
+ type="button"
150
+ :disabled="field.disabled"
151
+ :aria-invalid="field.invalid || undefined"
152
+ :aria-describedby="field.describedBy"
153
+ class="group flex w-full items-center gap-2 rounded-control border bg-surface-soft px-3 text-left text-ink outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring data-[state=open]:border-brand disabled:cursor-not-allowed disabled:opacity-55"
154
+ :class="[
155
+ shellSize[size],
156
+ field.invalid ? 'border-danger focus-visible:ring-danger-soft' : 'border-line focus-visible:border-brand',
157
+ ]"
158
+ >
159
+ <LpIcon name="lucide:calendar-range" :size="16" class="shrink-0 text-muted" />
160
+ <LpRollingText
161
+ :value="display || placeholder || text.placeholder"
162
+ align="start"
163
+ class="min-w-0 flex-1"
164
+ :class="display ? '' : 'text-muted'"
165
+ />
166
+ <span v-if="showClear" class="w-[15px] shrink-0" aria-hidden="true" />
167
+ <LpIcon
168
+ name="lucide:chevron-down"
169
+ :size="15"
170
+ class="shrink-0 text-muted transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-data-[state=open]:rotate-180 motion-reduce:transition-none"
171
+ />
172
+ </button>
173
+ </PopoverTrigger>
174
+ <PopoverPortal>
175
+ <PopoverContent
176
+ :side-offset="6"
177
+ align="start"
178
+ :collision-padding="8"
179
+ :class="[POPOVER_PANEL, 'z-(--z-popover) flex max-w-[calc(100vw-1rem)] flex-col rounded-card p-0 outline-none sm:flex-row']"
180
+ >
181
+ <div
182
+ v-if="presetList.length"
183
+ role="group"
184
+ :aria-label="text.presets"
185
+ class="flex gap-1 overflow-x-auto border-b border-line p-2 sm:w-40 sm:flex-col sm:overflow-visible sm:border-r sm:border-b-0"
186
+ >
187
+ <button
188
+ v-for="(preset, i) in presetList"
189
+ :key="`${i}:${preset.label}`"
190
+ type="button"
191
+ :disabled="!presetFits(preset)"
192
+ :aria-pressed="sameRange(value, preset.range())"
193
+ class="shrink-0 rounded-control px-2.5 py-1.5 text-left text-sm whitespace-nowrap text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:bg-surface-soft hover:text-ink focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40 aria-pressed:bg-brand-soft aria-pressed:text-ink"
194
+ @click="pickPreset(preset)"
195
+ >
196
+ {{ preset.label }}
197
+ </button>
198
+ </div>
199
+ <LpRangeCalendar
200
+ :model-value="value"
201
+ :min="min"
202
+ :max="max"
203
+ :is-disabled="isDisabled"
204
+ :is-date-unavailable="isDateUnavailable"
205
+ :maximum-days="maximumDays"
206
+ :months="wide ? 2 : 1"
207
+ :locale="locale"
208
+ class="border-0 bg-transparent"
209
+ @update:model-value="pick"
210
+ />
211
+ </PopoverContent>
212
+ </PopoverPortal>
213
+ </PopoverRoot>
214
+ <button
215
+ v-if="showClear"
216
+ type="button"
217
+ :aria-label="text.clear"
218
+ class="absolute top-1/2 right-[calc(0.75rem+15px+0.5rem)] flex -translate-y-1/2 items-center rounded-sm text-muted outline-none transition-colors hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
219
+ @click="emit('update:modelValue', undefined)"
220
+ >
221
+ <LpIcon name="lucide:x" :size="15" />
222
+ </button>
223
+ </div>
224
+ </template>
@@ -1,8 +1,11 @@
1
1
  <script lang="ts">
2
- import { ref as sharedRef } from "vue"
2
+ import { ref as sharedRef, type Ref } from "vue"
3
3
 
4
- // One pull at a time across every edge-open drawer on the page.
5
- const edgeOwner = sharedRef<symbol | null>(null)
4
+ // One pull at a time across every edge-open drawer on the page, pinned to a
5
+ // global symbol so a barrel and a deep import still share it.
6
+ const EDGE_KEY = Symbol.for("@leavepulse/ui:drawer-edge-owner")
7
+ type GlobalWithEdge = typeof globalThis & { [EDGE_KEY]?: Ref<symbol | null> }
8
+ const edgeOwner = ((globalThis as GlobalWithEdge)[EDGE_KEY] ??= sharedRef<symbol | null>(null))
6
9
  </script>
7
10
 
8
11
  <script setup lang="ts">
@@ -32,6 +35,7 @@ import {
32
35
  import { computed, onBeforeUnmount, ref, useSlots, watch } from "vue"
33
36
  import { CLOSE_ICON } from "./dropdown"
34
37
  import { useGlass } from "../composables/useGlass"
38
+ import { useLabels } from "../composables/useLpConfig"
35
39
  import LpIcon from "./LpIcon.vue"
36
40
  import LpScrollArea from "./LpScrollArea.vue"
37
41
  import { anyModalOpen, useModalLayer } from "./modalDepth"
@@ -109,6 +113,9 @@ const props = withDefaults(
109
113
  * `provideGlass`, and without one it is off.
110
114
  */
111
115
  glass?: boolean
116
+ /** Accessible name for a drawer without a visible `title`. */
117
+ ariaLabel?: string
118
+ labels?: Partial<{ close: string }>
112
119
  }>(),
113
120
  {
114
121
  side: "right",
@@ -124,6 +131,7 @@ const props = withDefaults(
124
131
  )
125
132
 
126
133
  const frosted = useGlass(() => props.glass)
134
+ const text = useLabels("drawer", { close: "Close" }, () => props.labels)
127
135
 
128
136
  const emit = defineEmits<{
129
137
  (e: "update:open", value: boolean): void
@@ -148,7 +156,8 @@ const SIZES = { sm: 22, md: 28, lg: 36, xl: 48 } // rem along the main axis
148
156
  * settings drawer) claimed those same rungs and the tie went to DOM order —
149
157
  * the drawer, portalled later on reopen, covered the dialog it had opened.
150
158
  */
151
- const { claim, release, layer } = useModalLayer()
159
+ const contentRef = ref<{ $el: HTMLElement } | null>(null)
160
+ const { claim, release, layer } = useModalLayer({ element: () => contentRef.value?.$el })
152
161
  watch(() => props.open, (open) => (open ? claim() : release()), { immediate: true })
153
162
  onBeforeUnmount(release)
154
163
 
@@ -179,36 +188,60 @@ const sizeStyle = computed(() => {
179
188
  return isHorizontal.value ? { width: len } : { height: len }
180
189
  })
181
190
 
191
+ // Resolve a CSS length to px against the viewport. Anything it cannot read
192
+ // (calc(), min()) comes back NaN for the caller to fall back on.
193
+ function toPx(css: string): number {
194
+ const n = Number.parseFloat(css)
195
+ if (css.endsWith("rem")) {
196
+ const root = typeof document === "undefined" ? 16 : Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16
197
+ return n * root
198
+ }
199
+ if (css.endsWith("vw")) return (n / 100) * window.innerWidth
200
+ if (css.endsWith("vh")) return (n / 100) * window.innerHeight
201
+ if (css.endsWith("px") || /^[\d.]+$/.test(css.trim())) return n
202
+ return Number.NaN
203
+ }
204
+
182
205
  // Drag the inner edge to resize. For a right drawer the panel grows as the
183
- // pointer moves left (width = viewportRight − clientX); mirror for left.
206
+ // pointer moves left (width = viewportRight − clientX); mirror for left. The
207
+ // pointer is captured, so a release over an iframe or outside the window still
208
+ // ends the drag, and a cancelled touch ends it too.
209
+ let endResize: (() => void) | null = null
210
+
184
211
  function startResize(e: PointerEvent) {
185
212
  if (!props.resizable || !isHorizontal.value) return
186
213
  e.preventDefault()
214
+ endResize?.()
215
+ const grip = e.currentTarget instanceof HTMLElement ? e.currentTarget : null
216
+ grip?.setPointerCapture(e.pointerId)
187
217
  const fromRight = dir.value === "right"
188
- const toPx = (css: string) => {
189
- // Resolve rem/vw to px against the viewport for clamping.
190
- if (css.endsWith("rem")) return Number.parseFloat(css) * 16
191
- if (css.endsWith("vw")) return (Number.parseFloat(css) / 100) * window.innerWidth
192
- return Number.parseFloat(css)
193
- }
194
218
  const min = toPx(props.minSize)
195
219
  const max = toPx(props.maxSize)
196
220
  const onMove = (ev: PointerEvent) => {
221
+ if (ev.pointerId !== e.pointerId) return
197
222
  const raw = fromRight ? window.innerWidth - ev.clientX : ev.clientX
198
- resizedPx.value = Math.max(min, Math.min(max, raw))
223
+ resizedPx.value = Math.max(Number.isNaN(min) ? 0 : min, Math.min(Number.isNaN(max) ? Infinity : max, raw))
199
224
  }
200
- const onUp = () => {
225
+ const end = () => {
201
226
  window.removeEventListener("pointermove", onMove)
202
- window.removeEventListener("pointerup", onUp)
227
+ window.removeEventListener("pointerup", end)
228
+ window.removeEventListener("pointercancel", end)
229
+ grip?.removeEventListener("lostpointercapture", end)
203
230
  document.body.style.userSelect = ""
204
231
  document.body.style.cursor = ""
232
+ endResize = null
205
233
  }
234
+ endResize = end
206
235
  document.body.style.userSelect = "none"
207
236
  document.body.style.cursor = "ew-resize"
208
237
  window.addEventListener("pointermove", onMove)
209
- window.addEventListener("pointerup", onUp)
238
+ window.addEventListener("pointerup", end)
239
+ window.addEventListener("pointercancel", end)
240
+ grip?.addEventListener("lostpointercapture", end)
210
241
  }
211
242
 
243
+ onBeforeUnmount(() => endResize?.())
244
+
212
245
  const slots = useSlots()
213
246
  const hasHeader = computed(() => Boolean(props.title || slots.title))
214
247
 
@@ -281,9 +314,9 @@ watch(
281
314
  // Resolve the panel's main-axis size in px to map drag distance → progress.
282
315
  function panelPx(): number {
283
316
  const vw = typeof window === "undefined" ? 0 : window.innerWidth
284
- // mirror sizeStyle's min(94vw, SIZES[size]rem); 1rem≈16px.
285
- const cap = props.width ? Number.parseFloat(props.width) : SIZES[props.size] * 16
286
- return Math.min(vw * 0.94, Number.isFinite(cap) ? cap : SIZES[props.size] * 16)
317
+ const preset = toPx(`${SIZES[props.size]}rem`)
318
+ const cap = props.width ? toPx(props.width) : preset
319
+ return Math.min(vw * 0.94, Number.isFinite(cap) ? cap : preset)
287
320
  }
288
321
 
289
322
  function edgeEnabled(): boolean {
@@ -479,6 +512,8 @@ const previewStyle = computed(() => {
479
512
  :class="{ 'lp-scrim--light': frosted, 'lp-scrim--nested': layer.nested }"
480
513
  />
481
514
  <DrawerContent
515
+ ref="contentRef"
516
+ v-bind="description ? {} : { 'aria-describedby': undefined }"
482
517
  :class="contentClass"
483
518
  :style="[sizeStyle, { zIndex: layer.panel }]"
484
519
  :data-vaul-animate="handingOff || skipOpenSlide ? 'false' : undefined"
@@ -498,6 +533,8 @@ const previewStyle = computed(() => {
498
533
  />
499
534
  </div>
500
535
 
536
+ <DrawerTitle v-if="!hasHeader && ariaLabel" class="sr-only">{{ ariaLabel }}</DrawerTitle>
537
+
501
538
  <!-- Drag handle: a pill the user grabs; vaul wires the drag to it. -->
502
539
  <DrawerHandle
503
540
  v-if="showHandle"
@@ -519,7 +556,7 @@ const previewStyle = computed(() => {
519
556
  </div>
520
557
  <DrawerClose
521
558
  class="group flex shrink-0 items-center rounded-md p-1 text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
522
- aria-label="Close"
559
+ :aria-label="text.close"
523
560
  >
524
561
  <LpIcon
525
562
  name="lucide:x"
@@ -28,6 +28,7 @@ defineProps<{ items: MenuItem[] }>()
28
28
  </DropdownMenuTrigger>
29
29
  <DropdownMenuPortal>
30
30
  <DropdownMenuContent
31
+ :collision-padding="8"
31
32
  :side-offset="6"
32
33
  align="end"
33
34
  :class="[POPOVER_PANEL, 'z-(--z-popover) min-w-44 p-1 outline-none']"
@@ -23,7 +23,7 @@ export default { inheritAttrs: false }
23
23
  * invisible to a keyboard: nothing focusable, nothing that Enter activates, and
24
24
  * the file dialog unreachable without a mouse.
25
25
  */
26
- import { onBeforeUnmount, onMounted, ref } from "vue"
26
+ import { onBeforeUnmount, onMounted, ref, watch } from "vue"
27
27
  import { useMergedAttrs } from "../composables/useMergedClass"
28
28
  import { useReveal } from "../composables/useReveal"
29
29
  import LpIcon from "./LpIcon.vue"
@@ -168,17 +168,34 @@ function onPicked(event: Event) {
168
168
  }
169
169
 
170
170
  function windowPaste(event: ClipboardEvent) {
171
- if (props.disabled) return
171
+ // Already taken by the zone itself, which the paste bubbled up from.
172
+ if (props.disabled || event.defaultPrevented) return
172
173
  const target = event.target as HTMLElement | null
173
174
  // A paste aimed at somewhere you can type belongs to that thing.
174
175
  if (target?.closest("input, textarea, [contenteditable]")) return
175
176
  onPaste(event)
176
177
  }
177
178
 
179
+ let listening = false
180
+ function listen(on: boolean) {
181
+ if (on === listening || typeof window === "undefined") return
182
+ listening = on
183
+ if (on) window.addEventListener("paste", windowPaste)
184
+ else window.removeEventListener("paste", windowPaste)
185
+ }
186
+ let mounted = false
178
187
  onMounted(() => {
179
- if (props.pasteTarget) window.addEventListener("paste", windowPaste)
188
+ mounted = true
189
+ listen(props.pasteTarget)
190
+ })
191
+ watch(
192
+ () => props.pasteTarget,
193
+ (on) => mounted && listen(on),
194
+ )
195
+ onBeforeUnmount(() => {
196
+ mounted = false
197
+ listen(false)
180
198
  })
181
- onBeforeUnmount(() => window.removeEventListener("paste", windowPaste))
182
199
 
183
200
  defineExpose({ browse: () => input.value?.click() })
184
201
 
@@ -0,0 +1,65 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ * The floating note from useFieldNotice: anchored under the nearest
4
+ * `relative` box, over whatever sits below it (usually the hint), so it never
5
+ * moves the layout. It slides in, slides out, and nudges sideways when the
6
+ * same note is raised again while showing.
7
+ */
8
+ import { ref, watch } from "vue"
9
+ import { prefersReducedMotion } from "../composables/easing"
10
+ import type { FieldNotice } from "../composables/useFieldNotice"
11
+ import LpIcon from "./LpIcon.vue"
12
+
13
+ const props = defineProps<{
14
+ notice: FieldNotice
15
+ id?: string
16
+ }>()
17
+
18
+ const el = ref<HTMLElement | null>(null)
19
+
20
+ watch(
21
+ () => props.notice.seq.value,
22
+ (_, before) => {
23
+ if (before === undefined || !el.value || prefersReducedMotion()) return
24
+ el.value.animate(
25
+ [
26
+ { transform: "translateX(0)" },
27
+ { transform: "translateX(-3px)" },
28
+ { transform: "translateX(3px)" },
29
+ { transform: "translateX(0)" },
30
+ ],
31
+ { duration: 240, easing: "ease-out" },
32
+ )
33
+ },
34
+ )
35
+ </script>
36
+
37
+ <template>
38
+ <Transition
39
+ enter-active-class="transition-[opacity,translate] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none"
40
+ leave-active-class="transition-[opacity,translate] duration-fast ease-out motion-reduce:transition-none"
41
+ enter-from-class="-translate-y-1 opacity-0"
42
+ leave-to-class="-translate-y-1 opacity-0"
43
+ >
44
+ <div
45
+ v-if="notice.message.value"
46
+ :id="id"
47
+ ref="el"
48
+ role="status"
49
+ class="pointer-events-none absolute top-full left-0 z-20 mt-1.5 flex max-w-full items-center gap-1.5 rounded-control border px-2 py-1 text-xs shadow-sm"
50
+ :class="
51
+ notice.tone.value === 'danger'
52
+ ? 'border-danger/40 bg-surface-raised bg-[linear-gradient(var(--color-danger-soft),var(--color-danger-soft))] text-danger'
53
+ : 'border-line bg-surface-raised text-ink'
54
+ "
55
+ >
56
+ <LpIcon
57
+ :name="notice.tone.value === 'danger' ? 'lucide:circle-alert' : 'lucide:info'"
58
+ :size="13"
59
+ class="shrink-0"
60
+ :class="notice.tone.value === 'danger' ? '' : 'text-muted'"
61
+ />
62
+ <span class="truncate">{{ notice.message.value }}</span>
63
+ </div>
64
+ </Transition>
65
+ </template>
@@ -38,10 +38,21 @@ import LpEmptyState from "./LpEmptyState.vue"
38
38
  import LpFileTreeNode from "./LpFileTreeNode.vue"
39
39
  import LpScrollArea from "./LpScrollArea.vue"
40
40
  import { useMergedAttrs } from "../composables/useMergedClass"
41
+ import { useLabels } from "../composables/useLpConfig"
41
42
 
42
43
  export type { FileNode } from "./fileTree"
43
44
 
44
45
  /** Rolled-up stats for the ticked entries — what a backup picker summarises. */
46
+ export interface FileTreeLabels {
47
+ [key: string]: string
48
+ empty: string
49
+ emptyDescription: string
50
+ file: string
51
+ files: string
52
+ folder: string
53
+ folders: string
54
+ }
55
+
45
56
  export interface FileTreeSummary {
46
57
  /** Ticked files (directories excluded — they're containers, not payload). */
47
58
  files: number
@@ -84,6 +95,7 @@ const props = withDefaults(
84
95
  /** Empty-state copy when `nodes` is empty. */
85
96
  emptyLabel?: string
86
97
  emptyDescription?: string
98
+ labels?: Partial<FileTreeLabels>
87
99
  }>(),
88
100
  {
89
101
  sort: true,
@@ -93,8 +105,6 @@ const props = withDefaults(
93
105
  showModified: false,
94
106
  loading: false,
95
107
  skeletonRows: 8,
96
- emptyLabel: "No files",
97
- emptyDescription: "This folder is empty.",
98
108
  },
99
109
  )
100
110
 
@@ -131,6 +141,19 @@ watch(
131
141
  },
132
142
  )
133
143
 
144
+ const text = useLabels<FileTreeLabels>(
145
+ "fileTree",
146
+ {
147
+ empty: "No files",
148
+ emptyDescription: "This folder is empty.",
149
+ file: "file",
150
+ files: "files",
151
+ folder: "folder",
152
+ folders: "folders",
153
+ },
154
+ () => props.labels,
155
+ )
156
+
134
157
  const loadingSet = computed(() => new Set(props.loadingIds ?? []))
135
158
 
136
159
  // Same controlled/uncontrolled deal as `expanded`.
@@ -229,7 +252,7 @@ const summary = computed<FileTreeSummary>(() => {
229
252
  }
230
253
  })
231
254
 
232
- watch(summary, (value) => emit("summary", value), { deep: false })
255
+ watch(summary, (value) => emit("summary", value), { immediate: true })
233
256
 
234
257
  function setChecked(next: Set<string>) {
235
258
  checkedIds.value = next
@@ -350,6 +373,9 @@ function expand(node: FileNode) {
350
373
 
351
374
  function collapse(node: FileNode) {
352
375
  if (!openIds.value.has(node.id)) return
376
+ // A fetch that failed left `children` unset; collapsing lets the next open
377
+ // ask again instead of showing an empty folder forever.
378
+ if (node.children === undefined && !loadingSet.value.has(node.id)) requested.delete(node.id)
353
379
  const next = new Set(openIds.value)
354
380
  next.delete(node.id)
355
381
  setExpanded(next)
@@ -506,8 +532,8 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
506
532
  <LpEmptyState
507
533
  v-else-if="!roots.length"
508
534
  icon="lucide:folder-search"
509
- :title="emptyLabel"
510
- :description="emptyDescription"
535
+ :title="emptyLabel ?? text.empty"
536
+ :description="emptyDescription ?? text.emptyDescription"
511
537
  />
512
538
 
513
539
  <!-- h-full so the tree fills whatever box the caller gave it and the scroll
@@ -558,9 +584,9 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
558
584
  class="shrink-0 border-t border-line px-2 py-1.5 text-xs text-muted"
559
585
  >
560
586
  <slot name="summary" v-bind="summary">
561
- {{ summary.files }} {{ summary.files === 1 ? "file" : "files" }}<template
587
+ {{ summary.files }} {{ summary.files === 1 ? text.file : text.files }}<template
562
588
  v-if="summary.dirs"
563
- >, {{ summary.dirs }} {{ summary.dirs === 1 ? "folder" : "folders" }}</template>
589
+ >, {{ summary.dirs }} {{ summary.dirs === 1 ? text.folder : text.folders }}</template>
564
590
  <template v-if="summary.bytes !== undefined"> · {{ summary.sizeLabel }}</template>
565
591
  </slot>
566
592
  </div>