@leavepulse/ui 0.77.3 → 0.78.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 (597) hide show
  1. package/README.md +68 -0
  2. package/dist/assets/{LpCommandPalette-DOIZHw-g.css → LpCommandPalette-DDJKICKh.css} +19 -19
  3. package/dist/assets/{LpDock-Cbh0wQUv.css → LpDock-Dw82tWVD.css} +3 -3
  4. package/dist/assets/{LpDropzone-B_qRq1-R.css → LpDropzone-DNbh_NFJ.css} +10 -10
  5. package/dist/assets/{LpImageEditor-XM5bAziW.css → LpImageEditor-Bgu20XLs.css} +6 -6
  6. package/dist/assets/{LpTable-Bv-EcQs7.css → LpTable-CCk0ZHo7.css} +19 -19
  7. package/dist/assets/LpTabs-Bh7a-Bdl.css +18 -0
  8. package/dist/chunks/AppShellContextNav-C6Ho2mof.js +49 -0
  9. package/dist/chunks/ChartRangeSelector-HO0sMMz6.js +129 -0
  10. package/dist/chunks/{ContextMenuItems-BXumicbh.js → ContextMenuItems-DmaKSJVv.js} +1 -1
  11. package/dist/chunks/{DropzoneLinkField-C-9BCGJn.js → DropzoneLinkField-DXIr0eFz.js} +2 -2
  12. package/dist/chunks/{FieldWrapper-CORPzo9s.js → FieldWrapper-DnuMTrqW.js} +1 -1
  13. package/dist/chunks/IconCloseButton-COZEsa9u.js +29 -0
  14. package/dist/chunks/{LayoutCanvas-CNOAUk7D.js → LayoutCanvas-CZKOltfA.js} +1 -1
  15. package/dist/chunks/{LayoutNode-DF5BMN0J.js → LayoutNode-D2f7v683.js} +45 -45
  16. package/dist/chunks/{LpActionBar-BP1ld7-S.js → LpActionBar-BGMk5KoG.js} +3 -3
  17. package/dist/chunks/{LpActivityLine-CJvzrNpj.js → LpActivityLine-Ea-t5Vza.js} +4 -2
  18. package/dist/chunks/{LpAddressInput-E8KtwU7-.js → LpAddressInput-ZqoZ-N2Y.js} +12 -11
  19. package/dist/chunks/{LpAlert-n5XPAZfv.js → LpAlert-CjvTZIKF.js} +3 -3
  20. package/dist/chunks/LpAppShell-BNAVTQsY.js +582 -0
  21. package/dist/chunks/{LpAutocomplete-C-SpPfBG.js → LpAutocomplete-DkY6CSyD.js} +24 -42
  22. package/dist/chunks/{LpAvatar-Cg6Az3wz.js → LpAvatar-cQVsNk6F.js} +2 -2
  23. package/dist/chunks/{LpBadge-wUYLN4U9.js → LpBadge-D5QQdDQ_.js} +2 -2
  24. package/dist/chunks/{LpBottomNav-CbNQQPuL.js → LpBottomNav-zxLGGbjm.js} +9 -2
  25. package/dist/chunks/{LpBreadcrumbs-DPdbth-c.js → LpBreadcrumbs-CIIBsygI.js} +4 -3
  26. package/dist/chunks/{LpButton-CFb44IjC.js → LpButton-DSHioIfD.js} +5 -4
  27. package/dist/chunks/{LpCalendar-CuoWks-0.js → LpCalendar-D4-LVjfF.js} +1 -1
  28. package/dist/chunks/{LpCard-r13WRqox.js → LpCard-BPtYXwnT.js} +1 -1
  29. package/dist/chunks/{LpCheckbox-Cs4SHaHX.js → LpCheckbox-D6RuDW4X.js} +38 -16
  30. package/dist/chunks/{LpCodeBlock-DRC8VbXW.js → LpCodeBlock-DuzmnIHl.js} +19 -41
  31. package/dist/chunks/{LpCodeDisplay-CE9TxCA4.js → LpCodeDisplay-CO8fngxa.js} +5 -5
  32. package/dist/chunks/{LpCollapse-CWS9M_lm.js → LpCollapse-DrAw5DBZ.js} +5 -4
  33. package/dist/chunks/{LpColorPicker-1y1QJHZR.js → LpColorPicker-BxAHEnR5.js} +11 -12
  34. package/dist/chunks/{LpCommandScope-CtUhJe8T.js → LpCommandScope-DRUvHiX7.js} +1 -1
  35. package/dist/chunks/{LpConfirmDialog-DlD45Bi_.js → LpConfirmDialog-6oCIJTDa.js} +3 -3
  36. package/dist/chunks/{LpConfirmHost-BIWjBLmI.js → LpConfirmHost-DgHvfT6s.js} +1 -1
  37. package/dist/chunks/{LpContextMenu-rx2tCjwk.js → LpContextMenu--eMGIql6.js} +13 -15
  38. package/dist/chunks/{LpContextSwitcher-Dwh6dyID.js → LpContextSwitcher-1C1tExdl.js} +2 -2
  39. package/dist/chunks/{LpCopyButton-xF7wFVoU.js → LpCopyButton-7EOSIuwa.js} +3 -3
  40. package/dist/chunks/{LpCountdown-DgeY3cLG.js → LpCountdown-DVl7vXI1.js} +4 -3
  41. package/dist/chunks/{LpDataList-Jxyl9H5w.js → LpDataList-DgBeM01y.js} +13 -4
  42. package/dist/chunks/{LpDatePicker-CA0QTfBC.js → LpDatePicker-DwSDY0mt.js} +12 -18
  43. package/dist/chunks/{LpDateRangePicker-u3J51xXk.js → LpDateRangePicker-CNQXdTVv.js} +10 -16
  44. package/dist/chunks/{LpDisclosure-Cpa7Hc5T.js → LpDisclosure-DG7JCo-d.js} +2 -2
  45. package/dist/chunks/{LpDrawer-CrG8t_KP.js → LpDrawer-Bt9UUqt_.js} +61 -61
  46. package/dist/chunks/{LpDropdownMenu-Dl7Z4ZhS.js → LpDropdownMenu-CLzxIOgM.js} +9 -17
  47. package/dist/chunks/{LpEditable-Cy6GLbFx.js → LpEditable-DVT0Rh8F.js} +9 -6
  48. package/dist/chunks/{LpEmptyState-EQHiRYhG.js → LpEmptyState-DurPF001.js} +1 -1
  49. package/dist/chunks/{LpFieldNotice-CzQ2_5rQ.js → LpFieldNotice-DJ10cYIo.js} +1 -1
  50. package/dist/chunks/LpFileTree-BSx1kBoV.js +735 -0
  51. package/dist/chunks/{LpFileTreeNode-cT6BIWvz.js → LpFileTreeNode-4dVmFHb3.js} +116 -34
  52. package/dist/chunks/{LpFormField-Ch69Jd-X.js → LpFormField-BKYn9dyP.js} +3 -3
  53. package/dist/chunks/{LpFrame-BVQCQLDI.js → LpFrame-Bv5Fphte.js} +25 -11
  54. package/dist/chunks/{LpHintExample-9Py0wc-L.js → LpHintExample-YzCXiSUp.js} +1 -1
  55. package/dist/chunks/{LpHintLayer-CgObdUsM.js → LpHintLayer-BeKRsRon.js} +2 -2
  56. package/dist/chunks/{LpHints-Bz-BlaSl.js → LpHints-qf3xYsza.js} +3 -3
  57. package/dist/chunks/{LpHoverCard-spGxyw7T.js → LpHoverCard-DanH3Nz1.js} +23 -5
  58. package/dist/chunks/{LpIcon--5WSb3jJ.js → LpIcon-C6JpQ5td.js} +6 -3
  59. package/dist/chunks/{LpIdenticon-BzdC_qz9.js → LpIdenticon-Dbbr4fvt.js} +2 -1
  60. package/dist/chunks/{LpInfraNode-46XOCkfq.js → LpInfraNode-BAU40Km2.js} +2 -2
  61. package/dist/chunks/{LpInput-Tw-yxT4I.js → LpInput-DL6HEJdi.js} +16 -28
  62. package/dist/chunks/LpInspector-CuoMAhiw.js +209 -0
  63. package/dist/chunks/{LpLightbox-B95yu22B.js → LpLightbox-xvpQ64MM.js} +57 -32
  64. package/dist/chunks/{LpLoader-DVwkOmt9.js → LpLoader-Bf1yZwaU.js} +1 -1
  65. package/dist/chunks/LpLogInspector-6cNnXqp4.js +119 -0
  66. package/dist/chunks/{LpLogViewer-ngqkns4m.js → LpLogViewer-Dvb1E9_G.js} +234 -53
  67. package/dist/chunks/{LpMapPicker-DRGgh89B.js → LpMapPicker-D5_YLLBC.js} +4 -3
  68. package/dist/chunks/{LpMasonry--k0571qG.js → LpMasonry-DPYekFxs.js} +8 -5
  69. package/dist/chunks/{LpMediaGallery-Bkram-MO.js → LpMediaGallery-Df-tiWeq.js} +3 -3
  70. package/dist/chunks/{LpModal-CoGhLTyH.js → LpModal-DrtQ_1A2.js} +11 -13
  71. package/dist/chunks/{LpModalNav-BGvs2y5Q.js → LpModalNav-DkmGCnZE.js} +11 -37
  72. package/dist/chunks/{LpNotificationBell-B0034Lk-.js → LpNotificationBell-D_wklO1m.js} +9 -9
  73. package/dist/chunks/{LpNumberField-e0IBkWU1.js → LpNumberField-CNHsmSDZ.js} +7 -16
  74. package/dist/chunks/{LpOtpInput-CeBPsM7D.js → LpOtpInput-BRpLBbOD.js} +2 -2
  75. package/dist/chunks/{LpPagination-BIIiYHXR.js → LpPagination-snQ020xQ.js} +6 -4
  76. package/dist/chunks/{LpPaletteTheme-DiLYKQiZ.js → LpPaletteTheme-dVtxHx-F.js} +4 -4
  77. package/dist/chunks/{LpPasswordInput-BkHKQDhM.js → LpPasswordInput-B-zD6tUS.js} +2 -2
  78. package/dist/chunks/{LpPhoneInput-CETEp5W7.js → LpPhoneInput-BSL7NX4z.js} +15 -26
  79. package/dist/chunks/{LpPie-DrZepi0p.js → LpPie-9t_guO49.js} +22 -21
  80. package/dist/chunks/{LpPipeline-B3aOVOkV.js → LpPipeline-rjXvEDRJ.js} +16 -7
  81. package/dist/chunks/{LpPopover-DmZ3wUtP.js → LpPopover-BQSuxtai.js} +10 -1
  82. package/dist/chunks/{LpProgress-Yr6ONG5t.js → LpProgress-D_MSjowb.js} +2 -1
  83. package/dist/chunks/{LpQuota-Dk2MyI2Z.js → LpQuota-CgWPgdVt.js} +1 -1
  84. package/dist/chunks/{LpRadioGroup-ggaPL92T.js → LpRadioGroup-BrazjLY_.js} +4 -4
  85. package/dist/chunks/{LpRangeCalendar-CYYUYqJ7.js → LpRangeCalendar-BrqlGZzE.js} +1 -1
  86. package/dist/chunks/{LpRangeSlider-CM-0ZITZ.js → LpRangeSlider-fz61tzc-.js} +5 -9
  87. package/dist/chunks/{LpRelativeTime-DwgCb5-8.js → LpRelativeTime-BovUk8MP.js} +1 -1
  88. package/dist/chunks/{LpRepeater-D7ZmnIYd.js → LpRepeater-BkJHSPI1.js} +3 -3
  89. package/dist/chunks/{LpRevealOnScroll-DcQjexLy.js → LpRevealOnScroll-CPwyiQAr.js} +1 -1
  90. package/dist/chunks/{LpScramble-Bn9OoOtC.js → LpScramble-DsE77iVp.js} +1 -1
  91. package/dist/chunks/{LpScrollArea-MyEnVB_5.js → LpScrollArea-ay4Xo_hg.js} +22 -10
  92. package/dist/chunks/{LpSecretField-BbmqM9ye.js → LpSecretField-DBvU6iAw.js} +10 -9
  93. package/dist/chunks/{LpSection-DFK_uoJE.js → LpSection-BfteipxU.js} +1 -1
  94. package/dist/chunks/{LpSegmented-CeACsMMA.js → LpSegmented-DbEJhbv-.js} +8 -10
  95. package/dist/chunks/{LpSelect-Cxq4yBGd.js → LpSelect-Y-iuSymW.js} +47 -30
  96. package/dist/chunks/{LpShaderBackdrop-Bote0iuy.js → LpShaderBackdrop-wQygYb5h.js} +13 -10
  97. package/dist/chunks/{LpShift-CCq7dofl.js → LpShift-DAgS_d2x.js} +18 -15
  98. package/dist/chunks/{LpSidebar-DzE1783C.js → LpSidebar-kSkpPh3-.js} +74 -87
  99. package/dist/chunks/{LpSidebarNav-DE3PhXVP.js → LpSidebarNav-C2mvqrY2.js} +8 -6
  100. package/dist/chunks/{LpSidebarRow-BRYhNDSo.js → LpSidebarRow-eRP4Q31q.js} +6 -5
  101. package/dist/chunks/{LpSlider-DxYoCJJO.js → LpSlider-A4ixovgS.js} +5 -9
  102. package/dist/chunks/{LpSpotlight-4F3NeUMY.js → LpSpotlight-DwxkhFgR.js} +2 -1
  103. package/dist/chunks/{LpStat-f2LfDCJ4.js → LpStat-pMNZgFcq.js} +2 -2
  104. package/dist/chunks/{LpStatus-DglxttQ7.js → LpStatus-DGJFwk5F.js} +2 -2
  105. package/dist/chunks/{LpStepMark-DpzKVyh8.js → LpStepMark-Dw2MRTrt.js} +2 -2
  106. package/dist/chunks/{LpStepper-DpsocBNJ.js → LpStepper-DNnh7lCu.js} +1 -1
  107. package/dist/chunks/{LpSwap-C96c0LWh.js → LpSwap-M7nFeW3C.js} +1 -1
  108. package/dist/chunks/{LpSwitch-BnXd1llc.js → LpSwitch-DnC2-0aV.js} +3 -3
  109. package/dist/chunks/{LpTabFace-DPCwKkH2.js → LpTabFace-DQoV8mQG.js} +2 -2
  110. package/dist/chunks/{LpTableFilter-CiVBpaq0.js → LpTableFilter-DgARTsA1.js} +4 -4
  111. package/dist/chunks/{LpTableOfContents-BN5zvpnx.js → LpTableOfContents-CKkWEK6R.js} +14 -9
  112. package/dist/chunks/{LpTagsInput-C-mDG648.js → LpTagsInput-Dt0mteSj.js} +19 -27
  113. package/dist/chunks/{LpTextarea-TE-yZv2U.js → LpTextarea-Co0b9PVx.js} +8 -16
  114. package/dist/chunks/LpThemeProvider-XK6ZeWAE.js +81 -0
  115. package/dist/chunks/{LpThemeStudio-BpORDvH6.js → LpThemeStudio-DzYfE-5k.js} +12 -195
  116. package/dist/chunks/{LpThemeSwitcher-BnEn_mvf.js → LpThemeSwitcher-CFLkdbBL.js} +8 -6
  117. package/dist/chunks/{LpTimeField-BawSVsmt.js → LpTimeField-DeOcKWJq.js} +6 -9
  118. package/dist/chunks/{LpToaster-IwTXjbbU.js → LpToaster-QFj0P-5m.js} +10 -8
  119. package/dist/chunks/{LpTooltip-BcBvIx4z.js → LpTooltip-BJf3lZMq.js} +51 -19
  120. package/dist/chunks/LpTopologyCanvas-BOHHHZ_6.js +673 -0
  121. package/dist/chunks/{LpTour-BZq-mUMv.js → LpTour-ClKYhx4h.js} +7 -5
  122. package/dist/chunks/{LpTruncate-CflAIwiJ.js → LpTruncate-C6u3gjc6.js} +6 -5
  123. package/dist/chunks/{LpTypewriter-BCL_a6wE.js → LpTypewriter-CZIzpdbe.js} +2 -1
  124. package/dist/chunks/{LpUptimeBar-DqNvvMBK.js → LpUptimeBar-BnYmR2rj.js} +3 -3
  125. package/dist/chunks/{LpVideoBackdrop-DS1wGv-X.js → LpVideoBackdrop-Ct4St4ZL.js} +10 -13
  126. package/dist/chunks/LpWindow-B8YSRVTH.js +297 -0
  127. package/dist/chunks/LpWorkspaceSwitcher-D0e9upAy.js +139 -0
  128. package/dist/chunks/{MenuTitle-BmCNNTKH.js → MenuTitle-Bh5dAdV1.js} +1 -1
  129. package/dist/chunks/{OptionContent-22UT8zLn.js → OptionContent-CGb8QlHA.js} +1 -1
  130. package/dist/chunks/{PickerField-Bfh8id3d.js → PickerField-BM1dyafh.js} +4 -4
  131. package/dist/chunks/{RevealToggle-BEit1n6Z.js → RevealToggle-CCPDE8Mv.js} +3 -3
  132. package/dist/chunks/SliderRail-DhPREa_R.js +18 -0
  133. package/dist/chunks/{TourCard-DNclH3u_.js → TourCard-DAzTYfav.js} +4 -4
  134. package/dist/chunks/{icons-DMITlfw5.js → icons-CYfcpSDS.js} +26 -10
  135. package/dist/component-names.d.ts +1 -1
  136. package/dist/components/AppShellContextNav.vue.d.ts +13 -0
  137. package/dist/components/AppShellContextNav.vue.js +4 -0
  138. package/dist/components/ChartRangeSelector.vue.d.ts +21 -0
  139. package/dist/components/ChartRangeSelector.vue.js +4 -0
  140. package/dist/components/ContextMenuItems.vue.js +1 -1
  141. package/dist/components/DropzoneLinkField.vue.d.ts +2 -2
  142. package/dist/components/DropzoneLinkField.vue.js +1 -1
  143. package/dist/components/FieldWrapper.vue.js +1 -1
  144. package/dist/components/IconCloseButton.vue.d.ts +7 -0
  145. package/dist/components/IconCloseButton.vue.js +4 -0
  146. package/dist/components/LayoutCanvas.vue.js +1 -1
  147. package/dist/components/LayoutNode.vue.js +1 -1
  148. package/dist/components/LpActionBar.vue.js +1 -1
  149. package/dist/components/LpActivityLine.vue.js +1 -1
  150. package/dist/components/LpAddressInput.vue.js +1 -1
  151. package/dist/components/LpAlert.vue.js +1 -1
  152. package/dist/components/LpAppShell.vue.d.ts +110 -2
  153. package/dist/components/LpAppShell.vue.js +1 -1
  154. package/dist/components/LpAutocomplete.vue.js +1 -1
  155. package/dist/components/LpAvatar.vue.js +1 -1
  156. package/dist/components/LpBadge.vue.js +1 -1
  157. package/dist/components/LpBottomNav.vue.d.ts +5 -0
  158. package/dist/components/LpBottomNav.vue.js +1 -1
  159. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  160. package/dist/components/LpButton.vue.d.ts +14 -2
  161. package/dist/components/LpButton.vue.js +1 -1
  162. package/dist/components/LpCalendar.vue.js +1 -1
  163. package/dist/components/LpCard.vue.js +1 -1
  164. package/dist/components/LpChart.vue.d.ts +74 -2
  165. package/dist/components/LpChart.vue.js +326 -85
  166. package/dist/components/LpCheckbox.vue.d.ts +6 -2
  167. package/dist/components/LpCheckbox.vue.js +1 -1
  168. package/dist/components/LpCodeBlock.vue.js +1 -1
  169. package/dist/components/LpCodeDisplay.vue.js +1 -1
  170. package/dist/components/LpCollapse.vue.js +1 -1
  171. package/dist/components/LpColorPicker.vue.js +1 -1
  172. package/dist/components/LpCommandPalette.vue.d.ts +8 -0
  173. package/dist/components/LpCommandPalette.vue.js +107 -56
  174. package/dist/components/LpCommandScope.vue.js +1 -1
  175. package/dist/components/LpConfirmDialog.vue.js +1 -1
  176. package/dist/components/LpConfirmHost.vue.js +1 -1
  177. package/dist/components/LpContextMenu.vue.js +1 -1
  178. package/dist/components/LpContextSwitcher.vue.js +1 -1
  179. package/dist/components/LpCopyButton.vue.js +1 -1
  180. package/dist/components/LpCountdown.vue.js +1 -1
  181. package/dist/components/LpDataList.vue.d.ts +1 -1
  182. package/dist/components/LpDataList.vue.js +1 -1
  183. package/dist/components/LpDatePicker.vue.d.ts +3 -15
  184. package/dist/components/LpDatePicker.vue.js +1 -1
  185. package/dist/components/LpDateRangePicker.vue.d.ts +3 -13
  186. package/dist/components/LpDateRangePicker.vue.js +1 -1
  187. package/dist/components/LpDisclosure.vue.js +1 -1
  188. package/dist/components/LpDock.vue.js +21 -18
  189. package/dist/components/LpDrawer.vue.d.ts +1 -1
  190. package/dist/components/LpDrawer.vue.js +1 -1
  191. package/dist/components/LpDropdownMenu.vue.js +1 -1
  192. package/dist/components/LpDropzone.vue.js +14 -11
  193. package/dist/components/LpEditable.vue.js +1 -1
  194. package/dist/components/LpEmptyState.vue.js +1 -1
  195. package/dist/components/LpFieldNotice.vue.js +1 -1
  196. package/dist/components/LpFileTree.vue.d.ts +96 -6
  197. package/dist/components/LpFileTree.vue.js +1 -1
  198. package/dist/components/LpFileTreeNode.vue.d.ts +48 -0
  199. package/dist/components/LpFileTreeNode.vue.js +1 -1
  200. package/dist/components/LpFormField.vue.js +1 -1
  201. package/dist/components/LpFrame.vue.js +1 -1
  202. package/dist/components/LpGauge.vue.d.ts +1 -1
  203. package/dist/components/LpHintExample.vue.js +1 -1
  204. package/dist/components/LpHintLayer.vue.js +1 -1
  205. package/dist/components/LpHints.vue.js +1 -1
  206. package/dist/components/LpHoverCard.vue.js +1 -1
  207. package/dist/components/LpIcon.vue.d.ts +2 -0
  208. package/dist/components/LpIcon.vue.js +1 -1
  209. package/dist/components/LpIdenticon.vue.js +1 -1
  210. package/dist/components/LpImageEditor.vue.js +7 -6
  211. package/dist/components/LpInfraNode.vue.js +1 -1
  212. package/dist/components/LpInput.vue.d.ts +1 -1
  213. package/dist/components/LpInput.vue.js +1 -1
  214. package/dist/components/LpInspector.vue.d.ts +87 -0
  215. package/dist/components/LpInspector.vue.js +4 -0
  216. package/dist/components/LpLightbox.vue.d.ts +8 -0
  217. package/dist/components/LpLightbox.vue.js +1 -1
  218. package/dist/components/LpLoader.vue.d.ts +1 -1
  219. package/dist/components/LpLoader.vue.js +1 -1
  220. package/dist/components/LpLogInspector.vue.d.ts +18 -0
  221. package/dist/components/LpLogInspector.vue.js +4 -0
  222. package/dist/components/LpLogViewer.vue.d.ts +93 -0
  223. package/dist/components/LpLogViewer.vue.js +1 -1
  224. package/dist/components/LpMapPicker.vue.js +1 -1
  225. package/dist/components/LpMasonry.vue.js +1 -1
  226. package/dist/components/LpMediaGallery.vue.js +1 -1
  227. package/dist/components/LpModal.vue.d.ts +4 -2
  228. package/dist/components/LpModal.vue.js +1 -1
  229. package/dist/components/LpModalNav.vue.d.ts +5 -5
  230. package/dist/components/LpModalNav.vue.js +1 -1
  231. package/dist/components/LpNotificationBell.vue.d.ts +2 -2
  232. package/dist/components/LpNotificationBell.vue.js +1 -1
  233. package/dist/components/LpNumberField.vue.js +1 -1
  234. package/dist/components/LpOtpInput.vue.js +1 -1
  235. package/dist/components/LpPagination.vue.js +1 -1
  236. package/dist/components/LpPaletteFacts.vue.js +1 -1
  237. package/dist/components/LpPaletteHeader.vue.js +2 -2
  238. package/dist/components/LpPaletteTheme.vue.js +1 -1
  239. package/dist/components/LpPasswordInput.vue.js +1 -1
  240. package/dist/components/LpPhoneInput.vue.js +1 -1
  241. package/dist/components/LpPie.vue.d.ts +2 -2
  242. package/dist/components/LpPie.vue.js +1 -1
  243. package/dist/components/LpPipeline.vue.d.ts +4 -0
  244. package/dist/components/LpPipeline.vue.js +1 -1
  245. package/dist/components/LpPopover.vue.d.ts +7 -3
  246. package/dist/components/LpPopover.vue.js +1 -1
  247. package/dist/components/LpProgress.vue.d.ts +1 -1
  248. package/dist/components/LpProgress.vue.js +1 -1
  249. package/dist/components/LpQuota.vue.js +1 -1
  250. package/dist/components/LpRadioGroup.vue.js +1 -1
  251. package/dist/components/LpRangeCalendar.vue.js +1 -1
  252. package/dist/components/LpRangeSlider.vue.js +1 -1
  253. package/dist/components/LpRelativeTime.vue.js +1 -1
  254. package/dist/components/LpRepeater.vue.js +1 -1
  255. package/dist/components/LpRevealOnScroll.vue.js +1 -1
  256. package/dist/components/LpScramble.vue.js +1 -1
  257. package/dist/components/LpScrollArea.vue.js +1 -1
  258. package/dist/components/LpSecretField.vue.js +1 -1
  259. package/dist/components/LpSection.vue.js +1 -1
  260. package/dist/components/LpSegmented.vue.js +1 -1
  261. package/dist/components/LpSelect.vue.js +1 -1
  262. package/dist/components/LpShaderBackdrop.vue.js +1 -1
  263. package/dist/components/LpShift.vue.js +1 -1
  264. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  265. package/dist/components/LpSidebar.vue.d.ts +1 -1
  266. package/dist/components/LpSidebar.vue.js +1 -1
  267. package/dist/components/LpSidebarNav.vue.js +1 -1
  268. package/dist/components/LpSidebarRow.vue.js +1 -1
  269. package/dist/components/LpSlider.vue.js +1 -1
  270. package/dist/components/LpSparkline.vue.d.ts +1 -1
  271. package/dist/components/LpSpotlight.vue.js +1 -1
  272. package/dist/components/LpStat.vue.js +1 -1
  273. package/dist/components/LpStatus.vue.js +1 -1
  274. package/dist/components/LpStepMark.vue.js +1 -1
  275. package/dist/components/LpStepper.vue.js +1 -1
  276. package/dist/components/LpSwap.vue.js +1 -1
  277. package/dist/components/LpSwitch.vue.js +1 -1
  278. package/dist/components/LpTabFace.vue.js +1 -1
  279. package/dist/components/LpTable.vue.d.ts +132 -0
  280. package/dist/components/LpTable.vue.js +547 -135
  281. package/dist/components/LpTableFilter.vue.js +1 -1
  282. package/dist/components/LpTableOfContents.vue.js +1 -1
  283. package/dist/components/LpTabs.vue.js +6 -5
  284. package/dist/components/LpTagsInput.vue.js +1 -1
  285. package/dist/components/LpTextarea.vue.js +1 -1
  286. package/dist/components/LpThemeProvider.vue.d.ts +2 -0
  287. package/dist/components/LpThemeProvider.vue.js +1 -1
  288. package/dist/components/LpThemeStudio.vue.js +1 -1
  289. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  290. package/dist/components/LpTimeField.vue.js +1 -1
  291. package/dist/components/LpToaster.vue.js +1 -1
  292. package/dist/components/LpTooltip.vue.js +1 -1
  293. package/dist/components/LpTopologyCanvas.vue.d.ts +131 -5
  294. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  295. package/dist/components/LpTour.vue.js +1 -1
  296. package/dist/components/LpTruncate.vue.js +1 -1
  297. package/dist/components/LpTypewriter.vue.js +1 -1
  298. package/dist/components/LpUptimeBar.vue.js +1 -1
  299. package/dist/components/LpVideoBackdrop.vue.js +1 -1
  300. package/dist/components/LpWindow.vue.d.ts +99 -0
  301. package/dist/components/LpWindow.vue.js +4 -0
  302. package/dist/components/LpWorkspaceSwitcher.vue.d.ts +18 -0
  303. package/dist/components/LpWorkspaceSwitcher.vue.js +4 -0
  304. package/dist/components/MenuTitle.vue.js +1 -1
  305. package/dist/components/OptionContent.vue.js +1 -1
  306. package/dist/components/PickerField.vue.d.ts +2 -2
  307. package/dist/components/PickerField.vue.js +1 -1
  308. package/dist/components/RevealToggle.vue.js +1 -1
  309. package/dist/components/SliderRail.vue.d.ts +3 -0
  310. package/dist/components/SliderRail.vue.js +4 -0
  311. package/dist/components/TourCard.vue.js +1 -1
  312. package/dist/components/address.js +2 -1
  313. package/dist/components/appShell.d.ts +36 -0
  314. package/dist/components/appShell.js +30 -0
  315. package/dist/components/calendar.d.ts +12 -3
  316. package/dist/components/calendar.js +2 -5
  317. package/dist/components/chart.d.ts +21 -0
  318. package/dist/components/chart.js +4 -3
  319. package/dist/components/chartWindow.d.ts +57 -0
  320. package/dist/components/chartWindow.js +136 -0
  321. package/dist/components/color.js +1 -1
  322. package/dist/components/commandPalette.d.ts +16 -2
  323. package/dist/components/commandPalette.js +39 -4
  324. package/dist/components/controlSize.d.ts +5 -0
  325. package/dist/components/controlSize.js +8 -0
  326. package/dist/components/dropdown.d.ts +5 -0
  327. package/dist/components/dropdown.js +2 -0
  328. package/dist/components/exportData.d.ts +6 -1
  329. package/dist/components/exportData.js +25 -18
  330. package/dist/components/fileTree.d.ts +67 -0
  331. package/dist/components/fileTree.js +141 -1
  332. package/dist/components/identicon.js +3 -2
  333. package/dist/components/logTools.d.ts +93 -0
  334. package/dist/components/logTools.js +210 -0
  335. package/dist/components/menuWhen.js +1 -1
  336. package/dist/components/meter.js +2 -1
  337. package/dist/components/pipeline.d.ts +2 -0
  338. package/dist/components/pipeline.js +5 -0
  339. package/dist/components/relativeTime.js +2 -1
  340. package/dist/components/reveal.js +2 -1
  341. package/dist/components/select.d.ts +5 -0
  342. package/dist/components/select.js +10 -0
  343. package/dist/components/sidebar.d.ts +1 -0
  344. package/dist/components/tableFeatures.d.ts +58 -0
  345. package/dist/components/tableFeatures.js +87 -0
  346. package/dist/components/topologyGraph.d.ts +164 -0
  347. package/dist/components/topologyGraph.js +416 -0
  348. package/dist/components/tour.js +5 -3
  349. package/dist/components/videoBackdrop.js +7 -6
  350. package/dist/composables/clamp.d.ts +2 -0
  351. package/dist/composables/clamp.js +4 -0
  352. package/dist/composables/devWarn.d.ts +3 -0
  353. package/dist/composables/devWarn.js +14 -0
  354. package/dist/composables/easing.d.ts +13 -4
  355. package/dist/composables/easing.js +16 -10
  356. package/dist/composables/overlayHistory.js +14 -1
  357. package/dist/composables/pointerDrag.d.ts +15 -0
  358. package/dist/composables/pointerDrag.js +40 -0
  359. package/dist/composables/propCheck.d.ts +10 -0
  360. package/dist/composables/propCheck.js +78 -0
  361. package/dist/composables/realm.d.ts +6 -0
  362. package/dist/composables/realm.js +23 -0
  363. package/dist/composables/scroll.js +4 -2
  364. package/dist/composables/scrollMemory.d.ts +12 -1
  365. package/dist/composables/scrollMemory.js +36 -12
  366. package/dist/composables/spatialNav.js +2 -1
  367. package/dist/composables/textEntry.d.ts +17 -0
  368. package/dist/composables/textEntry.js +19 -0
  369. package/dist/composables/useAsyncTask.d.ts +6 -0
  370. package/dist/composables/useChartSync.d.ts +5 -0
  371. package/dist/composables/useChartSync.js +33 -0
  372. package/dist/composables/useCloseGuard.d.ts +3 -1
  373. package/dist/composables/useCloseGuard.js +6 -3
  374. package/dist/composables/useCommands.d.ts +50 -0
  375. package/dist/composables/useCommands.js +66 -0
  376. package/dist/composables/useField.d.ts +37 -0
  377. package/dist/composables/useField.js +27 -2
  378. package/dist/composables/useFitScale.js +4 -1
  379. package/dist/composables/useFormDraft.d.ts +13 -2
  380. package/dist/composables/useFormDraft.js +17 -9
  381. package/dist/composables/useHintSystem.js +2 -2
  382. package/dist/composables/useHotkeys.js +11 -32
  383. package/dist/composables/useInView.js +2 -1
  384. package/dist/composables/useInputFilter.js +7 -5
  385. package/dist/composables/useInputMask.js +5 -3
  386. package/dist/composables/useLinkedView.d.ts +97 -0
  387. package/dist/composables/useLinkedView.js +139 -0
  388. package/dist/composables/useOptimisticToggle.d.ts +3 -1
  389. package/dist/composables/useOptimisticToggle.js +2 -1
  390. package/dist/composables/useOverlayHistory.d.ts +7 -0
  391. package/dist/composables/useOverlayHistory.js +16 -2
  392. package/dist/composables/usePaintSelect.js +18 -14
  393. package/dist/composables/usePaletteCommands.d.ts +7 -0
  394. package/dist/composables/usePaletteCommands.js +12 -1
  395. package/dist/composables/usePersisted.d.ts +10 -0
  396. package/dist/composables/usePersisted.js +1 -1
  397. package/dist/composables/usePillTransition.d.ts +2 -2
  398. package/dist/composables/usePillTransition.js +5 -5
  399. package/dist/composables/useResizeObserver.js +2 -1
  400. package/dist/composables/useReveal.js +2 -1
  401. package/dist/composables/useRovingFocus.d.ts +2 -2
  402. package/dist/composables/useRovingFocus.js +9 -11
  403. package/dist/composables/useScramble.js +7 -1
  404. package/dist/composables/useShift.js +8 -6
  405. package/dist/composables/useTilt.js +2 -1
  406. package/dist/composables/useTween.js +9 -5
  407. package/dist/composables/useView.d.ts +1 -0
  408. package/dist/composables/useView.js +14 -2
  409. package/dist/composables/useZoomPan.js +2 -4
  410. package/dist/composables/windowing.d.ts +40 -0
  411. package/dist/composables/windowing.js +118 -0
  412. package/dist/icons-all.js +1 -1
  413. package/dist/icons.d.ts +4 -0
  414. package/dist/index.d.ts +30 -5
  415. package/dist/index.js +310 -264
  416. package/dist/layout/tree.js +19 -15
  417. package/dist/layout/useLayout.d.ts +2 -0
  418. package/dist/layout/useLayout.js +5 -1
  419. package/dist/layout/useWorkspaces.d.ts +52 -0
  420. package/dist/layout/useWorkspaces.js +140 -0
  421. package/dist/{chunks/backdrop-Dkf2vM81.js → theme/backdrop.js} +5 -5
  422. package/dist/theme/config.js +25 -0
  423. package/dist/theme/define.js +61 -0
  424. package/dist/{chunks/palette-Co8tSASo.js → theme/palette.js} +2 -2
  425. package/dist/theme/presets.js +239 -0
  426. package/dist/theme/studio.js +134 -0
  427. package/dist/theme/tokens.js +63 -0
  428. package/dist/theme/useTheme.d.ts +3 -2
  429. package/dist/theme/useTheme.js +325 -0
  430. package/package.json +8 -1
  431. package/src/component-names.ts +5 -1
  432. package/src/components/AppShellContextNav.vue +44 -0
  433. package/src/components/ChartRangeSelector.vue +120 -0
  434. package/src/components/IconCloseButton.vue +21 -0
  435. package/src/components/LayoutCanvas.vue +3 -0
  436. package/src/components/LayoutNode.vue +37 -38
  437. package/src/components/LpActivityLine.vue +4 -2
  438. package/src/components/LpAddressInput.vue +3 -3
  439. package/src/components/LpAppShell.vue +362 -24
  440. package/src/components/LpAutocomplete.vue +13 -30
  441. package/src/components/LpBottomNav.vue +9 -1
  442. package/src/components/LpBreadcrumbs.vue +2 -1
  443. package/src/components/LpButton.vue +11 -4
  444. package/src/components/LpChart.vue +336 -37
  445. package/src/components/LpCheckbox.vue +43 -9
  446. package/src/components/LpCodeBlock.vue +17 -23
  447. package/src/components/LpCollapse.vue +5 -4
  448. package/src/components/LpColorPicker.vue +8 -9
  449. package/src/components/LpCommandPalette.vue +133 -50
  450. package/src/components/LpContextMenu.vue +15 -13
  451. package/src/components/LpCountdown.vue +2 -1
  452. package/src/components/LpDataList.vue +13 -1
  453. package/src/components/LpDatePicker.vue +5 -17
  454. package/src/components/LpDateRangePicker.vue +5 -16
  455. package/src/components/LpDock.vue +25 -16
  456. package/src/components/LpDrawer.vue +70 -61
  457. package/src/components/LpDropdownMenu.vue +6 -10
  458. package/src/components/LpDropzone.vue +12 -7
  459. package/src/components/LpEditable.vue +6 -2
  460. package/src/components/LpFileTree.vue +507 -66
  461. package/src/components/LpFileTreeNode.vue +106 -5
  462. package/src/components/LpFormField.vue +1 -1
  463. package/src/components/LpFrame.vue +33 -13
  464. package/src/components/LpHoverCard.vue +30 -5
  465. package/src/components/LpIcon.vue +6 -2
  466. package/src/components/LpIdenticon.vue +2 -1
  467. package/src/components/LpImageEditor.vue +5 -2
  468. package/src/components/LpInput.vue +15 -23
  469. package/src/components/LpInspector.vue +197 -0
  470. package/src/components/LpLightbox.vue +74 -29
  471. package/src/components/LpLogInspector.vue +85 -0
  472. package/src/components/LpLogViewer.vue +239 -29
  473. package/src/components/LpMapPicker.vue +2 -1
  474. package/src/components/LpMasonry.vue +8 -5
  475. package/src/components/LpModal.vue +12 -12
  476. package/src/components/LpModalNav.vue +2 -21
  477. package/src/components/LpNumberField.vue +6 -15
  478. package/src/components/LpOtpInput.vue +2 -2
  479. package/src/components/LpPagination.vue +5 -3
  480. package/src/components/LpPhoneInput.vue +8 -19
  481. package/src/components/LpPie.vue +25 -23
  482. package/src/components/LpPipeline.vue +14 -1
  483. package/src/components/LpPopover.vue +23 -2
  484. package/src/components/LpProgress.vue +2 -1
  485. package/src/components/LpRadioGroup.vue +2 -2
  486. package/src/components/LpRangeSlider.vue +5 -6
  487. package/src/components/LpRevealOnScroll.vue +1 -1
  488. package/src/components/LpScrollArea.vue +28 -11
  489. package/src/components/LpSecretField.vue +2 -1
  490. package/src/components/LpSegmented.vue +6 -8
  491. package/src/components/LpSelect.vue +54 -24
  492. package/src/components/LpShaderBackdrop.vue +12 -9
  493. package/src/components/LpShift.vue +18 -15
  494. package/src/components/LpSidebar.vue +77 -74
  495. package/src/components/LpSidebarNav.vue +3 -2
  496. package/src/components/LpSidebarRow.vue +2 -1
  497. package/src/components/LpSlider.vue +5 -6
  498. package/src/components/LpSpotlight.vue +2 -1
  499. package/src/components/LpSwitch.vue +5 -3
  500. package/src/components/LpTable.vue +609 -93
  501. package/src/components/LpTableFilter.vue +1 -1
  502. package/src/components/LpTableOfContents.vue +16 -9
  503. package/src/components/LpTabs.vue +2 -1
  504. package/src/components/LpTagsInput.vue +12 -19
  505. package/src/components/LpTextarea.vue +5 -11
  506. package/src/components/LpThemeProvider.vue +29 -13
  507. package/src/components/LpTimeField.vue +4 -7
  508. package/src/components/LpToaster.vue +7 -6
  509. package/src/components/LpTooltip.vue +62 -18
  510. package/src/components/LpTopologyCanvas.vue +567 -42
  511. package/src/components/LpTour.vue +7 -5
  512. package/src/components/LpTruncate.vue +4 -3
  513. package/src/components/LpTypewriter.vue +2 -1
  514. package/src/components/LpVideoBackdrop.vue +11 -13
  515. package/src/components/LpWindow.vue +373 -0
  516. package/src/components/LpWorkspaceSwitcher.vue +116 -0
  517. package/src/components/PickerField.vue +2 -2
  518. package/src/components/SliderRail.vue +10 -0
  519. package/src/components/address.ts +3 -1
  520. package/src/components/appShell.ts +74 -0
  521. package/src/components/calendar.ts +12 -5
  522. package/src/components/chart.ts +27 -3
  523. package/src/components/chartWindow.ts +198 -0
  524. package/src/components/color.ts +2 -1
  525. package/src/components/commandPalette.ts +66 -5
  526. package/src/components/controlSize.ts +6 -0
  527. package/src/components/dropdown.ts +3 -0
  528. package/src/components/exportData.ts +33 -18
  529. package/src/components/fileTree.ts +215 -0
  530. package/src/components/identicon.ts +4 -2
  531. package/src/components/logTools.ts +298 -0
  532. package/src/components/meter.ts +2 -1
  533. package/src/components/pipeline.ts +6 -0
  534. package/src/components/relativeTime.ts +2 -1
  535. package/src/components/reveal.ts +3 -1
  536. package/src/components/select.ts +14 -0
  537. package/src/components/sidebar.ts +1 -0
  538. package/src/components/tableFeatures.ts +149 -0
  539. package/src/components/topologyGraph.ts +655 -0
  540. package/src/components/tour.ts +5 -3
  541. package/src/components/videoBackdrop.ts +7 -6
  542. package/src/composables/clamp.ts +2 -0
  543. package/src/composables/devWarn.ts +19 -0
  544. package/src/composables/easing.ts +37 -12
  545. package/src/composables/overlayHistory.ts +24 -0
  546. package/src/composables/pointerDrag.ts +54 -0
  547. package/src/composables/propCheck.ts +90 -0
  548. package/src/composables/realm.ts +25 -0
  549. package/src/composables/scroll.ts +4 -2
  550. package/src/composables/scrollMemory.ts +56 -14
  551. package/src/composables/spatialNav.ts +2 -1
  552. package/src/composables/textEntry.ts +33 -0
  553. package/src/composables/useAsyncTask.ts +6 -0
  554. package/src/composables/useChartSync.ts +52 -0
  555. package/src/composables/useCloseGuard.ts +22 -10
  556. package/src/composables/useCommands.ts +123 -0
  557. package/src/composables/useField.ts +41 -2
  558. package/src/composables/useFitScale.ts +4 -1
  559. package/src/composables/useFormDraft.ts +38 -14
  560. package/src/composables/useHintSystem.ts +2 -2
  561. package/src/composables/useHotkeys.ts +19 -48
  562. package/src/composables/useInView.ts +2 -1
  563. package/src/composables/useInputFilter.ts +8 -5
  564. package/src/composables/useInputMask.ts +8 -3
  565. package/src/composables/useLinkedView.ts +222 -0
  566. package/src/composables/useOptimisticToggle.ts +7 -1
  567. package/src/composables/useOverlayHistory.ts +20 -0
  568. package/src/composables/usePaintSelect.ts +20 -14
  569. package/src/composables/usePaletteCommands.ts +13 -0
  570. package/src/composables/usePersisted.ts +10 -0
  571. package/src/composables/usePillTransition.ts +9 -6
  572. package/src/composables/useResizeObserver.ts +2 -1
  573. package/src/composables/useReveal.ts +2 -1
  574. package/src/composables/useRovingFocus.ts +11 -14
  575. package/src/composables/useScramble.ts +12 -1
  576. package/src/composables/useShift.ts +8 -6
  577. package/src/composables/useTilt.ts +2 -1
  578. package/src/composables/useTween.ts +10 -5
  579. package/src/composables/useView.ts +17 -3
  580. package/src/composables/useZoomPan.ts +2 -5
  581. package/src/composables/windowing.ts +190 -0
  582. package/src/icons.ts +27 -6
  583. package/src/index.ts +41 -4
  584. package/src/kit-icons.ts +1 -1
  585. package/src/layout/tree.ts +31 -16
  586. package/src/layout/useLayout.ts +6 -0
  587. package/src/layout/useWorkspaces.ts +234 -0
  588. package/src/nuxt.ts +9 -1
  589. package/src/runtime/propCheck.plugin.ts +10 -0
  590. package/src/theme/studio.ts +7 -5
  591. package/src/theme/useTheme.ts +89 -15
  592. package/src/tokens/tokens.css +8 -1
  593. package/dist/assets/LpTabs-CJ0rYQbx.css +0 -18
  594. package/dist/chunks/LpAppShell-Dgejipzz.js +0 -319
  595. package/dist/chunks/LpFileTree-CooHinsC.js +0 -425
  596. package/dist/chunks/LpThemeProvider-8MG6yqU9.js +0 -640
  597. package/dist/chunks/LpTopologyCanvas-B1gIbS0n.js +0 -298
@@ -7,13 +7,22 @@ export default { inheritAttrs: false }
7
7
 
8
8
  <script setup lang="ts">
9
9
  /*
10
- * Full application shell: a fixed-height (h-dvh) flex frame holding the side
11
- * navigation rail, a top header bar, and a single scrollable main region.
10
+ * Full application shell: the side navigation rail, a top header bar and the
11
+ * page, which is what scrolls.
12
+ *
13
+ * By default the page is the document: it scrolls the window, and the rail,
14
+ * the header and the bottom bar are sticky beside and above it. So everything
15
+ * a browser does with a long page keeps working — a full-page screenshot,
16
+ * printing, the phone's address bar folding away, pull to refresh, a tap on
17
+ * the status bar. `scroll="region"` keeps the older frame instead: the shell
18
+ * is exactly the viewport and the page scrolls inside main (LpScrollArea),
19
+ * which suits a shell embedded in a page of its own. A `fullBleed` page is
20
+ * held to the viewport in either.
12
21
  *
13
22
  * It owns the things every product's cabinet/console layout was re-implementing
14
23
  * by hand — and getting subtly wrong:
15
- * - the rail is full viewport height and never scrolls with the page (only
16
- * `main` scrolls, via LpScrollArea), so the sidebar footer stays pinned;
24
+ * - the rail is full viewport height and never scrolls with the page, so the
25
+ * sidebar footer stays pinned;
17
26
  * - a responsive burger that drives LpSidebar's mobile drawer;
18
27
  * - a default `isActive` (path prefix-match) and a default page title derived
19
28
  * from the nav items, so the header title and the active pill never drift.
@@ -30,10 +39,28 @@ export default { inheritAttrs: false }
30
39
  * #header-title (override the computed title), and the default slot (page body).
31
40
  * Overlays that aren't part of the frame (command palette, toaster) live in the
32
41
  * host next to <LpAppShell>.
42
+ *
43
+ * Every option below is off until asked for, and the shell's markup without
44
+ * them is what it always was: `headerCollapse`, `backToTop`, `density`,
45
+ * `pageTransition`, `safeArea`, the #aside region and `adaptiveNav`.
33
46
  */
34
- import { computed, onMounted, ref, useId, watch } from "vue"
47
+ import { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, useSlots, watch } from "vue"
48
+ import { easingToken, durationToken, prefersReducedMotion } from "../composables/easing"
35
49
  import { useLabels } from "../composables/useLpConfig"
36
50
  import { useScrollMemory } from "../composables/scrollMemory"
51
+ import { nodeWindow, useView, viewWindow } from "../composables/useView"
52
+ import AppShellContextNav from "./AppShellContextNav.vue"
53
+ import {
54
+ contextNavItems,
55
+ DENSITY_CLASS,
56
+ headerCollapsedAfter,
57
+ NAV_BREAKPOINT_PX,
58
+ resolveNavMode,
59
+ type AdaptiveNav,
60
+ type NavBreakpoint,
61
+ type NavMode,
62
+ type ShellDensity,
63
+ } from "./appShell"
37
64
  import LpBottomNav from "./LpBottomNav.vue"
38
65
  import LpContextSwitcher from "./LpContextSwitcher.vue"
39
66
  import LpButton from "./LpButton.vue"
@@ -46,6 +73,9 @@ import { mergeClass, useMergedAttrs } from "../composables/useMergedClass"
46
73
 
47
74
  export type { SidebarItem, SidebarSection } from "./sidebar"
48
75
 
76
+ /* Past this much scrolled the way back up is worth a button. */
77
+ const BACK_TO_TOP_AFTER_PX = 600
78
+
49
79
  const props = withDefaults(
50
80
  defineProps<{
51
81
  /** Flat items, or sections for grouped nav. Forwarded to LpSidebar. */
@@ -91,10 +121,19 @@ const props = withDefaults(
91
121
  * (for a canvas / map / graph that fills the viewport and pans internally).
92
122
  */
93
123
  fullBleed?: boolean
124
+ /**
125
+ * What scrolls. `"document"` (the default): the window, with the rail,
126
+ * header and bottom bar sticky; a page that filled main with `h-full`
127
+ * grows with its content now, so a page meant to fill the screen asks for
128
+ * `min-h-[calc(100dvh-4rem)]` or `fullBleed`. `"region"`: the shell is
129
+ * the viewport and the page scrolls inside main. Read once: changing it
130
+ * remounts the page.
131
+ */
132
+ scroll?: "document" | "region"
94
133
  /** Loading skeleton for the nav. */
95
134
  loading?: boolean
96
135
  /** Text overrides: the burger's name and the skip link. */
97
- labels?: Partial<{ openMenu: string; skipToContent: string }>
136
+ labels?: Partial<{ openMenu: string; skipToContent: string; backToTop: string; aside: string; contextNav: string }>
98
137
  /** Icon-only rail (v-model:compact). Forwarded to LpSidebar. */
99
138
  compact?: boolean
100
139
  /** The person sizes the rail and folds it to icons. Forwarded to LpSidebar. */
@@ -123,14 +162,50 @@ const props = withDefaults(
123
162
  /**
124
163
  * On phones, the main pages along the bottom instead of the burger, with
125
164
  * "More" for the rest (it opens the drawer). `true` takes the first four
126
- * pages, a number that many, a list of ids those pages.
165
+ * pages, a number that many, a list of ids those pages. Top-level pages
166
+ * only: an id from `children` is ignored, and such a page stays behind "More".
127
167
  */
128
168
  bottomNav?: boolean | number | string[]
169
+ /**
170
+ * The header leaves as the page scrolls down and returns on the way up,
171
+ * giving a small screen its height back. A keyboard user tabbing into it
172
+ * brings it back too. A full-bleed page owns its scrolling, so the header
173
+ * stays there.
174
+ */
175
+ headerCollapse?: boolean
176
+ /** A button back to the top of a long page; a number sets how far down it appears, in px. */
177
+ backToTop?: boolean | number
178
+ /** How tight the page sits in main. Unset keeps the shell's own padding. */
179
+ density?: ShellDensity
180
+ /**
181
+ * A new `path` fades the page and the header's title and actions in as one,
182
+ * on the same clock, so actions arriving by teleport do not land apart from
183
+ * their page. Nothing is remounted. Skipped under reduced motion.
184
+ */
185
+ pageTransition?: boolean
186
+ /**
187
+ * Keep the header and the page's end clear of a notch and a home bar
188
+ * (`viewport-fit=cover`). The bottom bar always does.
189
+ */
190
+ safeArea?: boolean
191
+ /** Tailwind breakpoint below which the #aside region is hidden. Defaults to `mobileBreakpoint`. */
192
+ asideBreakpoint?: NavBreakpoint
193
+ /** Classes for the #aside region, its width above all. */
194
+ asideClass?: string
195
+ /**
196
+ * One model for the navigation across widths; see `NavMode`. Below
197
+ * `mobileBreakpoint` it is `bottomNav` or the drawer, as ever; this adds the
198
+ * fold to icons in between and the row of sub-pages where the rail is gone.
199
+ * A `compact` the app controls wins, and a person folding the rail by hand
200
+ * holds until the width crosses into another mode.
201
+ */
202
+ adaptiveNav?: AdaptiveNav
129
203
  }>(),
130
204
  {
131
205
  mobileBreakpoint: "lg",
132
206
  divider: true,
133
207
  contentClass: "mx-auto w-full max-w-6xl",
208
+ scroll: "document",
134
209
  // Unset, not false: an absent boolean would hand the sidebar "expanded"
135
210
  // and override the state it remembers.
136
211
  compact: undefined,
@@ -144,6 +219,8 @@ const emit = defineEmits<{
144
219
  (e: "update:compact", value: boolean): void
145
220
  (e: "update:pinned", ids: string[]): void
146
221
  (e: "update:context", id: string): void
222
+ /** The navigation the width settled on, with `adaptiveNav` or without. */
223
+ (e: "nav-mode", mode: NavMode): void
147
224
  }>()
148
225
 
149
226
  const switching = computed(() => (props.contexts?.length ?? 0) > 1)
@@ -153,11 +230,16 @@ const flatItems = computed<SidebarItem[]>(() =>
153
230
  (props.sections ? props.sections.flatMap((s) => s.items) : (props.items ?? [])).flatMap((i) => [i, ...(i.children ?? [])]),
154
231
  )
155
232
 
233
+ // Compared in one spelling, so "/billing/" and "//billing" are "/billing".
234
+ const SLASH_RUN = /\/{2,}/g
235
+ const TRAILING_SLASH = /(?<=.)\/$/
236
+ const cleanPath = computed(() => props.path?.replace(SLASH_RUN, "/").replace(TRAILING_SLASH, ""))
237
+
156
238
  // Default active test: exact match for "/", prefix match (on a segment
157
239
  // boundary) otherwise — so "/billing" doesn't light up on "/billing-x".
158
240
  function matchesPath(item: SidebarItem): boolean {
159
- if (item.id === "/") return props.path === "/"
160
- return props.path === item.id || props.path!.startsWith(`${item.id}/`)
241
+ if (item.id === "/") return cleanPath.value === "/"
242
+ return cleanPath.value === item.id || cleanPath.value!.startsWith(`${item.id}/`)
161
243
  }
162
244
 
163
245
  // Only the LONGEST match lights up. A parent whose id prefixes its children —
@@ -193,7 +275,7 @@ const pageTitle = computed(() => {
193
275
  const drawerOpen = defineModel<boolean>("open", { default: false })
194
276
  const text = useLabels(
195
277
  "appShell",
196
- { openMenu: "Open menu", skipToContent: "Skip to content" },
278
+ { openMenu: "Open menu", skipToContent: "Skip to content", backToTop: "Back to top", aside: "Details", contextNav: "In this section" },
197
279
  () => props.labels,
198
280
  )
199
281
 
@@ -204,27 +286,218 @@ const text = useLabels(
204
286
  * A full-bleed page owns its scrolling, so the shell leaves it alone.
205
287
  */
206
288
  const scrollArea = ref<{ viewportEl: HTMLElement | null } | null>(null)
289
+ const asideArea = ref<{ viewportEl: HTMLElement | null } | null>(null)
290
+ const pageEl = ref<HTMLElement | null>(null)
207
291
  const bleedId = `lp-shell-bleed-${useId()}`
208
292
  const mounted = ref(false)
209
293
  onMounted(() => (mounted.value = true))
210
294
  const bleeding = computed(() => props.fullBleed && mounted.value)
295
+ const documentScroll = props.scroll !== "region"
296
+ // The window scrolls the page; a full-bleed page holds the shell to the viewport.
297
+ const windowScrolls = computed(() => documentScroll && !props.fullBleed)
211
298
  const mainId = `lp-main-${useId()}`
212
299
 
213
300
  // Focus instead of following the hash, which a router would treat as a navigation.
301
+ const mainEl = ref<HTMLElement | null>(null)
214
302
  function skipToMain() {
215
- document.getElementById(mainId)?.focus()
303
+ mainEl.value?.focus()
216
304
  }
217
305
 
218
306
  watch(
219
307
  () => props.path,
220
308
  () => {
221
309
  if (drawerOpen.value) drawerOpen.value = false
310
+ headerCollapsed.value = false
222
311
  },
223
312
  )
313
+ function pageScroller(): HTMLElement | null | undefined {
314
+ if (bleeding.value) return null
315
+ if (!documentScroll) return scrollArea.value?.viewportEl
316
+ return (pageEl.value?.ownerDocument.scrollingElement as HTMLElement | null | undefined) ?? null
317
+ }
224
318
  useScrollMemory(
225
- () => (bleeding.value ? null : scrollArea.value?.viewportEl),
319
+ pageScroller,
226
320
  () => props.path,
227
321
  () => props.hash,
322
+ "",
323
+ // The sticky header covers the top of the window; a region starts below it.
324
+ () => (documentScroll && !bleeding.value ? (headerEl.value?.offsetHeight ?? 0) : 0),
325
+ )
326
+ // The side region keeps its own place on the same address.
327
+ useScrollMemory(
328
+ () => asideArea.value?.viewportEl,
329
+ () => props.path,
330
+ () => undefined,
331
+ "aside:",
332
+ )
333
+
334
+ const headerCollapsed = ref(false)
335
+ const scrolledFar = ref(false)
336
+ const backToTopAfter = computed(() => (typeof props.backToTop === "number" ? props.backToTop : BACK_TO_TOP_AFTER_PX))
337
+ let lastTop = 0
338
+
339
+ function onScrolled(top: number, max: number) {
340
+ if (!props.headerCollapse && !props.backToTop) return
341
+ if (props.headerCollapse) headerCollapsed.value = headerCollapsedAfter(headerCollapsed.value, lastTop, top, max)
342
+ scrolledFar.value = top > backToTopAfter.value
343
+ lastTop = top
344
+ }
345
+ function onMainScroll(event: Event) {
346
+ const view = event.target as HTMLElement
347
+ onScrolled(view.scrollTop, view.scrollHeight - view.clientHeight)
348
+ }
349
+ function onWindowScroll() {
350
+ const view = pageScroller()
351
+ if (view) onScrolled(view.scrollTop, view.scrollHeight - view.clientHeight)
352
+ }
353
+
354
+ const headerHidden = computed(() => headerCollapsed.value && !bleeding.value)
355
+ const HEADER_HEIGHT = "4rem"
356
+ const headerHeight = computed(() => (props.safeArea ? `calc(${HEADER_HEIGHT} + env(safe-area-inset-top))` : HEADER_HEIGHT))
357
+
358
+ /*
359
+ * A full-bleed page shrinks the document to the viewport, which loses the
360
+ * place; it is taken back on the way out, unless the page changed in between.
361
+ *
362
+ * No scroll-padding-top on the document for the sticky header: with it the
363
+ * browser counts the header as covered by itself, and a control in it taking
364
+ * focus scrolled the page (by 350 px, measured). Anchors get the header's
365
+ * height from scroll memory instead.
366
+ */
367
+ let bleedTop: number | undefined
368
+ watch(
369
+ () => props.path,
370
+ () => (bleedTop = undefined),
371
+ )
372
+ watch(bleeding, (bleed) => {
373
+ if (!documentScroll) return
374
+ const view = pageEl.value?.ownerDocument.scrollingElement
375
+ if (!view) return
376
+ if (bleed) bleedTop = view.scrollTop
377
+ else if (bleedTop !== undefined) {
378
+ const top = bleedTop
379
+ bleedTop = undefined
380
+ void nextTick(() => view.scrollTo({ top, behavior: "instant" }))
381
+ }
382
+ })
383
+ let releaseDocument: (() => void) | undefined
384
+ onMounted(() => {
385
+ if (!documentScroll || !pageEl.value) return
386
+ const win = nodeWindow(pageEl.value)
387
+ win.addEventListener("scroll", onWindowScroll, { passive: true })
388
+ releaseDocument = () => win.removeEventListener("scroll", onWindowScroll)
389
+ })
390
+ onBeforeUnmount(() => releaseDocument?.())
391
+
392
+ function scrollToTop() {
393
+ const view = pageScroller()
394
+ if (!view) return
395
+ view.scrollTo({ top: 0, behavior: prefersReducedMotion(nodeWindow(view)) ? "instant" : "smooth" })
396
+ // The button is about to leave, and focus must not leave with it.
397
+ mainEl.value?.focus()
398
+ }
399
+
400
+ const headerClass = computed(() =>
401
+ mergeClass(
402
+ "relative flex h-16 shrink-0 items-center gap-3 border-b border-line bg-surface px-4 md:px-6",
403
+ windowScrolls.value && `${STICKY} top-0`,
404
+ props.headerCollapse &&
405
+ (windowScrolls.value ? "transition-transform" : "transition-[margin-top]") + " duration-[var(--duration-medium)] ease-[var(--ease-settle)]",
406
+ props.safeArea &&
407
+ "h-[calc(4rem+env(safe-area-inset-top))] pt-[env(safe-area-inset-top)] pl-[max(1rem,env(safe-area-inset-left))] pr-[max(1rem,env(safe-area-inset-right))] md:pl-[max(1.5rem,env(safe-area-inset-left))] md:pr-[max(1.5rem,env(safe-area-inset-right))]",
408
+ ),
409
+ )
410
+ const pageClass = computed(() =>
411
+ mergeClass(
412
+ DENSITY_CLASS[props.density ?? "comfortable"],
413
+ props.contentClass,
414
+ props.safeArea && !props.bottomNav && "pb-[calc(1.5rem+env(safe-area-inset-bottom))]",
415
+ ),
416
+ )
417
+
418
+ // Fades on one clock, read from the page's own tokens. Post flush: a teleported
419
+ // action arrives with the render that brought the page.
420
+ const headerEl = ref<HTMLElement | null>(null)
421
+ const bleedEl = ref<HTMLElement | null>(null)
422
+ watch(
423
+ () => props.path,
424
+ () => {
425
+ const content = bleeding.value ? bleedEl.value : documentScroll ? pageEl.value : scrollArea.value?.viewportEl
426
+ if (!props.pageTransition || !content || prefersReducedMotion(nodeWindow(content))) return
427
+ const timing = { duration: durationToken("duration-medium", undefined, content), easing: easingToken("ease-settle", undefined, content) }
428
+ // Title and actions; the burger is the frame, not the page.
429
+ const header = headerEl.value?.querySelectorAll(":scope > :not(button)") ?? []
430
+ for (const el of [content, ...header]) el.animate({ opacity: [0, 1] }, timing)
431
+ },
432
+ { flush: "post" },
433
+ )
434
+
435
+ const slots = useSlots()
436
+ const hasAside = computed(() => !!slots.aside)
437
+ const ASIDE_VISIBLE: Record<NavBreakpoint, string> = {
438
+ sm: "hidden sm:flex",
439
+ md: "hidden md:flex",
440
+ lg: "hidden lg:flex",
441
+ xl: "hidden xl:flex",
442
+ "2xl": "hidden 2xl:flex",
443
+ }
444
+ const asideClasses = computed(() =>
445
+ mergeClass(
446
+ "min-h-0 w-80 flex-col border-l border-line bg-surface",
447
+ windowScrolls.value &&
448
+ "sticky top-(--lp-shell-top) h-[calc(100dvh-var(--lp-shell-top))] self-start transition-[top,height] duration-[var(--duration-medium)] ease-[var(--ease-settle)]",
449
+ ASIDE_VISIBLE[props.asideBreakpoint ?? props.mobileBreakpoint],
450
+ props.asideClass,
451
+ ),
452
+ )
453
+
454
+ /*
455
+ * The aside takes a grid column rather than a wrapper around main: a wrapper
456
+ * that comes and goes with the slot would remount the page. Header, the
457
+ * sub-page row and the bottom bar span both columns.
458
+ */
459
+ const columnClass = computed(() => (hasAside.value ? "grid min-w-0 flex-1 grid-cols-[minmax(0,1fr)_auto]" : "flex min-w-0 flex-1 flex-col"))
460
+ const columnStyle = computed(() => ({
461
+ ...(hasAside.value ? { gridTemplateRows: `auto ${contextVisible.value ? "auto " : ""}minmax(0,1fr) auto` } : {}),
462
+ ...(windowScrolls.value ? { "--lp-shell-top": shellTop.value } : {}),
463
+ }))
464
+ const spanClass = computed(() => (hasAside.value ? "col-span-2" : undefined))
465
+
466
+ // One width rather than a media query per breakpoint: those report one by one,
467
+ // so a jump across two passed through a mode the window was never in.
468
+ const view = useView()
469
+ const width = ref(0)
470
+ const measureWidth = () => (width.value = viewWindow(view.value).innerWidth)
471
+ onMounted(() => {
472
+ measureWidth()
473
+ viewWindow(view.value).addEventListener("resize", measureWidth)
474
+ })
475
+ onBeforeUnmount(() => viewWindow(view.value).removeEventListener("resize", measureWidth))
476
+
477
+ // Before mount there is no width: a server render is the plain rail.
478
+ const navMode = computed<NavMode>(() =>
479
+ mounted.value
480
+ ? resolveNavMode((breakpoint) => width.value >= NAV_BREAKPOINT_PX[breakpoint], {
481
+ mobileBreakpoint: props.mobileBreakpoint,
482
+ bottomNav: !!props.bottomNav,
483
+ compactBelow: props.adaptiveNav?.compactBelow,
484
+ })
485
+ : "rail",
486
+ )
487
+ watch(navMode, (mode) => emit("nav-mode", mode))
488
+
489
+ // What a person chose by hand, for as long as the width stays in this mode.
490
+ const handCompact = ref<boolean | undefined>()
491
+ watch(navMode, () => (handCompact.value = undefined))
492
+ const railCompact = computed(() => props.compact ?? handCompact.value ?? (navMode.value === "compact" ? true : undefined))
493
+ function onCompact(value: boolean) {
494
+ if (props.adaptiveNav) handCompact.value = value
495
+ emit("update:compact", value)
496
+ }
497
+
498
+ const contextItems = computed(() => (props.adaptiveNav?.contextNav ? contextNavItems(topItems.value, activeTest.value) : []))
499
+ const contextVisible = computed(
500
+ () => (navMode.value === "bottom" || navMode.value === "drawer") && (contextItems.value.length > 0 || (!!slots["context-nav"] && !!props.adaptiveNav?.contextNav)),
228
501
  )
229
502
 
230
503
  function onSelect(item: SidebarItem) {
@@ -260,11 +533,33 @@ function onBottom(item: SidebarItem) {
260
533
 
261
534
  // Expose the burger trigger so a host can omit its own header entirely.
262
535
  const _openDrawer = () => (drawerOpen.value = true)
263
- defineExpose({ openDrawer: _openDrawer })
536
+ defineExpose({ openDrawer: _openDrawer, navMode })
264
537
 
265
- // The shell claims the full viewport height, which is wrong when it is embedded in a page that already has a header of its own.
266
- const { class: rootClass, attrs: rest } = useMergedAttrs(
267
- "flex h-dvh overflow-hidden bg-surface text-ink",
538
+ // In region mode the shell claims the full viewport height, which is wrong when it is embedded in a page that already has a header of its own.
539
+ // The document mode clips sideways with `clip`, not `hidden`: a hidden overflow would make the root the sticky children's scroller.
540
+ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
541
+ windowScrolls.value ? "flex min-h-dvh overflow-x-clip bg-surface text-ink" : "flex h-dvh overflow-hidden bg-surface text-ink",
542
+ )
543
+
544
+ /*
545
+ * What stays in view while the window scrolls the page. The header sits on
546
+ * top; the row of sub-pages and the aside sit under it, following it out
547
+ * when it collapses (--lp-shell-top is how much of the header is showing).
548
+ */
549
+ const STICKY = "sticky z-(--z-sticky)"
550
+ const railClass = computed(() => (windowScrolls.value ? "sticky top-0 flex h-dvh shrink-0 self-start" : "contents"))
551
+ const shellTop = computed(() => (headerHidden.value ? "0px" : headerHeight.value))
552
+ const BACK_TO_TOP_ABOVE_BAR: Record<NavBreakpoint, string> = {
553
+ sm: "bottom-[calc(4.5rem+env(safe-area-inset-bottom))] sm:bottom-[calc(1rem+env(safe-area-inset-bottom))]",
554
+ md: "bottom-[calc(4.5rem+env(safe-area-inset-bottom))] md:bottom-[calc(1rem+env(safe-area-inset-bottom))]",
555
+ lg: "bottom-[calc(4.5rem+env(safe-area-inset-bottom))] lg:bottom-[calc(1rem+env(safe-area-inset-bottom))]",
556
+ xl: "bottom-[calc(4.5rem+env(safe-area-inset-bottom))] xl:bottom-[calc(1rem+env(safe-area-inset-bottom))]",
557
+ "2xl": "bottom-[calc(4.5rem+env(safe-area-inset-bottom))] 2xl:bottom-[calc(1rem+env(safe-area-inset-bottom))]",
558
+ }
559
+ const backToTopClass = computed(() =>
560
+ windowScrolls.value
561
+ ? ["fixed right-6 z-(--z-sticky)", props.bottomNav ? BACK_TO_TOP_ABOVE_BAR[props.mobileBreakpoint] : "bottom-[calc(1rem+env(safe-area-inset-bottom))]"]
562
+ : "absolute bottom-[calc(1rem+env(safe-area-inset-bottom))] right-6",
268
563
  )
269
564
  </script>
270
565
 
@@ -279,6 +574,10 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
279
574
  >
280
575
  {{ text.skipToContent }}
281
576
  </a>
577
+ <!-- The rail's own box: a viewport-tall sticky column while the window
578
+ scrolls the page, and no box at all (contents) in the region frame,
579
+ where the rail is the frame's full height already. -->
580
+ <div :class="railClass">
282
581
  <LpSidebar
283
582
  :model-value="modelValue"
284
583
  :items="items"
@@ -292,7 +591,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
292
591
  :loading="loading"
293
592
  :collapsed="collapsed"
294
593
  :auto-collapse-after="autoCollapseAfter"
295
- :compact="compact"
594
+ :compact="railCompact"
296
595
  :resizable="resizable"
297
596
  :pinnable="pinnable"
298
597
  :pinned="pinned"
@@ -303,7 +602,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
303
602
  :hints="hints"
304
603
  class="shrink-0"
305
604
  @update:model-value="onModel"
306
- @update:compact="(v) => emit('update:compact', v)"
605
+ @update:compact="onCompact"
307
606
  @update:pinned="(v) => emit('update:pinned', v)"
308
607
  @update:open="(v) => (drawerOpen = v)"
309
608
  @update:collapsed="(v) => emit('update:collapsed', v)"
@@ -334,9 +633,10 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
334
633
  <slot name="sidebar-item" v-bind="slotProps" />
335
634
  </template>
336
635
  </LpSidebar>
636
+ </div>
337
637
 
338
638
  <!-- Main column: header + the single scroll region. -->
339
- <div class="flex min-w-0 flex-1 flex-col">
639
+ <div :class="columnClass" :style="columnStyle">
340
640
  <!-- `relative` so a consumer can centre something — section tabs, a
341
641
  search box — against the WHOLE bar. Centring it inside the title
342
642
  block instead makes its position depend on how wide the actions are,
@@ -344,7 +644,10 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
344
644
  59 px off centre with two actions and 77 px with three, which reads
345
645
  as the navigation drifting rather than the page changing. -->
346
646
  <header
347
- class="relative flex h-16 shrink-0 items-center gap-3 border-b border-line bg-surface px-4 md:px-6"
647
+ ref="headerEl"
648
+ :class="[headerClass, spanClass]"
649
+ :style="headerHidden ? (windowScrolls ? { transform: 'translateY(-100%)' } : { marginTop: `calc(-1 * ${headerHeight})` }) : undefined"
650
+ @focusin="headerCollapsed = false"
348
651
  >
349
652
  <LpButton
350
653
  :class="burgerClass"
@@ -398,26 +701,61 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
398
701
  tab going from a full-bleed page to a padded one cut instead of
399
702
  playing its transition, and lost the page's state. Until mount the
400
703
  page stays in the scroll region, which is what a server render has. -->
401
- <main :id="mainId" tabindex="-1" class="flex min-h-0 flex-1 flex-col outline-none">
402
- <div v-show="bleeding" :id="bleedId" class="min-h-0 flex-1 overflow-hidden" />
704
+ <template v-if="contextVisible">
705
+ <slot name="context-nav" :items="contextItems" :mode="navMode">
706
+ <AppShellContextNav
707
+ :class="[spanClass, windowScrolls && `${STICKY} top-(--lp-shell-top) transition-[top] duration-[var(--duration-medium)] ease-[var(--ease-settle)]`]"
708
+ :items="contextItems"
709
+ :is-active="activeTest"
710
+ :label="text.contextNav"
711
+ @select="onBottom"
712
+ />
713
+ </slot>
714
+ </template>
715
+ <main ref="mainEl" :id="mainId" tabindex="-1" :data-lp-density="density" :class="['flex min-h-0 flex-1 flex-col outline-none', backToTop && 'relative']">
716
+ <div ref="bleedEl" v-show="bleeding" :id="bleedId" class="min-h-0 flex-1 overflow-hidden" />
717
+ <div v-if="documentScroll" v-show="!bleeding" ref="pageEl" :class="['flex-1', pageClass]">
718
+ <Teleport :to="`#${bleedId}`" :disabled="!bleeding" defer>
719
+ <slot />
720
+ </Teleport>
721
+ </div>
403
722
  <LpScrollArea
723
+ v-else
404
724
  v-show="!bleeding"
405
725
  ref="scrollArea"
406
726
  class="min-h-0 flex-1"
407
- :content-class="mergeClass('px-4 py-6 md:px-6', contentClass)"
727
+ :content-class="pageClass"
728
+ @scroll="onMainScroll"
408
729
  >
409
730
  <Teleport :to="`#${bleedId}`" :disabled="!bleeding" defer>
410
731
  <slot />
411
732
  </Teleport>
412
733
  </LpScrollArea>
734
+ <LpButton
735
+ v-if="backToTop && scrolledFar && !bleeding"
736
+ :class="backToTopClass"
737
+ variant="outline"
738
+ size="sm"
739
+ square
740
+ :aria-label="text.backToTop"
741
+ @click="scrollToTop"
742
+ >
743
+ <LpIcon name="lucide:arrow-up" :size="16" />
744
+ </LpButton>
413
745
  </main>
746
+ <aside v-if="hasAside" :aria-label="text.aside" :class="asideClasses">
747
+ <LpScrollArea ref="asideArea" class="min-h-0 flex-1" content-class="p-4">
748
+ <slot name="aside" />
749
+ </LpScrollArea>
750
+ </aside>
414
751
  <LpBottomNav
415
752
  v-if="bottomNav"
416
- :class="burgerHidden[mobileBreakpoint]"
753
+ :class="[burgerHidden[mobileBreakpoint], spanClass, windowScrolls && `${STICKY} bottom-0`]"
417
754
  :items="bottomItems"
418
755
  :is-active="activeTest"
419
756
  more
420
757
  :more-active="moreActive"
758
+ :more-expanded="drawerOpen"
421
759
  @select="onBottom"
422
760
  @more="drawerOpen = true"
423
761
  />
@@ -10,6 +10,8 @@ export type AutocompleteLabels = {
10
10
  </script>
11
11
 
12
12
  <script setup lang="ts">
13
+ import IconCloseButton from "./IconCloseButton.vue"
14
+ import { CONTROL_SIZE } from "./controlSize"
13
15
  import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
14
16
  /*
15
17
  * Free-text autocomplete — a text field that suggests as you type but never
@@ -33,11 +35,11 @@ import {
33
35
  ComboboxRoot,
34
36
  ComboboxViewport,
35
37
  } from "reka-ui"
36
- import { computed, nextTick, ref, useAttrs, useId, watch } from "vue"
37
- import { useFieldControl } from "../composables/useField"
38
+ import { computed, nextTick, ref, useId, watch } from "vue"
39
+ import { useAttrsField, mergeDescribedBy } from "../composables/useField"
38
40
  import { useInputFilter } from "../composables/useInputFilter"
39
41
  import { useLabels } from "../composables/useLpConfig"
40
- import { CLOSE_ICON, OPTION_ITEM, optionStagger, POPOVER_PANEL } from "./dropdown"
42
+ import { CLOSE_ICON, OPTION_ITEM, optionStagger, COMBOBOX_PANEL, POPOVER_PANEL } from "./dropdown"
41
43
  import LpIcon from "./LpIcon.vue"
42
44
  import OptionContent from "./OptionContent.vue"
43
45
  import type { LpOptionBase, OptionSelection } from "./options"
@@ -115,18 +117,12 @@ const text = computed<string>({
115
117
 
116
118
  // class/style lay out the whole control; every other attr (name, autocomplete,
117
119
  // @blur, @keydown…) belongs to the text <input>.
118
- const attrs = useAttrs()
119
120
  const inputAttrs = computed(() => {
120
121
  const { class: _c, style: _s, ...rest } = attrs
121
122
  return rest
122
123
  })
123
124
 
124
- const field = useFieldControl(() => ({
125
- wrapped: false,
126
- id: typeof attrs.id === "string" ? attrs.id : undefined,
127
- invalid: props.invalid || isInvalid(props.modelValue),
128
- disabled: props.disabled,
129
- }))
125
+ const { attrs, field } = useAttrsField(() => ({ invalid: props.invalid || isInvalid(props.modelValue), disabled: props.disabled }))
130
126
  const isInvalidState = computed(() => field.value.invalid)
131
127
  const labels = useLabels<AutocompleteLabels>(
132
128
  "autocomplete",
@@ -179,7 +175,7 @@ watch(
179
175
  // reader is told who it is through a description.
180
176
  const selectedId = useId()
181
177
  const describedBy = computed(() =>
182
- [field.value.describedBy, showSelected.value ? selectedId : undefined].filter(Boolean).join(" ") || undefined,
178
+ mergeDescribedBy(attrs["aria-describedby"], field.value.describedBy, showSelected.value ? selectedId : undefined),
183
179
  )
184
180
 
185
181
  // Threshold met AND something to show → the list is allowed to open.
@@ -210,11 +206,7 @@ function onOpenChange(next: boolean) {
210
206
 
211
207
  const hasValue = computed(() => (props.modelValue ?? "").length > 0)
212
208
 
213
- const anchorSize = {
214
- sm: "h-(--size-control-sm) text-xs",
215
- md: "h-(--size-control-md) text-sm",
216
- lg: "h-(--size-control-lg) text-sm",
217
- }
209
+ const anchorSize = CONTROL_SIZE
218
210
 
219
211
  function onInput(event: Event) {
220
212
  filterInput(event)
@@ -373,20 +365,13 @@ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.v
373
365
  </span>
374
366
  </Transition>
375
367
  </div>
376
- <button
368
+ <IconCloseButton
377
369
  v-if="clearable && hasValue && !field.disabled"
378
- type="button"
379
- class="group flex shrink-0 items-center text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink"
380
- :aria-label="labels.clear"
370
+ inline
371
+ :label="labels.clear"
381
372
  @pointerdown.prevent
382
373
  @click="clear"
383
- >
384
- <LpIcon
385
- name="lucide:x"
386
- :size="16"
387
- :class="CLOSE_ICON"
388
- />
389
- </button>
374
+ />
390
375
  </ComboboxAnchor>
391
376
 
392
377
  <!-- A press on the list keeps focus in the field. Otherwise the field
@@ -397,9 +382,7 @@ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.v
397
382
  <ComboboxContent
398
383
  @interact-outside="guardOutside"
399
384
  @escape-key-down="guardOutside"
400
- :collision-padding="8"
401
- position="popper"
402
- :side-offset="6"
385
+ v-bind="COMBOBOX_PANEL"
403
386
  :class="[POPOVER_PANEL, 'z-(--z-popover) max-h-72 w-(--reka-combobox-trigger-width) overflow-hidden']"
404
387
  @open-auto-focus.prevent
405
388
  @mousedown.prevent