@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
@@ -1,10 +1,18 @@
1
1
  <script setup lang="ts">
2
2
  // Confirmation dialog built on LpModal. Resolves an action via confirm/cancel.
3
- import { computed } from "vue"
3
+ import { computed, nextTick, ref, watch } from "vue"
4
+ import { useLabels } from "../composables/useLpConfig"
4
5
  import LpButton from "./LpButton.vue"
5
6
  import LpIcon from "./LpIcon.vue"
6
7
  import LpModal from "./LpModal.vue"
7
8
 
9
+ export type ConfirmLabels = {
10
+ confirm: string
11
+ cancel: string
12
+ loading: string
13
+ failed: string
14
+ }
15
+
8
16
  const props = withDefaults(
9
17
  defineProps<{
10
18
  open?: boolean
@@ -33,13 +41,22 @@ const props = withDefaults(
33
41
  * The confirm action is in flight: the button disables and swaps to
34
42
  * `loadingLabel`, and confirming no longer closes the dialog — the owner
35
43
  * closes it once the work settles, so a failure can stay on screen.
44
+ * Setting it from the `confirm` handler is enough; the dialog waits a tick
45
+ * for it before deciding to close.
36
46
  */
37
47
  loading?: boolean
38
48
  loadingLabel?: string
49
+ /**
50
+ * The work behind the confirm button. When given, the dialog runs it,
51
+ * shows the loading state itself, closes once it resolves and stays open
52
+ * with the error's message when it rejects.
53
+ */
54
+ action?: () => unknown
39
55
  /** Frosted glass panel, passed to LpModal. Follows `provideGlass` when unset. */
40
56
  glass?: boolean
57
+ labels?: Partial<ConfirmLabels>
41
58
  }>(),
42
- { confirmLabel: "Confirm", cancelLabel: "Cancel", loadingLabel: "Working…", glass: undefined },
59
+ { glass: undefined },
43
60
  )
44
61
 
45
62
  const emit = defineEmits<{
@@ -48,29 +65,61 @@ const emit = defineEmits<{
48
65
  (e: "cancel"): void
49
66
  }>()
50
67
 
68
+ const text = useLabels<ConfirmLabels>(
69
+ "confirm",
70
+ { confirm: "Confirm", cancel: "Cancel", loading: "Working…", failed: "Something went wrong." },
71
+ () => props.labels,
72
+ )
73
+
51
74
  /*
52
75
  * Only a destructive prompt is marked by default. A routine confirmation
53
76
  * ("Save these changes?") with a warning badge cries wolf, and the badge stops
54
77
  * meaning anything on the dialog where it matters.
55
- *
56
- * "Not given" cannot be tested with `undefined` here. A prop whose type union
57
- * includes Boolean is boolean-cast by Vue, so an absent `icon` arrives as
58
- * `false` — the same value the caller passes to suppress the marker — and the
59
- * default silently never applied. A string is an override, and everything else
60
- * (absent, or an explicit false) falls through to the danger default.
61
78
  */
62
79
  const marker = computed<string>(() => {
63
80
  if (props.icon !== undefined) return props.icon
64
81
  return props.danger ? "lucide:triangle-alert" : ""
65
82
  })
66
83
 
67
- function confirm() {
84
+ const running = ref(false)
85
+ const error = ref("")
86
+ const busy = computed(() => props.loading || running.value)
87
+
88
+ watch(
89
+ () => props.open,
90
+ (open) => {
91
+ if (open) error.value = ""
92
+ },
93
+ )
94
+
95
+ async function confirm() {
96
+ if (busy.value) return
97
+ error.value = ""
68
98
  emit("confirm")
69
- // An async confirm owns its own dismissal; closing here would tear the
70
- // dialog down before the caller knows whether the action succeeded.
99
+ if (props.action) {
100
+ running.value = true
101
+ try {
102
+ await props.action()
103
+ } catch (e) {
104
+ error.value = e instanceof Error && e.message ? e.message : text.value.failed
105
+ return
106
+ } finally {
107
+ running.value = false
108
+ }
109
+ emit("update:open", false)
110
+ return
111
+ }
112
+ // A handler that sets `loading` does so through the parent, whose new props
113
+ // reach this component only on the next render.
114
+ await nextTick()
71
115
  if (!props.loading) emit("update:open", false)
72
116
  }
73
- function cancel() {
117
+
118
+ // Every way out that is not the confirm button counts as a cancel: Escape, the
119
+ // scrim, the close button. None of them may close it while work is in flight.
120
+ function dismiss(open: boolean) {
121
+ if (open) return emit("update:open", true)
122
+ if (busy.value) return
74
123
  emit("cancel")
75
124
  emit("update:open", false)
76
125
  }
@@ -82,7 +131,8 @@ function cancel() {
82
131
  :open="open"
83
132
  :title="title"
84
133
  :description="description"
85
- @update:open="(v) => $emit('update:open', v)"
134
+ role="alertdialog"
135
+ @update:open="dismiss"
86
136
  >
87
137
  <template v-if="marker" #title>
88
138
  <span class="inline-flex items-center gap-2.5">
@@ -100,16 +150,17 @@ function cancel() {
100
150
  </span>
101
151
  </template>
102
152
  <slot />
153
+ <p v-if="error" role="alert" class="mt-3 text-sm text-danger">{{ error }}</p>
103
154
  <template #footer>
104
- <LpButton variant="ghost" :disabled="loading" @click="cancel">
105
- {{ cancelLabel }}
155
+ <LpButton variant="ghost" :disabled="busy" @click="dismiss(false)">
156
+ {{ cancelLabel ?? text.cancel }}
106
157
  </LpButton>
107
158
  <LpButton
108
159
  :variant="danger ? 'danger' : 'solid'"
109
- :disabled="loading"
160
+ :disabled="busy"
110
161
  @click="confirm"
111
162
  >
112
- {{ loading ? loadingLabel : confirmLabel }}
163
+ {{ busy ? (loadingLabel ?? text.loading) : (confirmLabel ?? text.confirm) }}
113
164
  </LpButton>
114
165
  </template>
115
166
  </LpModal>
@@ -0,0 +1,59 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ * Renders the requests made through `confirm()` / `useConfirm()`. Mount one
4
+ * near the app root, next to LpToaster.
5
+ */
6
+ import { computed, ref, watch } from "vue"
7
+ import { confirmQueue, settleConfirm } from "./confirm"
8
+ import LpConfirmDialog from "./LpConfirmDialog.vue"
9
+
10
+ withDefaults(
11
+ defineProps<{
12
+ /** Frosted glass panel. Follows `provideGlass` when unset. */
13
+ glass?: boolean
14
+ }>(),
15
+ { glass: undefined },
16
+ )
17
+
18
+ const current = computed(() => confirmQueue[0])
19
+ const open = ref(false)
20
+ let confirmed = false
21
+ const answer = (value: boolean) => (confirmed = value)
22
+
23
+ watch(
24
+ () => current.value?.id,
25
+ (id) => {
26
+ confirmed = false
27
+ open.value = id !== undefined
28
+ },
29
+ { immediate: true },
30
+ )
31
+
32
+ // The dialog closes itself after a successful action, or straight away for a
33
+ // plain confirmation; a failed action keeps it open and never gets here.
34
+ function onOpen(value: boolean) {
35
+ open.value = value
36
+ const request = current.value
37
+ if (!value && request) settleConfirm(request.id, confirmed)
38
+ }
39
+ </script>
40
+
41
+ <template>
42
+ <LpConfirmDialog
43
+ v-if="current"
44
+ :key="current.id"
45
+ :open="open"
46
+ :glass="glass"
47
+ :title="current.title"
48
+ :description="current.description"
49
+ :danger="current.danger"
50
+ :icon="current.icon"
51
+ :confirm-label="current.confirmLabel"
52
+ :cancel-label="current.cancelLabel"
53
+ :loading-label="current.loadingLabel"
54
+ :action="current.action"
55
+ @confirm="answer(true)"
56
+ @cancel="answer(false)"
57
+ @update:open="onOpen"
58
+ />
59
+ </template>
@@ -6,20 +6,10 @@
6
6
  * long-press on touch) opens the menu at the pointer. Shares the floating-panel
7
7
  * surface + option-row look with the rest of the kit via dropdown.ts.
8
8
  */
9
- import {
10
- ContextMenuContent,
11
- ContextMenuItem,
12
- ContextMenuPortal,
13
- ContextMenuRoot,
14
- ContextMenuSeparator,
15
- ContextMenuSub,
16
- ContextMenuSubContent,
17
- ContextMenuSubTrigger,
18
- ContextMenuTrigger,
19
- } from "reka-ui"
9
+ import { ContextMenuContent, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from "reka-ui"
20
10
  import { computed } from "vue"
11
+ import ContextMenuItems from "./ContextMenuItems.vue"
21
12
  import { POPOVER_PANEL } from "./dropdown"
22
- import LpIcon from "./LpIcon.vue"
23
13
 
24
14
  export interface ContextMenuItemDef {
25
15
  label: string
@@ -30,7 +20,7 @@ export interface ContextMenuItemDef {
30
20
  disabled?: boolean
31
21
  separatorBefore?: boolean
32
22
  onSelect?: () => void
33
- /** Nested submenu. When present, onSelect is ignored. */
23
+ /** Nested submenu, to any depth. When present, onSelect is ignored. */
34
24
  children?: ContextMenuItemDef[]
35
25
  /**
36
26
  * Renders as a tick-box rather than a plain item, and the menu stays open on
@@ -52,17 +42,13 @@ const props = defineProps<{
52
42
  always?: boolean
53
43
  }>()
54
44
 
55
- // With no items, render just the trigger slot — no reka machinery — so the
56
- // element keeps the browser's native context menu. Lets callers wrap
57
- // unconditionally and pass [] to opt out per element. `always` overrides this so
58
- // a late-populated menu is ready on the very first click.
45
+ // With no items the trigger is disabled, so the element keeps the browser's
46
+ // native context menu. Lets callers wrap unconditionally and pass [] to opt out
47
+ // per element. `always` overrides this so a late-populated menu is ready on the
48
+ // very first click.
59
49
  const enabled = computed(() => props.always || props.items.length > 0)
60
50
 
61
51
  const PANEL = `${POPOVER_PANEL} z-(--z-popover) min-w-48 p-1 outline-none`
62
- const ITEM =
63
- "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"
64
- const ICON =
65
- "transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-data-[highlighted]/item:translate-x-0.5"
66
52
 
67
53
  // A menu opened over a text field takes focus, and on close reka hands it to the
68
54
  // trigger, so the caret and the selection the user right-clicked are gone — the
@@ -92,104 +78,17 @@ function returnFocus(event: Event) {
92
78
  </script>
93
79
 
94
80
  <template>
95
- <slot v-if="!enabled" />
96
-
97
- <ContextMenuRoot v-else>
98
- <ContextMenuTrigger as-child @contextmenu.capture="rememberField">
81
+ <!-- One tree whether or not there are items: swapping between a bare slot and
82
+ the reka trigger remounted the wrapped content whenever items arrived
83
+ late, losing its state and focus. A disabled trigger leaves the browser's
84
+ own menu in place. -->
85
+ <ContextMenuRoot>
86
+ <ContextMenuTrigger as-child :disabled="!enabled" @contextmenu.capture="rememberField">
99
87
  <slot />
100
88
  </ContextMenuTrigger>
101
89
  <ContextMenuPortal v-if="items.length">
102
- <ContextMenuContent :class="PANEL" @close-auto-focus="returnFocus">
103
- <template v-for="(item, i) in items" :key="i">
104
- <ContextMenuSeparator v-if="item.separatorBefore" class="my-1 h-px bg-line" />
105
-
106
- <!-- Submenu branch -->
107
- <ContextMenuSub v-if="item.children?.length">
108
- <ContextMenuSubTrigger
109
- :class="[ITEM, 'justify-between text-ink data-[highlighted]:text-brand data-[state=open]:bg-brand-soft data-[state=open]:text-brand']"
110
- >
111
- <span class="flex items-center gap-2">
112
- <LpIcon v-if="item.icon" :name="item.icon" :size="15" :class="ICON" />
113
- {{ item.label }}
114
- </span>
115
- <LpIcon name="lucide:chevron-right" :size="14" class="text-muted" />
116
- </ContextMenuSubTrigger>
117
- <ContextMenuPortal>
118
- <ContextMenuSubContent :class="PANEL" :side-offset="2" :align-offset="-4">
119
- <template v-for="(sub, j) in item.children" :key="j">
120
- <ContextMenuSeparator v-if="sub.separatorBefore" class="my-1 h-px bg-line" />
121
- <!-- The tick-box case, as in the top-level menu: this is
122
- where most of them live, since a submenu is what a list
123
- of choices opens into. -->
124
- <ContextMenuItem
125
- v-if="sub.checked !== undefined"
126
- :class="[ITEM, 'justify-between text-ink data-[highlighted]:text-brand']"
127
- :disabled="sub.disabled"
128
- @select.prevent="sub.onSelect?.()"
129
- >
130
- <span class="flex items-center gap-2">
131
- <LpIcon
132
- :name="sub.checked ? 'lucide:check' : 'lucide:minus'"
133
- :size="15"
134
- :class="[ICON, sub.checked ? '' : 'opacity-25']"
135
- />
136
- {{ sub.label }}
137
- </span>
138
- <kbd v-if="sub.shortcut" class="text-xs text-muted">{{ sub.shortcut }}</kbd>
139
- </ContextMenuItem>
140
-
141
- <ContextMenuItem
142
- v-else
143
- :class="[ITEM, 'justify-between', sub.danger ? 'text-danger data-[highlighted]:bg-danger-soft data-[highlighted]:text-danger' : 'text-ink data-[highlighted]:text-brand']"
144
- :disabled="sub.disabled"
145
- @select="sub.onSelect?.()"
146
- >
147
- <span class="flex items-center gap-2">
148
- <LpIcon v-if="sub.icon" :name="sub.icon" :size="15" :class="ICON" />
149
- {{ sub.label }}
150
- </span>
151
- <kbd v-if="sub.shortcut" class="text-xs text-muted">{{ sub.shortcut }}</kbd>
152
- </ContextMenuItem>
153
- </template>
154
- </ContextMenuSubContent>
155
- </ContextMenuPortal>
156
- </ContextMenuSub>
157
-
158
- <!-- Tick-box item. `@select.prevent` keeps the menu open: these come
159
- in lists where several are set in one visit, and closing after
160
- each tick would mean reopening for the next. The tick occupies a
161
- fixed slot so unticked rows stay aligned with ticked ones. -->
162
- <ContextMenuItem
163
- v-else-if="item.checked !== undefined"
164
- :class="[ITEM, 'justify-between text-ink data-[highlighted]:text-brand']"
165
- :disabled="item.disabled"
166
- @select.prevent="item.onSelect?.()"
167
- >
168
- <span class="flex items-center gap-2">
169
- <LpIcon
170
- :name="item.checked ? 'lucide:check' : 'lucide:minus'"
171
- :size="15"
172
- :class="[ICON, item.checked ? '' : 'opacity-25']"
173
- />
174
- {{ item.label }}
175
- </span>
176
- <kbd v-if="item.shortcut" class="text-xs text-muted">{{ item.shortcut }}</kbd>
177
- </ContextMenuItem>
178
-
179
- <!-- Leaf item -->
180
- <ContextMenuItem
181
- v-else
182
- :class="[ITEM, 'justify-between', item.danger ? 'text-danger data-[highlighted]:bg-danger-soft data-[highlighted]:text-danger' : 'text-ink data-[highlighted]:text-brand']"
183
- :disabled="item.disabled"
184
- @select="item.onSelect?.()"
185
- >
186
- <span class="flex items-center gap-2">
187
- <LpIcon v-if="item.icon" :name="item.icon" :size="15" :class="ICON" />
188
- {{ item.label }}
189
- </span>
190
- <kbd v-if="item.shortcut" class="text-xs text-muted">{{ item.shortcut }}</kbd>
191
- </ContextMenuItem>
192
- </template>
90
+ <ContextMenuContent :collision-padding="8" :class="PANEL" @close-auto-focus="returnFocus">
91
+ <ContextMenuItems :items="items" :panel="PANEL" />
193
92
  </ContextMenuContent>
194
93
  </ContextMenuPortal>
195
94
  </ContextMenuRoot>
@@ -1,3 +1,7 @@
1
+ <script lang="ts">
2
+ export default { inheritAttrs: false }
3
+ </script>
4
+
1
5
  <script setup lang="ts">
2
6
  /*
3
7
  * Date field + calendar popover. The model is an ISO date string ("YYYY-MM-DD",
@@ -14,10 +18,14 @@
14
18
  * the common case.
15
19
  */
16
20
  import { PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from "reka-ui"
17
- import { computed, ref } from "vue"
21
+ import { computed, ref, useAttrs } from "vue"
22
+ import { useFieldControl } from "../composables/useField"
23
+ import { useLabels } from "../composables/useLpConfig"
24
+ import { mergeClass } from "../composables/useMergedClass"
18
25
  import { POPOVER_PANEL } from "./dropdown"
19
26
  import LpCalendar from "./LpCalendar.vue"
20
27
  import LpIcon from "./LpIcon.vue"
28
+ import LpRollingText from "./LpRollingText.vue"
21
29
  import LpTimeField from "./LpTimeField.vue"
22
30
 
23
31
  const props = withDefaults(
@@ -80,7 +88,11 @@ const shellSize = {
80
88
  lg: "h-(--size-control-lg) text-sm",
81
89
  }
82
90
 
83
- function onPick(v: string | undefined) {
91
+ function onPick(picked: string | undefined) {
92
+ // Clicking the selected day again is the calendar's "deselect". Here it is
93
+ // someone confirming the date they already have, so it keeps the value and
94
+ // closes like any other pick; clearing has its own button.
95
+ const v = picked ?? (day.value || undefined)
84
96
  if (!props.time) {
85
97
  emit("update:modelValue", v)
86
98
  open.value = false
@@ -101,75 +113,99 @@ function onTime(at: string) {
101
113
  function clear() {
102
114
  emit("update:modelValue", undefined)
103
115
  }
116
+
117
+ const attrs = useAttrs()
118
+ const field = useFieldControl(() => ({
119
+ wrapped: false,
120
+ id: typeof attrs.id === "string" ? attrs.id : undefined,
121
+ invalid: props.invalid,
122
+ disabled: props.disabled,
123
+ }))
124
+ const labels = useLabels("datePicker", { clear: "Clear" })
125
+ const showClear = computed(() => props.clearable && !!props.modelValue && !field.value.disabled)
104
126
  </script>
105
127
 
106
128
  <template>
107
- <PopoverRoot v-model:open="open">
108
- <PopoverTrigger as-child>
109
- <button
110
- type="button"
111
- :disabled="disabled"
112
- 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"
113
- :class="[
114
- shellSize[size],
115
- invalid ? 'border-danger focus-visible:ring-danger-soft' : 'border-line focus-visible:border-brand',
116
- ]"
117
- >
118
- <LpIcon name="lucide:calendar" :size="16" class="shrink-0 text-muted" />
119
- <span class="min-w-0 flex-1 truncate" :class="display ? '' : 'text-muted'">
120
- {{ display || placeholder }}
121
- </span>
122
- <span
123
- v-if="clearable && modelValue && !disabled"
124
- role="button"
125
- tabindex="-1"
126
- aria-label="Clear"
127
- class="shrink-0 text-muted transition-colors hover:text-ink"
128
- @click.stop="clear"
129
- @keydown.enter.stop="clear"
129
+ <!-- The clear control sits beside the trigger, not inside it: a button
130
+ nested in a button is invalid, and the nested one was unreachable by
131
+ keyboard. -->
132
+ <div :class="mergeClass('relative w-full', attrs.class as string)" :style="attrs.style as never">
133
+ <PopoverRoot v-model:open="open">
134
+ <PopoverTrigger as-child>
135
+ <button
136
+ v-bind="{ ...attrs, class: undefined, style: undefined }"
137
+ :id="field.id"
138
+ type="button"
139
+ :disabled="field.disabled"
140
+ :aria-invalid="field.invalid || undefined"
141
+ :aria-describedby="field.describedBy"
142
+ 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"
143
+ :class="[
144
+ shellSize[size],
145
+ field.invalid ? 'border-danger focus-visible:ring-danger-soft' : 'border-line focus-visible:border-brand',
146
+ ]"
147
+ >
148
+ <LpIcon name="lucide:calendar" :size="16" class="shrink-0 text-muted" />
149
+ <LpRollingText
150
+ :value="display || placeholder || ''"
151
+ align="start"
152
+ class="min-w-0 flex-1"
153
+ :class="display ? '' : 'text-muted'"
154
+ />
155
+ <span v-if="showClear" class="w-[15px] shrink-0" aria-hidden="true" />
156
+ <LpIcon
157
+ name="lucide:chevron-down"
158
+ :size="15"
159
+ class="shrink-0 text-muted transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-data-[state=open]:rotate-180"
160
+ />
161
+ </button>
162
+ </PopoverTrigger>
163
+ <PopoverPortal>
164
+ <PopoverContent
165
+ :collision-padding="8"
166
+ :side-offset="6"
167
+ align="start"
168
+ :class="[POPOVER_PANEL, 'z-(--z-popover) rounded-card p-0 outline-none']"
130
169
  >
131
- <LpIcon name="lucide:x" :size="15" />
132
- </span>
133
- <LpIcon
134
- name="lucide:chevron-down"
135
- :size="15"
136
- class="shrink-0 text-muted transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-data-[state=open]:rotate-180"
137
- />
138
- </button>
139
- </PopoverTrigger>
140
- <PopoverPortal>
141
- <PopoverContent
142
- :side-offset="6"
143
- align="start"
144
- :class="[POPOVER_PANEL, 'z-(--z-popover) rounded-card p-0 outline-none']"
145
- >
146
- <LpCalendar
147
- :model-value="day"
148
- :min="min"
149
- :max="max"
150
- :is-disabled="isDisabled"
151
- class="border-0 bg-transparent"
152
- @update:model-value="onPick"
153
- />
154
- <div v-if="time" class="flex items-center gap-2 border-t border-line px-3 py-2">
155
- <LpIcon name="lucide:clock" :size="15" class="shrink-0 text-muted" />
156
- <LpTimeField
157
- :model-value="minute || undefined"
158
- :disabled="!day"
159
- :icon="false"
160
- size="sm"
161
- class="flex-1"
162
- @update:model-value="(at) => onTime(at || '')"
170
+ <LpCalendar
171
+ :model-value="day"
172
+ :min="min"
173
+ :max="max"
174
+ :is-disabled="isDisabled"
175
+ class="border-0 bg-transparent"
176
+ @update:model-value="onPick"
163
177
  />
164
- <button
165
- type="button"
166
- class="shrink-0 rounded-control px-2 py-1 text-xs text-muted transition-colors hover:text-ink"
167
- @click="open = false"
168
- >
169
- {{ doneLabel }}
170
- </button>
171
- </div>
172
- </PopoverContent>
173
- </PopoverPortal>
174
- </PopoverRoot>
178
+ <div v-if="time" class="flex items-center gap-2 border-t border-line px-3 py-2">
179
+ <LpIcon name="lucide:clock" :size="15" class="shrink-0 text-muted" />
180
+ <LpTimeField
181
+ :model-value="minute || undefined"
182
+ :disabled="!day"
183
+ :icon="false"
184
+ size="sm"
185
+ class="flex-1"
186
+ @update:model-value="(at) => onTime(at || '')"
187
+ />
188
+ <button
189
+ type="button"
190
+ class="shrink-0 rounded-control px-2 py-1 text-xs text-muted transition-colors hover:text-ink"
191
+ @click="open = false"
192
+ >
193
+ {{ doneLabel }}
194
+ </button>
195
+ </div>
196
+ </PopoverContent>
197
+ </PopoverPortal>
198
+ </PopoverRoot>
199
+ <!-- Positioned over the spacer the trigger leaves for it, just before the
200
+ chevron (gap-2 + 15px chevron + px-3 from the right edge). -->
201
+ <button
202
+ v-if="showClear"
203
+ type="button"
204
+ :aria-label="labels.clear"
205
+ 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"
206
+ @click="clear"
207
+ >
208
+ <LpIcon name="lucide:x" :size="15" />
209
+ </button>
210
+ </div>
175
211
  </template>