@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
@@ -14,6 +14,7 @@
14
14
  * the static <nav> it always has — existing call sites are unaffected.
15
15
  */
16
16
  import { computed, ref, useId } from "vue"
17
+ import { useLabels } from "../composables/useLpConfig"
17
18
  import LpDrawer from "./LpDrawer.vue"
18
19
  import LpInput from "./LpInput.vue"
19
20
  import LpSidebarNav from "./LpSidebarNav.vue"
@@ -45,8 +46,10 @@ const props = withDefaults(
45
46
  * query is active — a hidden match is the same as no match.
46
47
  */
47
48
  searchable?: boolean
48
- /** Placeholder for that field. */
49
+ /** Placeholder for that field (defaults to the `search` label). */
49
50
  searchPlaceholder?: string
51
+ /** Text overrides: `nav` names the landmark, `search` the filter field. */
52
+ labels?: Partial<{ nav: string; search: string }>
50
53
  /** The query, when the app wants to own it (v-model:search). */
51
54
  search?: string
52
55
  /** Show a skeleton placeholder instead of the items. */
@@ -113,7 +116,6 @@ const props = withDefaults(
113
116
  autoCollapseAfter?: number
114
117
  }>(),
115
118
  {
116
- searchPlaceholder: "Search",
117
119
  skeletonRows: 6,
118
120
  skeletonHeader: true,
119
121
  responsive: false,
@@ -132,6 +134,9 @@ const emit = defineEmits<{
132
134
  (e: "update:search", value: string): void
133
135
  }>()
134
136
 
137
+ const text = useLabels("sidebar", { nav: "Sidebar", search: "Search" }, () => props.labels)
138
+ const placeholder = computed(() => props.searchPlaceholder ?? text.value.search)
139
+
135
140
  // Normalise both inputs to a single section list.
136
141
  const allGroups = computed<SidebarSection[]>(() =>
137
142
  props.sections ?? (props.items ? [{ items: props.items }] : []),
@@ -271,7 +276,7 @@ defineOptions({ inheritAttrs: false })
271
276
  class="h-full flex-col gap-1 p-3"
272
277
  :class="[railClass, chromeClass, width ? '' : 'w-60']"
273
278
  :style="width ? { width } : undefined"
274
- aria-label="Sidebar"
279
+ :aria-label="text.nav"
275
280
  v-bind="$attrs"
276
281
  >
277
282
  <!-- Rail: edge-to-edge (negative margins cancel the p-3) and h-16 to match
@@ -298,8 +303,8 @@ defineOptions({ inheritAttrs: false })
298
303
  size="sm"
299
304
  icon="lucide:search"
300
305
  class="mb-2 shrink-0"
301
- :placeholder="searchPlaceholder"
302
- :aria-label="searchPlaceholder"
306
+ :placeholder="placeholder"
307
+ :aria-label="placeholder"
303
308
  />
304
309
  <LpSidebarNav
305
310
  :groups="groups"
@@ -339,7 +344,7 @@ defineOptions({ inheritAttrs: false })
339
344
  :edge-breakpoint="mobileBreakpoint"
340
345
  @update:open="(v) => emit('update:open', v)"
341
346
  >
342
- <div class="flex h-full flex-col gap-1">
347
+ <nav class="flex h-full flex-col gap-1" :aria-label="text.nav">
343
348
  <div
344
349
  v-if="$slots.header"
345
350
  class="shrink-0"
@@ -357,8 +362,8 @@ defineOptions({ inheritAttrs: false })
357
362
  size="sm"
358
363
  icon="lucide:search"
359
364
  class="mb-2 shrink-0"
360
- :placeholder="searchPlaceholder"
361
- :aria-label="searchPlaceholder"
365
+ :placeholder="placeholder"
366
+ :aria-label="placeholder"
362
367
  />
363
368
  <LpSidebarNav
364
369
  :groups="groups"
@@ -383,6 +388,6 @@ defineOptions({ inheritAttrs: false })
383
388
  <div v-if="$slots.footer" class="mt-2 shrink-0 border-t border-line pt-3">
384
389
  <slot name="footer" />
385
390
  </div>
386
- </div>
391
+ </nav>
387
392
  </LpDrawer>
388
393
  </template>
@@ -21,6 +21,8 @@ import LpContextMenu from "./LpContextMenu.vue"
21
21
  import LpIcon from "./LpIcon.vue"
22
22
  import LpScrollArea from "./LpScrollArea.vue"
23
23
  import type { SidebarItem, SidebarSection } from "./sidebar"
24
+ import { resolveLink, type ResolvedLink } from "./link"
25
+ import { useLinkComponent } from "../composables/useLpConfig"
24
26
  import { useMergedAttrs } from "../composables/useMergedClass"
25
27
 
26
28
  const props = withDefaults(
@@ -75,6 +77,18 @@ function isOpen(section: SidebarSection): boolean {
75
77
 
76
78
  const pillTransition = usePillTransition()
77
79
 
80
+ const linkComponent = useLinkComponent()
81
+
82
+ /*
83
+ * A disabled item is always a disabled button: an anchor cannot be disabled,
84
+ * and the old enabled-button fallback for a disabled link stayed focusable.
85
+ */
86
+ function rowOf(item: SidebarItem): ResolvedLink {
87
+ const link = item.disabled ? null : resolveLink(linkComponent.value, { to: item.to, href: item.href })
88
+ if (link) return link
89
+ return { is: "button", props: { type: "button", disabled: item.disabled || undefined } }
90
+ }
91
+
78
92
  // The nav fills the remaining height with a fixed gap between groups; a short sidebar needs both under the consumer's control.
79
93
  const { class: rootClass, attrs: rest } = useMergedAttrs(
80
94
  "flex min-h-0 flex-1 flex-col gap-4",
@@ -118,7 +132,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
118
132
  because a thing that reacts to a click must look like one. -->
119
133
  <CollapsibleTrigger
120
134
  v-if="section.title && section.collapsible"
121
- class="group/section mt-2 flex cursor-pointer items-center gap-2 rounded-control px-2 py-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:bg-white/[0.04] hover:text-ink focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
135
+ class="group/section mt-2 flex cursor-pointer items-center gap-2 rounded-control px-2 py-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:bg-ink/[0.04] hover:text-ink focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
122
136
  >
123
137
  <LpIcon
124
138
  v-if="section.icon"
@@ -165,10 +179,8 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
165
179
  :items="item.menu ?? []"
166
180
  >
167
181
  <component
168
- :is="item.href && !item.disabled ? 'a' : 'button'"
169
- :href="item.href && !item.disabled ? item.href : undefined"
170
- :type="item.href ? undefined : 'button'"
171
- :disabled="item.href ? undefined : item.disabled"
182
+ :is="rowOf(item).is"
183
+ v-bind="rowOf(item).props"
172
184
  :aria-current="itemActive(item) ? 'page' : undefined"
173
185
  :aria-disabled="item.disabled || undefined"
174
186
  class="group/item relative flex items-center gap-3 rounded-control px-3 py-2 text-sm font-medium outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
@@ -88,7 +88,10 @@ const isBlock = computed(() => !slots.default)
88
88
  }
89
89
 
90
90
  @media (prefers-reduced-motion: reduce) {
91
- .lp-skeleton-item {
91
+ /* Repeats the descendant form: it outranks a bare class, and grouped items
92
+ kept pulsing under reduced motion. */
93
+ .lp-skeleton-item,
94
+ .lp-skeleton .lp-skeleton-item {
92
95
  animation: none;
93
96
  opacity: 0.75;
94
97
  }
@@ -9,6 +9,7 @@ export default { inheritAttrs: false }
9
9
  // Single-value slider (reka Slider). reka models value as an array; we expose
10
10
  // a plain number for the common single-thumb case. Themed via tokens.
11
11
  import { SliderRange, SliderRoot, SliderThumb, SliderTrack } from "reka-ui"
12
+ import { useFieldControl } from "../composables/useField"
12
13
  import { useMergedAttrs } from "../composables/useMergedClass"
13
14
 
14
15
  const props = withDefaults(
@@ -38,6 +39,8 @@ function onCommit(v: number[] | undefined) {
38
39
  if (v && v.length) emit("change", v[0])
39
40
  }
40
41
 
42
+ const field = useFieldControl(() => ({ wrapped: false, disabled: props.disabled }))
43
+
41
44
  // A slider is full-width by default; putting one in a toolbar means constraining it, and a fixed h-5 has to yield to a denser row.
42
45
  const { class: rootClass, attrs: rest } = useMergedAttrs(
43
46
  "relative flex h-5 w-full touch-none select-none items-center data-[disabled]:opacity-55",
@@ -50,7 +53,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
50
53
  :min="min"
51
54
  :max="max"
52
55
  :step="step"
53
- :disabled="disabled"
56
+ :disabled="field.disabled"
54
57
  :class="rootClass"
55
58
  v-bind="rest"
56
59
  @update:model-value="onUpdate"
@@ -60,7 +63,10 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
60
63
  <SliderRange class="absolute h-full rounded-pill bg-brand" />
61
64
  </SliderTrack>
62
65
  <SliderThumb
66
+ :id="field.id"
63
67
  :aria-label="ariaLabel"
68
+ :aria-labelledby="ariaLabel ? undefined : field.labelId"
69
+ :aria-describedby="field.describedBy"
64
70
  class="block size-4 rounded-pill border-2 border-brand bg-surface shadow outline-none transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] hover:scale-110 active:scale-95 focus-visible:ring-2 focus-visible:ring-ring motion-reduce:hover:scale-100 motion-reduce:active:scale-100"
65
71
  />
66
72
  </SliderRoot>
@@ -16,6 +16,7 @@
16
16
  */
17
17
  import { computed } from "vue"
18
18
  import { useReveal } from "../composables/useReveal"
19
+ import { useLabels } from "../composables/useLpConfig"
19
20
  import { areaPath, extent, linePath, normalise, type Box } from "./chart"
20
21
 
21
22
  const props = withDefaults(
@@ -56,6 +57,7 @@ const props = withDefaults(
56
57
  * watched being drawn — not a figure mostly spent easing out.
57
58
  */
58
59
  duration?: number
60
+ labels?: Partial<Record<"empty" | "up" | "down" | "flat" | "latest", string>>
59
61
  }>(),
60
62
  { width: 84, height: 24, tone: "brand", smooth: 0.9, animate: true, duration: 900 },
61
63
  )
@@ -131,14 +133,20 @@ const lastPoint = computed(() => {
131
133
  const hasData = computed(() => series.value[0].values.some((v) => v != null))
132
134
 
133
135
  /* A bare <svg> is invisible to a screen reader; say the shape out loud. */
136
+ const text = useLabels(
137
+ "sparkline",
138
+ { empty: "No data", up: "trending up", down: "trending down", flat: "flat", latest: "latest" },
139
+ () => props.labels,
140
+ )
141
+
134
142
  const label = computed(() => {
135
143
  if (props.ariaLabel) return props.ariaLabel
136
144
  const values = series.value[0].values.filter((v): v is number => v != null)
137
- if (!values.length) return "No data"
145
+ if (!values.length) return text.value.empty
138
146
  const first = values[0]
139
147
  const last = values[values.length - 1]
140
- const dir = last > first ? "trending up" : last < first ? "trending down" : "flat"
141
- return `${dir}, latest ${last}`
148
+ const dir = last > first ? text.value.up : last < first ? text.value.down : text.value.flat
149
+ return `${dir}, ${text.value.latest} ${last}`
142
150
  })
143
151
 
144
152
  /*
@@ -8,6 +8,7 @@ export default { inheritAttrs: false }
8
8
  <script setup lang="ts">
9
9
  import { computed } from "vue"
10
10
  import LpIcon from "./LpIcon.vue"
11
+ import LpRollingText from "./LpRollingText.vue"
11
12
  import { useMergedAttrs } from "../composables/useMergedClass"
12
13
 
13
14
  export interface Step {
@@ -65,19 +66,28 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
65
66
  >
66
67
  <div class="flex items-center gap-2">
67
68
  <span
68
- class="flex size-7 shrink-0 items-center justify-center rounded-pill border text-xs font-semibold transition-colors"
69
+ class="flex size-7 shrink-0 items-center justify-center rounded-pill border text-xs font-semibold transition-colors duration-[var(--duration-medium)]"
69
70
  :class="isCompleted(step, i)
70
71
  ? 'border-transparent bg-brand text-ink-inverse'
71
72
  : isCurrent(i)
72
73
  ? 'border-brand text-brand'
73
74
  : 'border-line text-muted'"
74
75
  >
75
- <LpIcon v-if="isCompleted(step, i)" name="lucide:check" :size="14" />
76
- <LpIcon v-else-if="step.icon" :name="step.icon" :size="14" />
77
- <template v-else>{{ i + 1 }}</template>
76
+ <!-- The number turns into the tick rather than swapping for it. -->
77
+ <LpRollingText
78
+ mode="flip"
79
+ :duration="260"
80
+ :value="isCompleted(step, i) ? 'done' : step.icon ? 'icon' : String(i + 1)"
81
+ >
82
+ <template #default="{ value }">
83
+ <LpIcon v-if="value === 'done'" name="lucide:check" :size="14" />
84
+ <LpIcon v-else-if="value === 'icon' && step.icon" :name="step.icon" :size="14" />
85
+ <template v-else>{{ value }}</template>
86
+ </template>
87
+ </LpRollingText>
78
88
  </span>
79
89
  <span
80
- class="text-sm"
90
+ class="text-sm transition-colors duration-[var(--duration-medium)]"
81
91
  :class="isCompleted(step, i) || isCurrent(i) ? 'text-ink' : 'text-muted'"
82
92
  >
83
93
  {{ step.label }}
@@ -91,11 +101,14 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
91
101
  }}</span>
92
102
  </span>
93
103
  </div>
94
- <span
95
- v-if="i < steps.length - 1"
96
- class="h-px flex-1"
97
- :class="isCompleted(step, i) ? 'bg-brand' : 'bg-line'"
98
- />
104
+ <!-- The connector fills from the step it leaves towards the next one,
105
+ so finishing a step reads as moving forward. -->
106
+ <span v-if="i < steps.length - 1" class="relative h-px flex-1 overflow-hidden bg-line">
107
+ <span
108
+ class="absolute inset-0 origin-left bg-brand transition-transform duration-[var(--duration-medium)] ease-[var(--ease-emphasized)] motion-reduce:transition-none rtl:origin-right"
109
+ :class="isCompleted(step, i) ? 'scale-x-100' : 'scale-x-0'"
110
+ />
111
+ </span>
99
112
  </li>
100
113
  </ol>
101
114
  </template>
@@ -6,8 +6,9 @@ export default { inheritAttrs: false }
6
6
  </script>
7
7
 
8
8
  <script setup lang="ts">
9
- import { computed, useSlots } from "vue"
9
+ import { computed, useId, useSlots } from "vue"
10
10
  import { SwitchRoot, SwitchThumb } from "reka-ui"
11
+ import { useFieldControl } from "../composables/useField"
11
12
  import { useMergedAttrs } from "../composables/useMergedClass"
12
13
 
13
14
  const props = withDefaults(
@@ -34,7 +35,7 @@ const labelled = computed(() => Boolean(props.label || props.description || slot
34
35
  const TRACK =
35
36
  "group inline-flex h-5 w-9 shrink-0 items-center rounded-pill border border-line bg-surface-soft px-0.5 outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring data-[state=checked]:border-transparent data-[state=checked]:bg-brand disabled:cursor-not-allowed disabled:opacity-55"
36
37
  const THUMB =
37
- "h-3.5 w-3.5 rounded-full bg-ink shadow transition-[translate,width,background-color] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-active:w-5 group-data-[state=checked]:translate-x-4 group-data-[state=checked]:bg-ink-inverse group-data-[state=checked]:group-active:-translate-x-1 motion-reduce:group-active:w-3.5"
38
+ "h-3.5 w-3.5 rounded-full bg-ink shadow transition-[translate,width,background-color] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-active:w-5 group-data-[state=checked]:translate-x-4 group-data-[state=checked]:bg-ink-inverse group-data-[state=checked]:group-active:translate-x-2.5 motion-reduce:group-active:w-3.5"
38
39
 
39
40
  // Bare, the switch is the root, so a consumer asking for a bigger track must not
40
41
  // have to depend on stylesheet order to get it. With text, the row is the root
@@ -46,31 +47,53 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() => {
46
47
  : "inline-flex cursor-pointer items-center gap-2 text-sm text-ink"
47
48
  })
48
49
 
50
+ // Whatever is not class (name, aria-*, @focus…) belongs to the switch itself,
51
+ // labelled or not: on the label row it would land on the <label>.
52
+ const field = useFieldControl(() => ({
53
+ wrapped: false,
54
+ id: typeof rest.value.id === "string" ? rest.value.id : undefined,
55
+ disabled: props.disabled,
56
+ }))
57
+ const descriptionId = `${useId()}-description`
58
+ const describedBy = computed(
59
+ () => [props.description && labelled.value ? descriptionId : "", field.value.describedBy ?? ""].filter(Boolean).join(" ") || undefined,
60
+ )
61
+
49
62
  function onUpdate(value: boolean) {
50
63
  emit("update:modelValue", value)
51
64
  }
52
65
  </script>
53
66
 
54
67
  <template>
55
- <label v-if="labelled" :class="[rootClass, disabled && 'cursor-not-allowed']" v-bind="rest">
68
+ <label v-if="labelled" :class="[rootClass, field.disabled && 'cursor-not-allowed']">
56
69
  <span v-if="labelPosition === 'start'" class="flex min-w-0 flex-col">
57
70
  <slot>{{ label }}</slot>
58
- <span v-if="description" class="text-xs text-muted">{{ description }}</span>
71
+ <span v-if="description" :id="descriptionId" class="text-xs text-muted">{{ description }}</span>
59
72
  </span>
60
- <SwitchRoot :model-value="modelValue" :disabled="disabled" :class="TRACK" @update:model-value="onUpdate">
73
+ <SwitchRoot
74
+ v-bind="rest"
75
+ :id="field.id"
76
+ :model-value="modelValue"
77
+ :disabled="field.disabled"
78
+ :aria-describedby="describedBy"
79
+ :class="TRACK"
80
+ @update:model-value="onUpdate"
81
+ >
61
82
  <SwitchThumb :class="THUMB" />
62
83
  </SwitchRoot>
63
84
  <span v-if="labelPosition === 'end'" class="flex min-w-0 flex-col">
64
85
  <slot>{{ label }}</slot>
65
- <span v-if="description" class="text-xs text-muted">{{ description }}</span>
86
+ <span v-if="description" :id="descriptionId" class="text-xs text-muted">{{ description }}</span>
66
87
  </span>
67
88
  </label>
68
89
  <SwitchRoot
69
90
  v-else
91
+ v-bind="rest"
92
+ :id="field.id"
70
93
  :model-value="modelValue"
71
- :disabled="disabled"
94
+ :disabled="field.disabled"
95
+ :aria-describedby="describedBy"
72
96
  :class="rootClass"
73
- v-bind="rest"
74
97
  @update:model-value="onUpdate"
75
98
  >
76
99
  <SwitchThumb :class="THUMB" />