@leavepulse/ui 0.77.2 → 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.
- package/README.md +68 -0
- package/dist/assets/{LpCommandPalette-DOIZHw-g.css → LpCommandPalette-DDJKICKh.css} +19 -19
- package/dist/assets/{LpDock-Cbh0wQUv.css → LpDock-Dw82tWVD.css} +3 -3
- package/dist/assets/{LpDropzone-B_qRq1-R.css → LpDropzone-DNbh_NFJ.css} +10 -10
- package/dist/assets/{LpImageEditor-XM5bAziW.css → LpImageEditor-Bgu20XLs.css} +6 -6
- package/dist/assets/{LpTable-Bv-EcQs7.css → LpTable-CCk0ZHo7.css} +19 -19
- package/dist/assets/LpTabs-Bh7a-Bdl.css +18 -0
- package/dist/chunks/AppShellContextNav-C6Ho2mof.js +49 -0
- package/dist/chunks/ChartRangeSelector-HO0sMMz6.js +129 -0
- package/dist/chunks/{ContextMenuItems-BXumicbh.js → ContextMenuItems-DmaKSJVv.js} +1 -1
- package/dist/chunks/{DropzoneLinkField-C-9BCGJn.js → DropzoneLinkField-DXIr0eFz.js} +2 -2
- package/dist/chunks/{FieldWrapper-CORPzo9s.js → FieldWrapper-DnuMTrqW.js} +1 -1
- package/dist/chunks/IconCloseButton-COZEsa9u.js +29 -0
- package/dist/chunks/{LayoutCanvas-CNOAUk7D.js → LayoutCanvas-CZKOltfA.js} +1 -1
- package/dist/chunks/{LayoutNode-DF5BMN0J.js → LayoutNode-D2f7v683.js} +45 -45
- package/dist/chunks/{LpActionBar-BP1ld7-S.js → LpActionBar-BGMk5KoG.js} +3 -3
- package/dist/chunks/{LpActivityLine-CJvzrNpj.js → LpActivityLine-Ea-t5Vza.js} +4 -2
- package/dist/chunks/{LpAddressInput-sYMvYzcY.js → LpAddressInput-ZqoZ-N2Y.js} +12 -11
- package/dist/chunks/{LpAlert-n5XPAZfv.js → LpAlert-CjvTZIKF.js} +3 -3
- package/dist/chunks/LpAppShell-BNAVTQsY.js +582 -0
- package/dist/chunks/{LpAutocomplete-DOu7FN5P.js → LpAutocomplete-DkY6CSyD.js} +26 -42
- package/dist/chunks/{LpAvatar-Cg6Az3wz.js → LpAvatar-cQVsNk6F.js} +2 -2
- package/dist/chunks/{LpBadge-wUYLN4U9.js → LpBadge-D5QQdDQ_.js} +2 -2
- package/dist/chunks/{LpBottomNav-CbNQQPuL.js → LpBottomNav-zxLGGbjm.js} +9 -2
- package/dist/chunks/{LpBreadcrumbs-DPdbth-c.js → LpBreadcrumbs-CIIBsygI.js} +4 -3
- package/dist/chunks/{LpButton-CFb44IjC.js → LpButton-DSHioIfD.js} +5 -4
- package/dist/chunks/{LpCalendar-CuoWks-0.js → LpCalendar-D4-LVjfF.js} +1 -1
- package/dist/chunks/{LpCard-r13WRqox.js → LpCard-BPtYXwnT.js} +1 -1
- package/dist/chunks/{LpCheckbox-Cs4SHaHX.js → LpCheckbox-D6RuDW4X.js} +38 -16
- package/dist/chunks/{LpCodeBlock-DRC8VbXW.js → LpCodeBlock-DuzmnIHl.js} +19 -41
- package/dist/chunks/{LpCodeDisplay-CE9TxCA4.js → LpCodeDisplay-CO8fngxa.js} +5 -5
- package/dist/chunks/{LpCollapse-CWS9M_lm.js → LpCollapse-DrAw5DBZ.js} +5 -4
- package/dist/chunks/{LpColorPicker-1y1QJHZR.js → LpColorPicker-BxAHEnR5.js} +11 -12
- package/dist/chunks/{LpCommandScope-CtUhJe8T.js → LpCommandScope-DRUvHiX7.js} +1 -1
- package/dist/chunks/{LpConfirmDialog-DlD45Bi_.js → LpConfirmDialog-6oCIJTDa.js} +3 -3
- package/dist/chunks/{LpConfirmHost-BIWjBLmI.js → LpConfirmHost-DgHvfT6s.js} +1 -1
- package/dist/chunks/{LpContextMenu-rx2tCjwk.js → LpContextMenu--eMGIql6.js} +13 -15
- package/dist/chunks/{LpContextSwitcher-Dwh6dyID.js → LpContextSwitcher-1C1tExdl.js} +2 -2
- package/dist/chunks/{LpCopyButton-xF7wFVoU.js → LpCopyButton-7EOSIuwa.js} +3 -3
- package/dist/chunks/{LpCountdown-DgeY3cLG.js → LpCountdown-DVl7vXI1.js} +4 -3
- package/dist/chunks/{LpDataList-Jxyl9H5w.js → LpDataList-DgBeM01y.js} +13 -4
- package/dist/chunks/{LpDatePicker-CA0QTfBC.js → LpDatePicker-DwSDY0mt.js} +12 -18
- package/dist/chunks/{LpDateRangePicker-u3J51xXk.js → LpDateRangePicker-CNQXdTVv.js} +10 -16
- package/dist/chunks/{LpDisclosure-Cpa7Hc5T.js → LpDisclosure-DG7JCo-d.js} +2 -2
- package/dist/chunks/{LpDrawer-CrG8t_KP.js → LpDrawer-Bt9UUqt_.js} +61 -61
- package/dist/chunks/{LpDropdownMenu-Dl7Z4ZhS.js → LpDropdownMenu-CLzxIOgM.js} +9 -17
- package/dist/chunks/{LpEditable-Cy6GLbFx.js → LpEditable-DVT0Rh8F.js} +9 -6
- package/dist/chunks/{LpEmptyState-EQHiRYhG.js → LpEmptyState-DurPF001.js} +1 -1
- package/dist/chunks/{LpFieldNotice-CzQ2_5rQ.js → LpFieldNotice-DJ10cYIo.js} +1 -1
- package/dist/chunks/LpFileTree-BSx1kBoV.js +735 -0
- package/dist/chunks/{LpFileTreeNode-cT6BIWvz.js → LpFileTreeNode-4dVmFHb3.js} +116 -34
- package/dist/chunks/{LpFormField-Ch69Jd-X.js → LpFormField-BKYn9dyP.js} +3 -3
- package/dist/chunks/{LpFrame-BVQCQLDI.js → LpFrame-Bv5Fphte.js} +25 -11
- package/dist/chunks/{LpHintExample-9Py0wc-L.js → LpHintExample-YzCXiSUp.js} +1 -1
- package/dist/chunks/{LpHintLayer-CgObdUsM.js → LpHintLayer-BeKRsRon.js} +2 -2
- package/dist/chunks/{LpHints-Bz-BlaSl.js → LpHints-qf3xYsza.js} +3 -3
- package/dist/chunks/{LpHoverCard-spGxyw7T.js → LpHoverCard-DanH3Nz1.js} +23 -5
- package/dist/chunks/{LpIcon--5WSb3jJ.js → LpIcon-C6JpQ5td.js} +6 -3
- package/dist/chunks/{LpIdenticon-BzdC_qz9.js → LpIdenticon-Dbbr4fvt.js} +2 -1
- package/dist/chunks/{LpInfraNode-46XOCkfq.js → LpInfraNode-BAU40Km2.js} +2 -2
- package/dist/chunks/{LpInput-Tw-yxT4I.js → LpInput-DL6HEJdi.js} +16 -28
- package/dist/chunks/LpInspector-CuoMAhiw.js +209 -0
- package/dist/chunks/{LpLightbox-B95yu22B.js → LpLightbox-xvpQ64MM.js} +57 -32
- package/dist/chunks/{LpLoader-DVwkOmt9.js → LpLoader-Bf1yZwaU.js} +1 -1
- package/dist/chunks/LpLogInspector-6cNnXqp4.js +119 -0
- package/dist/chunks/{LpLogViewer-ngqkns4m.js → LpLogViewer-Dvb1E9_G.js} +234 -53
- package/dist/chunks/{LpMapPicker-DRGgh89B.js → LpMapPicker-D5_YLLBC.js} +4 -3
- package/dist/chunks/{LpMasonry--k0571qG.js → LpMasonry-DPYekFxs.js} +8 -5
- package/dist/chunks/{LpMediaGallery-Bkram-MO.js → LpMediaGallery-Df-tiWeq.js} +3 -3
- package/dist/chunks/{LpModal-CoGhLTyH.js → LpModal-DrtQ_1A2.js} +11 -13
- package/dist/chunks/{LpModalNav-BGvs2y5Q.js → LpModalNav-DkmGCnZE.js} +11 -37
- package/dist/chunks/{LpNotificationBell-B0034Lk-.js → LpNotificationBell-D_wklO1m.js} +9 -9
- package/dist/chunks/{LpNumberField-e0IBkWU1.js → LpNumberField-CNHsmSDZ.js} +7 -16
- package/dist/chunks/{LpOtpInput-CeBPsM7D.js → LpOtpInput-BRpLBbOD.js} +2 -2
- package/dist/chunks/{LpPagination-BIIiYHXR.js → LpPagination-snQ020xQ.js} +6 -4
- package/dist/chunks/{LpPaletteTheme-DiLYKQiZ.js → LpPaletteTheme-dVtxHx-F.js} +4 -4
- package/dist/chunks/{LpPasswordInput-BkHKQDhM.js → LpPasswordInput-B-zD6tUS.js} +2 -2
- package/dist/chunks/{LpPhoneInput-CETEp5W7.js → LpPhoneInput-BSL7NX4z.js} +15 -26
- package/dist/chunks/{LpPie-DrZepi0p.js → LpPie-9t_guO49.js} +22 -21
- package/dist/chunks/{LpPipeline-B3aOVOkV.js → LpPipeline-rjXvEDRJ.js} +16 -7
- package/dist/chunks/{LpPopover-DmZ3wUtP.js → LpPopover-BQSuxtai.js} +10 -1
- package/dist/chunks/{LpProgress-Yr6ONG5t.js → LpProgress-D_MSjowb.js} +2 -1
- package/dist/chunks/{LpQuota-Dk2MyI2Z.js → LpQuota-CgWPgdVt.js} +1 -1
- package/dist/chunks/{LpRadioGroup-ggaPL92T.js → LpRadioGroup-BrazjLY_.js} +4 -4
- package/dist/chunks/{LpRangeCalendar-CYYUYqJ7.js → LpRangeCalendar-BrqlGZzE.js} +1 -1
- package/dist/chunks/{LpRangeSlider-CM-0ZITZ.js → LpRangeSlider-fz61tzc-.js} +5 -9
- package/dist/chunks/{LpRelativeTime-DwgCb5-8.js → LpRelativeTime-BovUk8MP.js} +1 -1
- package/dist/chunks/{LpRepeater-D7ZmnIYd.js → LpRepeater-BkJHSPI1.js} +3 -3
- package/dist/chunks/{LpRevealOnScroll-DcQjexLy.js → LpRevealOnScroll-CPwyiQAr.js} +1 -1
- package/dist/chunks/{LpScramble-Bn9OoOtC.js → LpScramble-DsE77iVp.js} +1 -1
- package/dist/chunks/{LpScrollArea-MyEnVB_5.js → LpScrollArea-ay4Xo_hg.js} +22 -10
- package/dist/chunks/{LpSecretField-BbmqM9ye.js → LpSecretField-DBvU6iAw.js} +10 -9
- package/dist/chunks/{LpSection-DFK_uoJE.js → LpSection-BfteipxU.js} +1 -1
- package/dist/chunks/{LpSegmented-CeACsMMA.js → LpSegmented-DbEJhbv-.js} +8 -10
- package/dist/chunks/{LpSelect-Cxq4yBGd.js → LpSelect-Y-iuSymW.js} +47 -30
- package/dist/chunks/{LpShaderBackdrop-Bote0iuy.js → LpShaderBackdrop-wQygYb5h.js} +13 -10
- package/dist/chunks/{LpShift-CCq7dofl.js → LpShift-DAgS_d2x.js} +18 -15
- package/dist/chunks/{LpSidebar-DzE1783C.js → LpSidebar-kSkpPh3-.js} +74 -87
- package/dist/chunks/{LpSidebarNav-DE3PhXVP.js → LpSidebarNav-C2mvqrY2.js} +8 -6
- package/dist/chunks/{LpSidebarRow-BRYhNDSo.js → LpSidebarRow-eRP4Q31q.js} +6 -5
- package/dist/chunks/{LpSlider-DxYoCJJO.js → LpSlider-A4ixovgS.js} +5 -9
- package/dist/chunks/{LpSpotlight-4F3NeUMY.js → LpSpotlight-DwxkhFgR.js} +2 -1
- package/dist/chunks/{LpStat-f2LfDCJ4.js → LpStat-pMNZgFcq.js} +2 -2
- package/dist/chunks/{LpStatus-DglxttQ7.js → LpStatus-DGJFwk5F.js} +2 -2
- package/dist/chunks/{LpStepMark-DpzKVyh8.js → LpStepMark-Dw2MRTrt.js} +2 -2
- package/dist/chunks/{LpStepper-DpsocBNJ.js → LpStepper-DNnh7lCu.js} +1 -1
- package/dist/chunks/{LpSwap-C96c0LWh.js → LpSwap-M7nFeW3C.js} +1 -1
- package/dist/chunks/{LpSwitch-BnXd1llc.js → LpSwitch-DnC2-0aV.js} +3 -3
- package/dist/chunks/{LpTabFace-DPCwKkH2.js → LpTabFace-DQoV8mQG.js} +2 -2
- package/dist/chunks/{LpTableFilter-CiVBpaq0.js → LpTableFilter-DgARTsA1.js} +4 -4
- package/dist/chunks/{LpTableOfContents-BN5zvpnx.js → LpTableOfContents-CKkWEK6R.js} +14 -9
- package/dist/chunks/{LpTagsInput-C-mDG648.js → LpTagsInput-Dt0mteSj.js} +19 -27
- package/dist/chunks/{LpTextarea-TE-yZv2U.js → LpTextarea-Co0b9PVx.js} +8 -16
- package/dist/chunks/LpThemeProvider-XK6ZeWAE.js +81 -0
- package/dist/chunks/{LpThemeStudio-BpORDvH6.js → LpThemeStudio-DzYfE-5k.js} +12 -195
- package/dist/chunks/{LpThemeSwitcher-BnEn_mvf.js → LpThemeSwitcher-CFLkdbBL.js} +8 -6
- package/dist/chunks/{LpTimeField-BawSVsmt.js → LpTimeField-DeOcKWJq.js} +6 -9
- package/dist/chunks/{LpToaster-IwTXjbbU.js → LpToaster-QFj0P-5m.js} +10 -8
- package/dist/chunks/{LpTooltip-BcBvIx4z.js → LpTooltip-BJf3lZMq.js} +51 -19
- package/dist/chunks/LpTopologyCanvas-BOHHHZ_6.js +673 -0
- package/dist/chunks/{LpTour-BZq-mUMv.js → LpTour-ClKYhx4h.js} +7 -5
- package/dist/chunks/{LpTruncate-CflAIwiJ.js → LpTruncate-C6u3gjc6.js} +6 -5
- package/dist/chunks/{LpTypewriter-BCL_a6wE.js → LpTypewriter-CZIzpdbe.js} +2 -1
- package/dist/chunks/{LpUptimeBar-DqNvvMBK.js → LpUptimeBar-BnYmR2rj.js} +3 -3
- package/dist/chunks/{LpVideoBackdrop-DS1wGv-X.js → LpVideoBackdrop-Ct4St4ZL.js} +10 -13
- package/dist/chunks/LpWindow-B8YSRVTH.js +297 -0
- package/dist/chunks/LpWorkspaceSwitcher-D0e9upAy.js +139 -0
- package/dist/chunks/{MenuTitle-BmCNNTKH.js → MenuTitle-Bh5dAdV1.js} +1 -1
- package/dist/chunks/{OptionContent-22UT8zLn.js → OptionContent-CGb8QlHA.js} +1 -1
- package/dist/chunks/{PickerField-Bfh8id3d.js → PickerField-BM1dyafh.js} +4 -4
- package/dist/chunks/{RevealToggle-BEit1n6Z.js → RevealToggle-CCPDE8Mv.js} +3 -3
- package/dist/chunks/SliderRail-DhPREa_R.js +18 -0
- package/dist/chunks/{TourCard-DNclH3u_.js → TourCard-DAzTYfav.js} +4 -4
- package/dist/chunks/{icons-DMITlfw5.js → icons-CYfcpSDS.js} +26 -10
- package/dist/component-names.d.ts +1 -1
- package/dist/components/AppShellContextNav.vue.d.ts +13 -0
- package/dist/components/AppShellContextNav.vue.js +4 -0
- package/dist/components/ChartRangeSelector.vue.d.ts +21 -0
- package/dist/components/ChartRangeSelector.vue.js +4 -0
- package/dist/components/ContextMenuItems.vue.js +1 -1
- package/dist/components/DropzoneLinkField.vue.d.ts +2 -2
- package/dist/components/DropzoneLinkField.vue.js +1 -1
- package/dist/components/FieldWrapper.vue.js +1 -1
- package/dist/components/IconCloseButton.vue.d.ts +7 -0
- package/dist/components/IconCloseButton.vue.js +4 -0
- package/dist/components/LayoutCanvas.vue.js +1 -1
- package/dist/components/LayoutNode.vue.js +1 -1
- package/dist/components/LpActionBar.vue.js +1 -1
- package/dist/components/LpActivityLine.vue.js +1 -1
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.js +1 -1
- package/dist/components/LpAppShell.vue.d.ts +110 -2
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpAutocomplete.vue.js +1 -1
- package/dist/components/LpAvatar.vue.js +1 -1
- package/dist/components/LpBadge.vue.js +1 -1
- package/dist/components/LpBottomNav.vue.d.ts +5 -0
- package/dist/components/LpBottomNav.vue.js +1 -1
- package/dist/components/LpBreadcrumbs.vue.js +1 -1
- package/dist/components/LpButton.vue.d.ts +14 -2
- package/dist/components/LpButton.vue.js +1 -1
- package/dist/components/LpCalendar.vue.js +1 -1
- package/dist/components/LpCard.vue.js +1 -1
- package/dist/components/LpChart.vue.d.ts +74 -2
- package/dist/components/LpChart.vue.js +326 -85
- package/dist/components/LpCheckbox.vue.d.ts +6 -2
- package/dist/components/LpCheckbox.vue.js +1 -1
- package/dist/components/LpCodeBlock.vue.js +1 -1
- package/dist/components/LpCodeDisplay.vue.js +1 -1
- package/dist/components/LpCollapse.vue.js +1 -1
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.d.ts +8 -0
- package/dist/components/LpCommandPalette.vue.js +107 -56
- package/dist/components/LpCommandScope.vue.js +1 -1
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpConfirmHost.vue.js +1 -1
- package/dist/components/LpContextMenu.vue.js +1 -1
- package/dist/components/LpContextSwitcher.vue.js +1 -1
- package/dist/components/LpCopyButton.vue.js +1 -1
- package/dist/components/LpCountdown.vue.js +1 -1
- package/dist/components/LpDataList.vue.d.ts +1 -1
- package/dist/components/LpDataList.vue.js +1 -1
- package/dist/components/LpDatePicker.vue.d.ts +3 -15
- package/dist/components/LpDatePicker.vue.js +1 -1
- package/dist/components/LpDateRangePicker.vue.d.ts +3 -13
- package/dist/components/LpDateRangePicker.vue.js +1 -1
- package/dist/components/LpDisclosure.vue.js +1 -1
- package/dist/components/LpDock.vue.js +21 -18
- package/dist/components/LpDrawer.vue.d.ts +1 -1
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpDropdownMenu.vue.js +1 -1
- package/dist/components/LpDropzone.vue.js +14 -11
- package/dist/components/LpEditable.vue.js +1 -1
- package/dist/components/LpEmptyState.vue.js +1 -1
- package/dist/components/LpFieldNotice.vue.js +1 -1
- package/dist/components/LpFileTree.vue.d.ts +96 -6
- package/dist/components/LpFileTree.vue.js +1 -1
- package/dist/components/LpFileTreeNode.vue.d.ts +48 -0
- package/dist/components/LpFileTreeNode.vue.js +1 -1
- package/dist/components/LpFormField.vue.js +1 -1
- package/dist/components/LpFrame.vue.js +1 -1
- package/dist/components/LpGauge.vue.d.ts +1 -1
- package/dist/components/LpHintExample.vue.js +1 -1
- package/dist/components/LpHintLayer.vue.js +1 -1
- package/dist/components/LpHints.vue.js +1 -1
- package/dist/components/LpHoverCard.vue.js +1 -1
- package/dist/components/LpIcon.vue.d.ts +2 -0
- package/dist/components/LpIcon.vue.js +1 -1
- package/dist/components/LpIdenticon.vue.js +1 -1
- package/dist/components/LpImageEditor.vue.js +7 -6
- package/dist/components/LpInfraNode.vue.js +1 -1
- package/dist/components/LpInput.vue.d.ts +1 -1
- package/dist/components/LpInput.vue.js +1 -1
- package/dist/components/LpInspector.vue.d.ts +87 -0
- package/dist/components/LpInspector.vue.js +4 -0
- package/dist/components/LpLightbox.vue.d.ts +8 -0
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpLoader.vue.d.ts +1 -1
- package/dist/components/LpLoader.vue.js +1 -1
- package/dist/components/LpLogInspector.vue.d.ts +18 -0
- package/dist/components/LpLogInspector.vue.js +4 -0
- package/dist/components/LpLogViewer.vue.d.ts +93 -0
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpMapPicker.vue.js +1 -1
- package/dist/components/LpMasonry.vue.js +1 -1
- package/dist/components/LpMediaGallery.vue.js +1 -1
- package/dist/components/LpModal.vue.d.ts +4 -2
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/components/LpModalNav.vue.d.ts +5 -5
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.d.ts +2 -2
- package/dist/components/LpNotificationBell.vue.js +1 -1
- package/dist/components/LpNumberField.vue.js +1 -1
- package/dist/components/LpOtpInput.vue.js +1 -1
- package/dist/components/LpPagination.vue.js +1 -1
- package/dist/components/LpPaletteFacts.vue.js +1 -1
- package/dist/components/LpPaletteHeader.vue.js +2 -2
- package/dist/components/LpPaletteTheme.vue.js +1 -1
- package/dist/components/LpPasswordInput.vue.js +1 -1
- package/dist/components/LpPhoneInput.vue.js +1 -1
- package/dist/components/LpPie.vue.d.ts +2 -2
- package/dist/components/LpPie.vue.js +1 -1
- package/dist/components/LpPipeline.vue.d.ts +4 -0
- package/dist/components/LpPipeline.vue.js +1 -1
- package/dist/components/LpPopover.vue.d.ts +7 -3
- package/dist/components/LpPopover.vue.js +1 -1
- package/dist/components/LpProgress.vue.d.ts +1 -1
- package/dist/components/LpProgress.vue.js +1 -1
- package/dist/components/LpQuota.vue.js +1 -1
- package/dist/components/LpRadioGroup.vue.js +1 -1
- package/dist/components/LpRangeCalendar.vue.js +1 -1
- package/dist/components/LpRangeSlider.vue.js +1 -1
- package/dist/components/LpRelativeTime.vue.js +1 -1
- package/dist/components/LpRepeater.vue.js +1 -1
- package/dist/components/LpRevealOnScroll.vue.js +1 -1
- package/dist/components/LpScramble.vue.js +1 -1
- package/dist/components/LpScrollArea.vue.js +1 -1
- package/dist/components/LpSecretField.vue.js +1 -1
- package/dist/components/LpSection.vue.js +1 -1
- package/dist/components/LpSegmented.vue.js +1 -1
- package/dist/components/LpSelect.vue.js +1 -1
- package/dist/components/LpShaderBackdrop.vue.js +1 -1
- package/dist/components/LpShift.vue.js +1 -1
- package/dist/components/LpShortcutsDialog.vue.js +1 -1
- package/dist/components/LpSidebar.vue.d.ts +1 -1
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.vue.js +1 -1
- package/dist/components/LpSidebarRow.vue.js +1 -1
- package/dist/components/LpSlider.vue.js +1 -1
- package/dist/components/LpSparkline.vue.d.ts +1 -1
- package/dist/components/LpSpotlight.vue.js +1 -1
- package/dist/components/LpStat.vue.js +1 -1
- package/dist/components/LpStatus.vue.js +1 -1
- package/dist/components/LpStepMark.vue.js +1 -1
- package/dist/components/LpStepper.vue.js +1 -1
- package/dist/components/LpSwap.vue.js +1 -1
- package/dist/components/LpSwitch.vue.js +1 -1
- package/dist/components/LpTabFace.vue.js +1 -1
- package/dist/components/LpTable.vue.d.ts +132 -0
- package/dist/components/LpTable.vue.js +547 -135
- package/dist/components/LpTableFilter.vue.js +1 -1
- package/dist/components/LpTableOfContents.vue.js +1 -1
- package/dist/components/LpTabs.vue.js +6 -5
- package/dist/components/LpTagsInput.vue.js +1 -1
- package/dist/components/LpTextarea.vue.js +1 -1
- package/dist/components/LpThemeProvider.vue.d.ts +2 -0
- package/dist/components/LpThemeProvider.vue.js +1 -1
- package/dist/components/LpThemeStudio.vue.js +1 -1
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpTimeField.vue.js +1 -1
- package/dist/components/LpToaster.vue.js +1 -1
- package/dist/components/LpTooltip.vue.js +1 -1
- package/dist/components/LpTopologyCanvas.vue.d.ts +131 -5
- package/dist/components/LpTopologyCanvas.vue.js +1 -1
- package/dist/components/LpTour.vue.js +1 -1
- package/dist/components/LpTruncate.vue.js +1 -1
- package/dist/components/LpTypewriter.vue.js +1 -1
- package/dist/components/LpUptimeBar.vue.js +1 -1
- package/dist/components/LpVideoBackdrop.vue.js +1 -1
- package/dist/components/LpWindow.vue.d.ts +99 -0
- package/dist/components/LpWindow.vue.js +4 -0
- package/dist/components/LpWorkspaceSwitcher.vue.d.ts +18 -0
- package/dist/components/LpWorkspaceSwitcher.vue.js +4 -0
- package/dist/components/MenuTitle.vue.js +1 -1
- package/dist/components/OptionContent.vue.js +1 -1
- package/dist/components/PickerField.vue.d.ts +2 -2
- package/dist/components/PickerField.vue.js +1 -1
- package/dist/components/RevealToggle.vue.js +1 -1
- package/dist/components/SliderRail.vue.d.ts +3 -0
- package/dist/components/SliderRail.vue.js +4 -0
- package/dist/components/TourCard.vue.js +1 -1
- package/dist/components/address.js +2 -1
- package/dist/components/appShell.d.ts +36 -0
- package/dist/components/appShell.js +30 -0
- package/dist/components/calendar.d.ts +12 -3
- package/dist/components/calendar.js +2 -5
- package/dist/components/chart.d.ts +21 -0
- package/dist/components/chart.js +4 -3
- package/dist/components/chartWindow.d.ts +57 -0
- package/dist/components/chartWindow.js +136 -0
- package/dist/components/color.js +1 -1
- package/dist/components/commandPalette.d.ts +16 -2
- package/dist/components/commandPalette.js +39 -4
- package/dist/components/controlSize.d.ts +5 -0
- package/dist/components/controlSize.js +8 -0
- package/dist/components/dropdown.d.ts +5 -0
- package/dist/components/dropdown.js +2 -0
- package/dist/components/exportData.d.ts +6 -1
- package/dist/components/exportData.js +25 -18
- package/dist/components/fileTree.d.ts +67 -0
- package/dist/components/fileTree.js +141 -1
- package/dist/components/identicon.js +3 -2
- package/dist/components/logTools.d.ts +93 -0
- package/dist/components/logTools.js +210 -0
- package/dist/components/menuWhen.js +1 -1
- package/dist/components/meter.js +2 -1
- package/dist/components/pipeline.d.ts +2 -0
- package/dist/components/pipeline.js +5 -0
- package/dist/components/relativeTime.js +2 -1
- package/dist/components/reveal.js +2 -1
- package/dist/components/select.d.ts +5 -0
- package/dist/components/select.js +10 -0
- package/dist/components/sidebar.d.ts +1 -0
- package/dist/components/tableFeatures.d.ts +58 -0
- package/dist/components/tableFeatures.js +87 -0
- package/dist/components/topologyGraph.d.ts +164 -0
- package/dist/components/topologyGraph.js +416 -0
- package/dist/components/tour.js +5 -3
- package/dist/components/videoBackdrop.js +7 -6
- package/dist/composables/clamp.d.ts +2 -0
- package/dist/composables/clamp.js +4 -0
- package/dist/composables/devWarn.d.ts +3 -0
- package/dist/composables/devWarn.js +14 -0
- package/dist/composables/easing.d.ts +13 -4
- package/dist/composables/easing.js +16 -10
- package/dist/composables/overlayHistory.js +14 -1
- package/dist/composables/pointerDrag.d.ts +15 -0
- package/dist/composables/pointerDrag.js +40 -0
- package/dist/composables/propCheck.d.ts +10 -0
- package/dist/composables/propCheck.js +78 -0
- package/dist/composables/realm.d.ts +6 -0
- package/dist/composables/realm.js +23 -0
- package/dist/composables/scroll.js +4 -2
- package/dist/composables/scrollMemory.d.ts +12 -1
- package/dist/composables/scrollMemory.js +36 -12
- package/dist/composables/spatialNav.js +2 -1
- package/dist/composables/textEntry.d.ts +17 -0
- package/dist/composables/textEntry.js +19 -0
- package/dist/composables/useAsyncTask.d.ts +6 -0
- package/dist/composables/useChartSync.d.ts +5 -0
- package/dist/composables/useChartSync.js +33 -0
- package/dist/composables/useCloseGuard.d.ts +3 -1
- package/dist/composables/useCloseGuard.js +6 -3
- package/dist/composables/useCommands.d.ts +50 -0
- package/dist/composables/useCommands.js +66 -0
- package/dist/composables/useField.d.ts +37 -0
- package/dist/composables/useField.js +27 -2
- package/dist/composables/useFitScale.js +4 -1
- package/dist/composables/useFormDraft.d.ts +13 -2
- package/dist/composables/useFormDraft.js +17 -9
- package/dist/composables/useHintSystem.js +2 -2
- package/dist/composables/useHotkeys.js +11 -32
- package/dist/composables/useInView.js +2 -1
- package/dist/composables/useInputFilter.js +7 -5
- package/dist/composables/useInputMask.js +5 -3
- package/dist/composables/useLinkedView.d.ts +97 -0
- package/dist/composables/useLinkedView.js +139 -0
- package/dist/composables/useOptimisticToggle.d.ts +3 -1
- package/dist/composables/useOptimisticToggle.js +2 -1
- package/dist/composables/useOverlayHistory.d.ts +7 -0
- package/dist/composables/useOverlayHistory.js +16 -2
- package/dist/composables/usePaintSelect.js +18 -14
- package/dist/composables/usePaletteCommands.d.ts +7 -0
- package/dist/composables/usePaletteCommands.js +12 -1
- package/dist/composables/usePersisted.d.ts +10 -0
- package/dist/composables/usePersisted.js +1 -1
- package/dist/composables/usePillTransition.d.ts +2 -2
- package/dist/composables/usePillTransition.js +5 -5
- package/dist/composables/useResizeObserver.js +2 -1
- package/dist/composables/useReveal.js +2 -1
- package/dist/composables/useRovingFocus.d.ts +2 -2
- package/dist/composables/useRovingFocus.js +9 -11
- package/dist/composables/useScramble.js +7 -1
- package/dist/composables/useShift.js +8 -6
- package/dist/composables/useTilt.js +2 -1
- package/dist/composables/useTween.js +9 -5
- package/dist/composables/useView.d.ts +1 -0
- package/dist/composables/useView.js +14 -2
- package/dist/composables/useZoomPan.js +2 -4
- package/dist/composables/windowing.d.ts +40 -0
- package/dist/composables/windowing.js +118 -0
- package/dist/icons-all.js +1 -1
- package/dist/icons.d.ts +4 -0
- package/dist/index.d.ts +30 -5
- package/dist/index.js +310 -264
- package/dist/layout/tree.js +19 -15
- package/dist/layout/useLayout.d.ts +2 -0
- package/dist/layout/useLayout.js +5 -1
- package/dist/layout/useWorkspaces.d.ts +52 -0
- package/dist/layout/useWorkspaces.js +140 -0
- package/dist/{chunks/backdrop-Dkf2vM81.js → theme/backdrop.js} +5 -5
- package/dist/theme/config.js +25 -0
- package/dist/theme/define.js +61 -0
- package/dist/{chunks/palette-Co8tSASo.js → theme/palette.js} +2 -2
- package/dist/theme/presets.js +239 -0
- package/dist/theme/studio.js +134 -0
- package/dist/theme/tokens.js +63 -0
- package/dist/theme/useTheme.d.ts +3 -2
- package/dist/theme/useTheme.js +325 -0
- package/package.json +8 -1
- package/src/component-names.ts +5 -1
- package/src/components/AppShellContextNav.vue +44 -0
- package/src/components/ChartRangeSelector.vue +120 -0
- package/src/components/IconCloseButton.vue +21 -0
- package/src/components/LayoutCanvas.vue +3 -0
- package/src/components/LayoutNode.vue +37 -38
- package/src/components/LpActivityLine.vue +4 -2
- package/src/components/LpAddressInput.vue +3 -3
- package/src/components/LpAppShell.vue +362 -24
- package/src/components/LpAutocomplete.vue +18 -30
- package/src/components/LpBottomNav.vue +9 -1
- package/src/components/LpBreadcrumbs.vue +2 -1
- package/src/components/LpButton.vue +11 -4
- package/src/components/LpChart.vue +336 -37
- package/src/components/LpCheckbox.vue +43 -9
- package/src/components/LpCodeBlock.vue +17 -23
- package/src/components/LpCollapse.vue +5 -4
- package/src/components/LpColorPicker.vue +8 -9
- package/src/components/LpCommandPalette.vue +133 -50
- package/src/components/LpContextMenu.vue +15 -13
- package/src/components/LpCountdown.vue +2 -1
- package/src/components/LpDataList.vue +13 -1
- package/src/components/LpDatePicker.vue +5 -17
- package/src/components/LpDateRangePicker.vue +5 -16
- package/src/components/LpDock.vue +25 -16
- package/src/components/LpDrawer.vue +70 -61
- package/src/components/LpDropdownMenu.vue +6 -10
- package/src/components/LpDropzone.vue +12 -7
- package/src/components/LpEditable.vue +6 -2
- package/src/components/LpFileTree.vue +507 -66
- package/src/components/LpFileTreeNode.vue +106 -5
- package/src/components/LpFormField.vue +1 -1
- package/src/components/LpFrame.vue +33 -13
- package/src/components/LpHoverCard.vue +30 -5
- package/src/components/LpIcon.vue +6 -2
- package/src/components/LpIdenticon.vue +2 -1
- package/src/components/LpImageEditor.vue +5 -2
- package/src/components/LpInput.vue +15 -23
- package/src/components/LpInspector.vue +197 -0
- package/src/components/LpLightbox.vue +74 -29
- package/src/components/LpLogInspector.vue +85 -0
- package/src/components/LpLogViewer.vue +239 -29
- package/src/components/LpMapPicker.vue +2 -1
- package/src/components/LpMasonry.vue +8 -5
- package/src/components/LpModal.vue +12 -12
- package/src/components/LpModalNav.vue +2 -21
- package/src/components/LpNumberField.vue +6 -15
- package/src/components/LpOtpInput.vue +2 -2
- package/src/components/LpPagination.vue +5 -3
- package/src/components/LpPhoneInput.vue +8 -19
- package/src/components/LpPie.vue +25 -23
- package/src/components/LpPipeline.vue +14 -1
- package/src/components/LpPopover.vue +23 -2
- package/src/components/LpProgress.vue +2 -1
- package/src/components/LpRadioGroup.vue +2 -2
- package/src/components/LpRangeSlider.vue +5 -6
- package/src/components/LpRevealOnScroll.vue +1 -1
- package/src/components/LpScrollArea.vue +28 -11
- package/src/components/LpSecretField.vue +2 -1
- package/src/components/LpSegmented.vue +6 -8
- package/src/components/LpSelect.vue +54 -24
- package/src/components/LpShaderBackdrop.vue +12 -9
- package/src/components/LpShift.vue +18 -15
- package/src/components/LpSidebar.vue +77 -74
- package/src/components/LpSidebarNav.vue +3 -2
- package/src/components/LpSidebarRow.vue +2 -1
- package/src/components/LpSlider.vue +5 -6
- package/src/components/LpSpotlight.vue +2 -1
- package/src/components/LpSwitch.vue +5 -3
- package/src/components/LpTable.vue +609 -93
- package/src/components/LpTableFilter.vue +1 -1
- package/src/components/LpTableOfContents.vue +16 -9
- package/src/components/LpTabs.vue +2 -1
- package/src/components/LpTagsInput.vue +12 -19
- package/src/components/LpTextarea.vue +5 -11
- package/src/components/LpThemeProvider.vue +29 -13
- package/src/components/LpTimeField.vue +4 -7
- package/src/components/LpToaster.vue +7 -6
- package/src/components/LpTooltip.vue +62 -18
- package/src/components/LpTopologyCanvas.vue +567 -42
- package/src/components/LpTour.vue +7 -5
- package/src/components/LpTruncate.vue +4 -3
- package/src/components/LpTypewriter.vue +2 -1
- package/src/components/LpVideoBackdrop.vue +11 -13
- package/src/components/LpWindow.vue +373 -0
- package/src/components/LpWorkspaceSwitcher.vue +116 -0
- package/src/components/PickerField.vue +2 -2
- package/src/components/SliderRail.vue +10 -0
- package/src/components/address.ts +3 -1
- package/src/components/appShell.ts +74 -0
- package/src/components/calendar.ts +12 -5
- package/src/components/chart.ts +27 -3
- package/src/components/chartWindow.ts +198 -0
- package/src/components/color.ts +2 -1
- package/src/components/commandPalette.ts +66 -5
- package/src/components/controlSize.ts +6 -0
- package/src/components/dropdown.ts +3 -0
- package/src/components/exportData.ts +33 -18
- package/src/components/fileTree.ts +215 -0
- package/src/components/identicon.ts +4 -2
- package/src/components/logTools.ts +298 -0
- package/src/components/meter.ts +2 -1
- package/src/components/pipeline.ts +6 -0
- package/src/components/relativeTime.ts +2 -1
- package/src/components/reveal.ts +3 -1
- package/src/components/select.ts +14 -0
- package/src/components/sidebar.ts +1 -0
- package/src/components/tableFeatures.ts +149 -0
- package/src/components/topologyGraph.ts +655 -0
- package/src/components/tour.ts +5 -3
- package/src/components/videoBackdrop.ts +7 -6
- package/src/composables/clamp.ts +2 -0
- package/src/composables/devWarn.ts +19 -0
- package/src/composables/easing.ts +37 -12
- package/src/composables/overlayHistory.ts +24 -0
- package/src/composables/pointerDrag.ts +54 -0
- package/src/composables/propCheck.ts +90 -0
- package/src/composables/realm.ts +25 -0
- package/src/composables/scroll.ts +4 -2
- package/src/composables/scrollMemory.ts +56 -14
- package/src/composables/spatialNav.ts +2 -1
- package/src/composables/textEntry.ts +33 -0
- package/src/composables/useAsyncTask.ts +6 -0
- package/src/composables/useChartSync.ts +52 -0
- package/src/composables/useCloseGuard.ts +22 -10
- package/src/composables/useCommands.ts +123 -0
- package/src/composables/useField.ts +41 -2
- package/src/composables/useFitScale.ts +4 -1
- package/src/composables/useFormDraft.ts +38 -14
- package/src/composables/useHintSystem.ts +2 -2
- package/src/composables/useHotkeys.ts +19 -48
- package/src/composables/useInView.ts +2 -1
- package/src/composables/useInputFilter.ts +8 -5
- package/src/composables/useInputMask.ts +8 -3
- package/src/composables/useLinkedView.ts +222 -0
- package/src/composables/useOptimisticToggle.ts +7 -1
- package/src/composables/useOverlayHistory.ts +20 -0
- package/src/composables/usePaintSelect.ts +20 -14
- package/src/composables/usePaletteCommands.ts +13 -0
- package/src/composables/usePersisted.ts +10 -0
- package/src/composables/usePillTransition.ts +9 -6
- package/src/composables/useResizeObserver.ts +2 -1
- package/src/composables/useReveal.ts +2 -1
- package/src/composables/useRovingFocus.ts +11 -14
- package/src/composables/useScramble.ts +12 -1
- package/src/composables/useShift.ts +8 -6
- package/src/composables/useTilt.ts +2 -1
- package/src/composables/useTween.ts +10 -5
- package/src/composables/useView.ts +17 -3
- package/src/composables/useZoomPan.ts +2 -5
- package/src/composables/windowing.ts +190 -0
- package/src/icons.ts +27 -6
- package/src/index.ts +41 -4
- package/src/kit-icons.ts +1 -1
- package/src/layout/tree.ts +31 -16
- package/src/layout/useLayout.ts +6 -0
- package/src/layout/useWorkspaces.ts +234 -0
- package/src/nuxt.ts +9 -1
- package/src/runtime/propCheck.plugin.ts +10 -0
- package/src/theme/studio.ts +7 -5
- package/src/theme/useTheme.ts +89 -15
- package/src/tokens/tokens.css +8 -1
- package/dist/assets/LpTabs-CJ0rYQbx.css +0 -18
- package/dist/chunks/LpAppShell-Dgejipzz.js +0 -319
- package/dist/chunks/LpFileTree-CooHinsC.js +0 -425
- package/dist/chunks/LpThemeProvider-8MG6yqU9.js +0 -640
- package/dist/chunks/LpTopologyCanvas-B1gIbS0n.js +0 -298
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
// appended to it, so opt out of the automatic pass-through that would add it
|
|
4
4
|
// a second time, unmerged.
|
|
5
5
|
export default { inheritAttrs: false }
|
|
6
|
+
|
|
7
|
+
/* One text-sm line with the row's own vertical padding. */
|
|
8
|
+
const DEFAULT_ROW_HEIGHT = 28
|
|
9
|
+
/* Rows rendered past each end of the view, so a fast scroll shows no gap. */
|
|
10
|
+
const OVERSCAN_ROWS = 8
|
|
6
11
|
</script>
|
|
7
12
|
|
|
8
13
|
<script setup lang="ts">
|
|
@@ -14,7 +19,8 @@ export default { inheritAttrs: false }
|
|
|
14
19
|
* context menus.
|
|
15
20
|
*
|
|
16
21
|
* Read-only by design — it navigates, it doesn't mutate. Renames/uploads belong
|
|
17
|
-
* to the caller, reachable through `node.menu
|
|
22
|
+
* to the caller, reachable through `node.menu`, and `renamable` and `movable`
|
|
23
|
+
* only ask: the tree emits `rename` and `move`, the app changes `nodes`.
|
|
18
24
|
*
|
|
19
25
|
* Lazy loading: a directory whose `children` is undefined is treated as
|
|
20
26
|
* not-yet-loaded — it stays expandable, shows a spinner while the caller fetches
|
|
@@ -23,27 +29,57 @@ export default { inheritAttrs: false }
|
|
|
23
29
|
* Keyboard (role="tree" conventions): ↑/↓ move through visible rows, → opens a
|
|
24
30
|
* directory or steps into it, ← collapses or steps out to the parent, Home/End
|
|
25
31
|
* jump to the ends, Enter/Space activates. A roving tabindex keeps the tree a
|
|
26
|
-
* single tab stop.
|
|
32
|
+
* single tab stop. `renamable` adds F2; `multiple` adds Shift+↑/↓ and Ctrl+A.
|
|
33
|
+
*
|
|
34
|
+
* Optional, and off unless asked for:
|
|
35
|
+
* - `searchable`, `filter`: only the matches and the folders leading to them.
|
|
36
|
+
* Only loaded entries can be found. A ticked folder ticks what is shown.
|
|
37
|
+
* - `breadcrumb`: the path of the selected (or focused) row, each step a way back.
|
|
38
|
+
* - `load`: the tree fetches a folder itself, aborting if it is closed meanwhile.
|
|
39
|
+
* - `multiple`: Ctrl/Cmd and Shift pick rows into `selection`, by id, so a pick
|
|
40
|
+
* outlives its folder being closed or filtered away.
|
|
41
|
+
* - `movable`: drag rows onto a folder (mouse only, HTML drag and drop).
|
|
42
|
+
* - `virtual`: renders only the rows in view, for directories of thousands. The
|
|
43
|
+
* DOM is flat, so rows state aria-level, aria-setsize and aria-posinset instead
|
|
44
|
+
* of nesting in role="group"; aria-setsize stays true for rows not rendered.
|
|
45
|
+
* The focused row always stays mounted, so the tab stop never goes. Rows need
|
|
46
|
+
* one height (`rowHeight`), and folders open at once, with no fold.
|
|
27
47
|
*/
|
|
28
|
-
import {
|
|
48
|
+
import { clamp } from "../composables/clamp"
|
|
49
|
+
import { computed, h, nextTick, onBeforeUnmount, ref, shallowRef, watch, type FunctionalComponent } from "vue"
|
|
29
50
|
import { useLocale } from "../composables/useLocale"
|
|
51
|
+
import { isElement } from "../composables/useView"
|
|
30
52
|
import {
|
|
31
53
|
ancestorIds,
|
|
54
|
+
canDrop,
|
|
32
55
|
computeStats,
|
|
56
|
+
filterTree,
|
|
57
|
+
flattenVisible,
|
|
33
58
|
formatSize,
|
|
59
|
+
isFilterEmpty,
|
|
60
|
+
nodeTrail,
|
|
61
|
+
rangeIds,
|
|
62
|
+
renameProblem,
|
|
63
|
+
rowWindow,
|
|
34
64
|
sortNodes,
|
|
35
65
|
subtreeIds,
|
|
66
|
+
topmostIds,
|
|
67
|
+
withLoadedChildren,
|
|
36
68
|
type FileNode,
|
|
69
|
+
type FileTreeFilter,
|
|
37
70
|
} from "./fileTree"
|
|
71
|
+
import LpBreadcrumbs from "./LpBreadcrumbs.vue"
|
|
38
72
|
import LpEmptyState from "./LpEmptyState.vue"
|
|
39
73
|
import LpFileTreeNode from "./LpFileTreeNode.vue"
|
|
74
|
+
import LpInput from "./LpInput.vue"
|
|
40
75
|
import LpScrollArea from "./LpScrollArea.vue"
|
|
41
76
|
import LpSkeleton from "./LpSkeleton.vue"
|
|
42
77
|
import { usePersisted } from "../composables/usePersisted"
|
|
43
78
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
44
79
|
import { useLabels } from "../composables/useLpConfig"
|
|
80
|
+
import { useResizeObserver } from "../composables/useResizeObserver"
|
|
45
81
|
|
|
46
|
-
export type { FileNode } from "./fileTree"
|
|
82
|
+
export type { FileNode, FileTreeFilter } from "./fileTree"
|
|
47
83
|
|
|
48
84
|
/** Rolled-up stats for the ticked entries — what a backup picker summarises. */
|
|
49
85
|
export interface FileTreeLabels {
|
|
@@ -54,6 +90,16 @@ export interface FileTreeLabels {
|
|
|
54
90
|
files: string
|
|
55
91
|
folder: string
|
|
56
92
|
folders: string
|
|
93
|
+
search: string
|
|
94
|
+
noMatches: string
|
|
95
|
+
rename: string
|
|
96
|
+
path: string
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** `move`: what was dropped (a moved folder carries its content) and where. */
|
|
100
|
+
export interface FileTreeMove {
|
|
101
|
+
nodes: FileNode[]
|
|
102
|
+
target: FileNode
|
|
57
103
|
}
|
|
58
104
|
|
|
59
105
|
export interface FileTreeSummary {
|
|
@@ -72,7 +118,10 @@ const props = withDefaults(
|
|
|
72
118
|
nodes: FileNode[]
|
|
73
119
|
/** Selected node id (v-model:selected). */
|
|
74
120
|
selected?: string
|
|
75
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* Expanded directory ids (v-model:expanded). Omit to let the tree own it.
|
|
123
|
+
* Unbinding it later keeps the last set shown; it does not go back to `persist`.
|
|
124
|
+
*/
|
|
76
125
|
expanded?: string[]
|
|
77
126
|
/**
|
|
78
127
|
* Remember which folders are open across visits under this key (the
|
|
@@ -87,7 +136,11 @@ const props = withDefaults(
|
|
|
87
136
|
* to back up" — `selected` stays independent (it's the highlighted row).
|
|
88
137
|
*/
|
|
89
138
|
checkable?: boolean
|
|
90
|
-
/**
|
|
139
|
+
/**
|
|
140
|
+
* Ticked node ids (v-model:checked). An id no longer in `nodes` stays ticked:
|
|
141
|
+
* with lazy children it may only be unloaded, so dropping a deleted one is
|
|
142
|
+
* the app's call.
|
|
143
|
+
*/
|
|
91
144
|
checked?: string[]
|
|
92
145
|
/** Right-aligned size column (directories roll up their subtree). */
|
|
93
146
|
showSize?: boolean
|
|
@@ -104,8 +157,36 @@ const props = withDefaults(
|
|
|
104
157
|
emptyLabel?: string
|
|
105
158
|
emptyDescription?: string
|
|
106
159
|
labels?: Partial<FileTreeLabels>
|
|
160
|
+
/** Narrow the tree by name, path, type, size or date. Combined with `searchable`. */
|
|
161
|
+
filter?: FileTreeFilter
|
|
162
|
+
/** A search field above the tree. */
|
|
163
|
+
searchable?: boolean
|
|
164
|
+
/** The search text (v-model:query). Omit to let the tree own it. */
|
|
165
|
+
query?: string
|
|
166
|
+
/** The path of the selected row above the tree, each step a way back to it. */
|
|
167
|
+
breadcrumb?: boolean
|
|
168
|
+
/**
|
|
169
|
+
* Fetch a folder when it first opens; the result is kept by the tree and
|
|
170
|
+
* `children` in `nodes` win over it. `signal` aborts when the folder is
|
|
171
|
+
* closed first. Rejecting emits `load-error` and the next open tries again.
|
|
172
|
+
* `expand` still fires. Without this, `expand` is yours to answer.
|
|
173
|
+
*/
|
|
174
|
+
load?: (node: FileNode, signal: AbortSignal) => Promise<FileNode[]>
|
|
175
|
+
/** Ctrl/Cmd and Shift pick several rows into `selection`. */
|
|
176
|
+
multiple?: boolean
|
|
177
|
+
/** Picked ids (v-model:selection), kept while their folders close or filter away. */
|
|
178
|
+
selection?: string[]
|
|
179
|
+
/** Drag rows onto a folder. Emits `move`; the app changes `nodes`. Mouse only. */
|
|
180
|
+
movable?: boolean
|
|
181
|
+
/** F2 renames the focused row in place. Emits `rename`; the app changes `nodes`. */
|
|
182
|
+
renamable?: boolean
|
|
183
|
+
/** Render only the rows in view. See the note above on what it changes. */
|
|
184
|
+
virtual?: boolean
|
|
185
|
+
/** Height of every row in `virtual` mode, in px. */
|
|
186
|
+
rowHeight?: number
|
|
107
187
|
}>(),
|
|
108
188
|
{
|
|
189
|
+
rowHeight: DEFAULT_ROW_HEIGHT,
|
|
109
190
|
sort: true,
|
|
110
191
|
iconSize: 15,
|
|
111
192
|
checkable: false,
|
|
@@ -129,6 +210,13 @@ const emit = defineEmits<{
|
|
|
129
210
|
(e: "update:checked", ids: string[]): void
|
|
130
211
|
/** Ticked set changed, with the rolled-up stats already computed. */
|
|
131
212
|
(e: "summary", summary: FileTreeSummary): void
|
|
213
|
+
(e: "update:query", value: string): void
|
|
214
|
+
(e: "update:selection", ids: string[]): void
|
|
215
|
+
/** Rows dropped on a folder. Nothing moves until the app changes `nodes`. */
|
|
216
|
+
(e: "move", move: FileTreeMove): void
|
|
217
|
+
/** A row given a new, valid name. Nothing changes until the app changes `nodes`. */
|
|
218
|
+
(e: "rename", node: FileNode, name: string): void
|
|
219
|
+
(e: "load-error", node: FileNode, error: unknown): void
|
|
132
220
|
}>()
|
|
133
221
|
|
|
134
222
|
defineSlots<{
|
|
@@ -166,11 +254,20 @@ const text = useLabels<FileTreeLabels>(
|
|
|
166
254
|
files: "files",
|
|
167
255
|
folder: "folder",
|
|
168
256
|
folders: "folders",
|
|
257
|
+
search: "Search files",
|
|
258
|
+
noMatches: "No matching files",
|
|
259
|
+
rename: "Rename",
|
|
260
|
+
path: "Path",
|
|
169
261
|
},
|
|
170
262
|
() => props.labels,
|
|
171
263
|
)
|
|
172
264
|
|
|
173
|
-
|
|
265
|
+
// What `load` brought back, laid over `nodes`. Everything below reads `source`.
|
|
266
|
+
const loadedChildren = shallowRef<ReadonlyMap<string, FileNode[]>>(new Map())
|
|
267
|
+
const source = computed(() => withLoadedChildren(props.nodes, loadedChildren.value))
|
|
268
|
+
|
|
269
|
+
const ownLoading = ref<Set<string>>(new Set())
|
|
270
|
+
const loadingSet = computed(() => new Set([...(props.loadingIds ?? []), ...ownLoading.value]))
|
|
174
271
|
|
|
175
272
|
// Same controlled/uncontrolled deal as `expanded`.
|
|
176
273
|
const checkedIds = ref<Set<string>>(new Set(props.checked ?? []))
|
|
@@ -196,7 +293,7 @@ const index = computed(() => {
|
|
|
196
293
|
if (node.children) walk(node.children, node.id)
|
|
197
294
|
}
|
|
198
295
|
}
|
|
199
|
-
walk(
|
|
296
|
+
walk(source.value)
|
|
200
297
|
return { byId, parentOfId }
|
|
201
298
|
})
|
|
202
299
|
|
|
@@ -206,7 +303,7 @@ const byId = computed(() => index.value.byId)
|
|
|
206
303
|
// walking its own subtree on each render. Declared before `summary`, which
|
|
207
304
|
// reads it and is watched eagerly — a `const` below that watcher is still in
|
|
208
305
|
// its temporal dead zone when the watcher first runs.
|
|
209
|
-
const stats = computed(() => computeStats(
|
|
306
|
+
const stats = computed(() => computeStats(source.value, checkedIds.value))
|
|
210
307
|
|
|
211
308
|
// Read by `summary` below, so declared ahead of it for the same reason.
|
|
212
309
|
const locale = useLocale()
|
|
@@ -262,7 +359,7 @@ const summary = computed<FileTreeSummary>(() => {
|
|
|
262
359
|
}
|
|
263
360
|
}
|
|
264
361
|
|
|
265
|
-
walk(
|
|
362
|
+
walk(source.value)
|
|
266
363
|
return {
|
|
267
364
|
files,
|
|
268
365
|
dirs,
|
|
@@ -322,25 +419,41 @@ function clearChecked() {
|
|
|
322
419
|
// doesn't refetch. Callers that want a refresh can drop `children` again.
|
|
323
420
|
const requested = new Set<string>()
|
|
324
421
|
|
|
325
|
-
|
|
422
|
+
// The search text: the bound `query`, else the tree's own.
|
|
423
|
+
const ownQuery = ref("")
|
|
424
|
+
const queryText = computed(() => props.query ?? ownQuery.value)
|
|
425
|
+
function setQuery(value: string) {
|
|
426
|
+
ownQuery.value = value
|
|
427
|
+
emit("update:query", value)
|
|
428
|
+
}
|
|
326
429
|
|
|
327
|
-
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
walk(props.nodes)
|
|
339
|
-
return out
|
|
430
|
+
const activeFilter = computed<FileTreeFilter>(() => ({ ...props.filter, query: queryText.value || props.filter?.query }))
|
|
431
|
+
const filtering = computed(() => !isFilterEmpty(activeFilter.value))
|
|
432
|
+
const filtered = computed(() => filterTree(source.value, activeFilter.value))
|
|
433
|
+
|
|
434
|
+
// A filter opens the folders holding a match without touching the user's own
|
|
435
|
+
// expansion. Closing one of those lasts until the filter changes.
|
|
436
|
+
const closedWhileFiltering = ref<Set<string>>(new Set())
|
|
437
|
+
watch([() => props.filter, queryText], () => {
|
|
438
|
+
closedWhileFiltering.value = new Set()
|
|
340
439
|
})
|
|
440
|
+
const shownOpen = computed(() => {
|
|
441
|
+
if (!filtering.value) return openIds.value
|
|
442
|
+
const open = new Set([...openIds.value, ...filtered.value.openIds])
|
|
443
|
+
for (const id of closedWhileFiltering.value) open.delete(id)
|
|
444
|
+
return open
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
const roots = computed(() => (props.sort ? sortNodes(filtered.value.nodes) : filtered.value.nodes))
|
|
448
|
+
|
|
449
|
+
const rows = computed(() => flattenVisible(filtered.value.nodes, shownOpen.value, props.sort))
|
|
450
|
+
|
|
451
|
+
/** Rows currently visible, in render order — the axis the arrow keys walk. */
|
|
452
|
+
const visible = computed(() => rows.value.map((row) => row.node))
|
|
341
453
|
|
|
342
454
|
// The roving-tabindex row. Follows selection when the caller sets one; falls
|
|
343
455
|
// back to the first row so the tree is reachable by Tab before any click.
|
|
456
|
+
const root = ref<HTMLElement | null>(null)
|
|
344
457
|
const focusedId = ref<string | undefined>(props.selected)
|
|
345
458
|
watch(
|
|
346
459
|
() => props.selected,
|
|
@@ -355,17 +468,19 @@ watch(
|
|
|
355
468
|
focusedId.value = undefined
|
|
356
469
|
} else if (!rows.some((n) => n.id === focusedId.value)) {
|
|
357
470
|
// The focused row scrolled out of existence (parent collapsed, data
|
|
358
|
-
// changed) — fall back to the first visible row.
|
|
471
|
+
// changed) — fall back to the first visible row. Runs before the DOM
|
|
472
|
+
// update, so it can still see whether that row held the keyboard, which
|
|
473
|
+
// would otherwise drop to the page with the row.
|
|
474
|
+
const active = root.value?.ownerDocument.activeElement
|
|
475
|
+
const hadFocus = isElement(active) && active.getAttribute("data-node-id") === focusedId.value
|
|
359
476
|
focusedId.value = rows[0].id
|
|
477
|
+
if (hadFocus) void nextTick(() => focusRow(rows[0].id))
|
|
360
478
|
}
|
|
361
479
|
},
|
|
362
480
|
{ immediate: true },
|
|
363
481
|
)
|
|
364
482
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
function focusRow(id: string) {
|
|
368
|
-
focusedId.value = id
|
|
483
|
+
function focusRowElement(id: string) {
|
|
369
484
|
// Rows are plain divs keyed by data-node-id; query rather than hold refs
|
|
370
485
|
// through the recursion.
|
|
371
486
|
const el = root.value?.querySelector<HTMLElement>(`[data-node-id="${CSS.escape(id)}"]`)
|
|
@@ -373,12 +488,67 @@ function focusRow(id: string) {
|
|
|
373
488
|
el?.scrollIntoView({ block: "nearest" })
|
|
374
489
|
}
|
|
375
490
|
|
|
491
|
+
function focusRow(id: string) {
|
|
492
|
+
focusedId.value = id
|
|
493
|
+
if (!props.virtual) {
|
|
494
|
+
focusRowElement(id)
|
|
495
|
+
return
|
|
496
|
+
}
|
|
497
|
+
// A row out of the window has no element yet: bring it in, then reach for it.
|
|
498
|
+
scrollToRow(id)
|
|
499
|
+
void nextTick(() => focusRowElement(id))
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// `virtual`: which rows exist, from the viewport's scroll position and height.
|
|
503
|
+
const scrollArea = ref<{ viewportEl: HTMLElement | null } | null>(null)
|
|
504
|
+
const scrollTop = ref(0)
|
|
505
|
+
const viewportHeight = ref(0)
|
|
506
|
+
|
|
507
|
+
useResizeObserver(
|
|
508
|
+
() => (props.virtual ? scrollArea.value?.viewportEl : null),
|
|
509
|
+
(_, el) => {
|
|
510
|
+
viewportHeight.value = el.clientHeight
|
|
511
|
+
},
|
|
512
|
+
{ immediate: true },
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
function onScroll(event: Event) {
|
|
516
|
+
if (props.virtual) scrollTop.value = (event.target as HTMLElement).scrollTop
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function scrollToRow(id: string) {
|
|
520
|
+
const viewport = scrollArea.value?.viewportEl
|
|
521
|
+
const at = rows.value.findIndex((row) => row.node.id === id)
|
|
522
|
+
if (!viewport || at < 0) return
|
|
523
|
+
const top = at * props.rowHeight
|
|
524
|
+
const bottom = top + props.rowHeight
|
|
525
|
+
if (top < viewport.scrollTop) viewport.scrollTop = top
|
|
526
|
+
else if (bottom > viewport.scrollTop + viewport.clientHeight) viewport.scrollTop = bottom - viewport.clientHeight
|
|
527
|
+
scrollTop.value = viewport.scrollTop
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/** The rows to mount. The focused one stays out of view too: it holds the tab stop. */
|
|
531
|
+
const mountedRows = computed(() => {
|
|
532
|
+
const { start, end } = rowWindow(scrollTop.value, viewportHeight.value, props.rowHeight, rows.value.length, OVERSCAN_ROWS)
|
|
533
|
+
const mounted: { row: (typeof rows.value)[number]; index: number }[] = []
|
|
534
|
+
for (let index = start; index < end; index++) mounted.push({ row: rows.value[index], index })
|
|
535
|
+
const focused = rows.value.findIndex((row) => row.node.id === focusedId.value)
|
|
536
|
+
if (focused >= 0 && (focused < start || focused >= end)) mounted.push({ row: rows.value[focused], index: focused })
|
|
537
|
+
return mounted
|
|
538
|
+
})
|
|
539
|
+
|
|
376
540
|
function setExpanded(next: Set<string>) {
|
|
377
541
|
openIds.value = next
|
|
378
542
|
emit("update:expanded", [...next])
|
|
379
543
|
}
|
|
380
544
|
|
|
381
545
|
function expand(node: FileNode) {
|
|
546
|
+
if (closedWhileFiltering.value.has(node.id)) {
|
|
547
|
+
const closed = new Set(closedWhileFiltering.value)
|
|
548
|
+
closed.delete(node.id)
|
|
549
|
+
closedWhileFiltering.value = closed
|
|
550
|
+
if (filtered.value.openIds.has(node.id)) return
|
|
551
|
+
}
|
|
382
552
|
if (node.kind !== "dir" || openIds.value.has(node.id)) return
|
|
383
553
|
const next = new Set(openIds.value)
|
|
384
554
|
next.add(node.id)
|
|
@@ -387,11 +557,62 @@ function expand(node: FileNode) {
|
|
|
387
557
|
if (node.children === undefined && !requested.has(node.id)) {
|
|
388
558
|
requested.add(node.id)
|
|
389
559
|
emit("expand", node)
|
|
560
|
+
void fetchChildren(node)
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// One request per folder. A controller that is no longer in the map lost its
|
|
565
|
+
// place, so its answer is dropped even if the fetch ignored the signal.
|
|
566
|
+
const fetches = new Map<string, AbortController>()
|
|
567
|
+
|
|
568
|
+
function setFetching(id: string, on: boolean) {
|
|
569
|
+
const next = new Set(ownLoading.value)
|
|
570
|
+
if (on) next.add(id)
|
|
571
|
+
else next.delete(id)
|
|
572
|
+
ownLoading.value = next
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
async function fetchChildren(node: FileNode) {
|
|
576
|
+
if (!props.load) return
|
|
577
|
+
const controller = new AbortController()
|
|
578
|
+
fetches.set(node.id, controller)
|
|
579
|
+
setFetching(node.id, true)
|
|
580
|
+
try {
|
|
581
|
+
const children = await props.load(node, controller.signal)
|
|
582
|
+
if (fetches.get(node.id) !== controller) return
|
|
583
|
+
loadedChildren.value = new Map(loadedChildren.value).set(node.id, children)
|
|
584
|
+
} catch (error) {
|
|
585
|
+
if (fetches.get(node.id) !== controller) return
|
|
586
|
+
requested.delete(node.id)
|
|
587
|
+
emit("load-error", node, error)
|
|
588
|
+
} finally {
|
|
589
|
+
if (fetches.get(node.id) === controller) {
|
|
590
|
+
fetches.delete(node.id)
|
|
591
|
+
setFetching(node.id, false)
|
|
592
|
+
}
|
|
390
593
|
}
|
|
391
594
|
}
|
|
392
595
|
|
|
596
|
+
function cancelFetch(id: string) {
|
|
597
|
+
const controller = fetches.get(id)
|
|
598
|
+
if (!controller) return
|
|
599
|
+
fetches.delete(id)
|
|
600
|
+
controller.abort()
|
|
601
|
+
setFetching(id, false)
|
|
602
|
+
requested.delete(id)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
onBeforeUnmount(() => {
|
|
606
|
+
for (const controller of fetches.values()) controller.abort()
|
|
607
|
+
fetches.clear()
|
|
608
|
+
})
|
|
609
|
+
|
|
393
610
|
function collapse(node: FileNode) {
|
|
611
|
+
if (filtered.value.openIds.has(node.id)) {
|
|
612
|
+
closedWhileFiltering.value = new Set(closedWhileFiltering.value).add(node.id)
|
|
613
|
+
}
|
|
394
614
|
if (!openIds.value.has(node.id)) return
|
|
615
|
+
cancelFetch(node.id)
|
|
395
616
|
// A fetch that failed left `children` unset; collapsing lets the next open
|
|
396
617
|
// ask again instead of showing an empty folder forever.
|
|
397
618
|
if (node.children === undefined && !loadingSet.value.has(node.id)) requested.delete(node.id)
|
|
@@ -402,7 +623,7 @@ function collapse(node: FileNode) {
|
|
|
402
623
|
}
|
|
403
624
|
|
|
404
625
|
function onToggle(node: FileNode) {
|
|
405
|
-
if (
|
|
626
|
+
if (shownOpen.value.has(node.id)) collapse(node)
|
|
406
627
|
else expand(node)
|
|
407
628
|
}
|
|
408
629
|
|
|
@@ -412,13 +633,128 @@ function onSelect(node: FileNode) {
|
|
|
412
633
|
// without picking something else is a trap.
|
|
413
634
|
if (props.selected === node.id) {
|
|
414
635
|
emit("update:selected", "")
|
|
636
|
+
if (props.multiple) setPicked(new Set())
|
|
415
637
|
return
|
|
416
638
|
}
|
|
417
639
|
emit("update:selected", node.id)
|
|
640
|
+
if (props.multiple) {
|
|
641
|
+
pickAnchor = node.id
|
|
642
|
+
setPicked(new Set([node.id]))
|
|
643
|
+
}
|
|
418
644
|
emit("select", node)
|
|
419
645
|
if (node.kind === "file") emit("open", node)
|
|
420
646
|
}
|
|
421
647
|
|
|
648
|
+
// `multiple`. Ids, not rows, so a pick outlives its folder closing or a filter
|
|
649
|
+
// hiding it. Same controlled/uncontrolled deal as `expanded`.
|
|
650
|
+
const pickedIds = ref<Set<string>>(new Set(props.selection ?? []))
|
|
651
|
+
watch(
|
|
652
|
+
() => props.selection,
|
|
653
|
+
(ids) => {
|
|
654
|
+
if (ids) pickedIds.value = new Set(ids)
|
|
655
|
+
},
|
|
656
|
+
)
|
|
657
|
+
let pickAnchor: string | undefined
|
|
658
|
+
|
|
659
|
+
function setPicked(next: Set<string>) {
|
|
660
|
+
pickedIds.value = next
|
|
661
|
+
emit("update:selection", [...next])
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function onPick(node: FileNode, mode: "toggle" | "range") {
|
|
665
|
+
const next = new Set(pickedIds.value)
|
|
666
|
+
if (mode === "range") {
|
|
667
|
+
for (const id of rangeIds(visible.value, pickAnchor, node.id)) next.add(id)
|
|
668
|
+
} else {
|
|
669
|
+
pickAnchor = node.id
|
|
670
|
+
if (!next.delete(node.id)) next.add(node.id)
|
|
671
|
+
}
|
|
672
|
+
setPicked(next)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function extendPick(delta: number) {
|
|
676
|
+
pickAnchor ??= focusedId.value
|
|
677
|
+
move(delta)
|
|
678
|
+
if (!focusedId.value) return
|
|
679
|
+
const next = new Set(pickedIds.value)
|
|
680
|
+
for (const id of rangeIds(visible.value, pickAnchor, focusedId.value)) next.add(id)
|
|
681
|
+
setPicked(next)
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// `movable`. Drags inside this tree only: `moving` is empty for anything else.
|
|
685
|
+
let moving: string[] = []
|
|
686
|
+
const dropTargetId = ref<string>()
|
|
687
|
+
|
|
688
|
+
function onMoveStart(node: FileNode) {
|
|
689
|
+
const ids = props.multiple && pickedIds.value.has(node.id) ? [...pickedIds.value] : [node.id]
|
|
690
|
+
moving = topmostIds(ids.filter((id) => byId.value.has(id)), index.value.parentOfId)
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function onMoveOver(node: FileNode, event: DragEvent) {
|
|
694
|
+
const allowed = canDrop(moving, node, index.value.parentOfId)
|
|
695
|
+
dropTargetId.value = allowed ? node.id : undefined
|
|
696
|
+
if (!allowed) return
|
|
697
|
+
event.preventDefault()
|
|
698
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = "move"
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function onMoveLeave(node: FileNode) {
|
|
702
|
+
if (dropTargetId.value === node.id) dropTargetId.value = undefined
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function onMoveEnd() {
|
|
706
|
+
moving = []
|
|
707
|
+
dropTargetId.value = undefined
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function onMoveDrop(node: FileNode) {
|
|
711
|
+
const target = byId.value.get(node.id)
|
|
712
|
+
if (target && canDrop(moving, target, index.value.parentOfId)) {
|
|
713
|
+
emit("move", { nodes: moving.flatMap((id) => byId.value.get(id) ?? []), target })
|
|
714
|
+
}
|
|
715
|
+
onMoveEnd()
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// `renamable`. The tree holds only the text being typed.
|
|
719
|
+
const renaming = ref<{ node: FileNode; value: string } | null>(null)
|
|
720
|
+
|
|
721
|
+
const siblingsOf = (node: FileNode) => {
|
|
722
|
+
const parent = index.value.parentOfId.get(node.id)
|
|
723
|
+
return (parent === undefined ? source.value : byId.value.get(parent)?.children) ?? []
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const renameInvalid = computed(() => {
|
|
727
|
+
const edit = renaming.value
|
|
728
|
+
return !!edit && renameProblem(edit.value, edit.node, siblingsOf(edit.node)) !== null
|
|
729
|
+
})
|
|
730
|
+
|
|
731
|
+
function startRename(id: string) {
|
|
732
|
+
const node = byId.value.get(id)
|
|
733
|
+
if (!props.renamable || !node || node.disabled) return
|
|
734
|
+
focusedId.value = id
|
|
735
|
+
renaming.value = { node, value: node.name }
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function endRename(refocus: boolean) {
|
|
739
|
+
const id = renaming.value?.node.id
|
|
740
|
+
renaming.value = null
|
|
741
|
+
if (refocus && id) void nextTick(() => focusRow(id))
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Enter on a bad name stays and says so. Leaving the field gives it up instead:
|
|
745
|
+
// the user has gone elsewhere, and a field that holds them there is a trap.
|
|
746
|
+
function commitRename(blurred = false) {
|
|
747
|
+
const edit = renaming.value
|
|
748
|
+
if (!edit) return
|
|
749
|
+
if (renameInvalid.value) {
|
|
750
|
+
if (blurred) endRename(false)
|
|
751
|
+
return
|
|
752
|
+
}
|
|
753
|
+
const name = edit.value.trim()
|
|
754
|
+
if (name !== edit.node.name) emit("rename", edit.node, name)
|
|
755
|
+
endRename(!blurred)
|
|
756
|
+
}
|
|
757
|
+
|
|
422
758
|
/** Nearest expanded ancestor of `id`, or undefined at the top level. */
|
|
423
759
|
function parentOf(id: string): FileNode | undefined {
|
|
424
760
|
const parentId = index.value.parentOfId.get(id)
|
|
@@ -429,7 +765,7 @@ function move(delta: number) {
|
|
|
429
765
|
const rows = visible.value
|
|
430
766
|
if (!rows.length) return
|
|
431
767
|
const i = rows.findIndex((n) => n.id === focusedId.value)
|
|
432
|
-
const next = rows[
|
|
768
|
+
const next = rows[clamp((i < 0 ? 0 : i) + delta, 0, rows.length - 1)]
|
|
433
769
|
if (next) focusRow(next.id)
|
|
434
770
|
}
|
|
435
771
|
|
|
@@ -442,17 +778,29 @@ function onKeydown(e: KeyboardEvent) {
|
|
|
442
778
|
switch (e.key) {
|
|
443
779
|
case "ArrowDown":
|
|
444
780
|
e.preventDefault()
|
|
445
|
-
|
|
781
|
+
if (props.multiple && e.shiftKey) extendPick(1)
|
|
782
|
+
else move(1)
|
|
446
783
|
break
|
|
447
784
|
case "ArrowUp":
|
|
448
785
|
e.preventDefault()
|
|
449
|
-
|
|
786
|
+
if (props.multiple && e.shiftKey) extendPick(-1)
|
|
787
|
+
else move(-1)
|
|
788
|
+
break
|
|
789
|
+
case "F2":
|
|
790
|
+
if (!node || !props.renamable) return
|
|
791
|
+
e.preventDefault()
|
|
792
|
+
startRename(node.id)
|
|
793
|
+
break
|
|
794
|
+
case "a":
|
|
795
|
+
if (!props.multiple || !(e.ctrlKey || e.metaKey)) return
|
|
796
|
+
e.preventDefault()
|
|
797
|
+
setPicked(new Set([...pickedIds.value, ...visible.value.filter((row) => !row.disabled).map((row) => row.id)]))
|
|
450
798
|
break
|
|
451
799
|
case "ArrowRight": {
|
|
452
800
|
if (!node) return
|
|
453
801
|
e.preventDefault()
|
|
454
802
|
// Closed directory opens; already-open one steps to its first child.
|
|
455
|
-
if (node.kind === "dir" && !
|
|
803
|
+
if (node.kind === "dir" && !shownOpen.value.has(node.id)) expand(node)
|
|
456
804
|
else if (node.kind === "dir" && node.children?.length) move(1)
|
|
457
805
|
break
|
|
458
806
|
}
|
|
@@ -460,7 +808,7 @@ function onKeydown(e: KeyboardEvent) {
|
|
|
460
808
|
if (!node) return
|
|
461
809
|
e.preventDefault()
|
|
462
810
|
// Open directory closes; anything else steps out to the parent row.
|
|
463
|
-
if (node.kind === "dir" &&
|
|
811
|
+
if (node.kind === "dir" && shownOpen.value.has(node.id)) collapse(node)
|
|
464
812
|
else {
|
|
465
813
|
const parent = parentOf(node.id)
|
|
466
814
|
if (parent) focusRow(parent.id)
|
|
@@ -503,7 +851,7 @@ function onKeydown(e: KeyboardEvent) {
|
|
|
503
851
|
/** Expand every ancestor of `id`, focus it and make it the selection. */
|
|
504
852
|
function reveal(id: string) {
|
|
505
853
|
const next = new Set(openIds.value)
|
|
506
|
-
for (const ancestor of ancestorIds(
|
|
854
|
+
for (const ancestor of ancestorIds(source.value, id)) next.add(ancestor)
|
|
507
855
|
setExpanded(next)
|
|
508
856
|
emit("update:selected", id)
|
|
509
857
|
// Wait a frame so the newly expanded rows exist before we query for one.
|
|
@@ -520,7 +868,7 @@ function expandAll() {
|
|
|
520
868
|
}
|
|
521
869
|
}
|
|
522
870
|
}
|
|
523
|
-
walk(
|
|
871
|
+
walk(source.value)
|
|
524
872
|
setExpanded(next)
|
|
525
873
|
}
|
|
526
874
|
|
|
@@ -528,7 +876,66 @@ function collapseAll() {
|
|
|
528
876
|
setExpanded(new Set())
|
|
529
877
|
}
|
|
530
878
|
|
|
531
|
-
|
|
879
|
+
/** Fetch a folder again through `load`. */
|
|
880
|
+
function reload(id: string) {
|
|
881
|
+
const node = byId.value.get(id)
|
|
882
|
+
if (!node || !props.load) return
|
|
883
|
+
cancelFetch(id)
|
|
884
|
+
void fetchChildren(node)
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
defineExpose({ reveal, expandAll, collapseAll, checkAll, clearChecked, summary, rename: startRename, reload })
|
|
888
|
+
|
|
889
|
+
// LpBreadcrumbs clicks through a link component; a step here goes nowhere but a row.
|
|
890
|
+
const CrumbButton: FunctionalComponent<{ to?: string }> = (_, { slots }) => h("button", { type: "button" }, slots.default?.())
|
|
891
|
+
CrumbButton.props = ["to"]
|
|
892
|
+
|
|
893
|
+
const trail = computed(() => {
|
|
894
|
+
const id = props.selected || focusedId.value
|
|
895
|
+
return props.breadcrumb && id ? nodeTrail(id, byId.value, index.value.parentOfId) : []
|
|
896
|
+
})
|
|
897
|
+
const crumbs = computed(() => trail.value.map((node) => ({ label: node.name, to: node.id })))
|
|
898
|
+
|
|
899
|
+
// What every row takes, whichever way the rows are laid out.
|
|
900
|
+
const rowProps = computed(() => ({
|
|
901
|
+
expanded: shownOpen.value,
|
|
902
|
+
loading: loadingSet.value,
|
|
903
|
+
selectedId: props.selected,
|
|
904
|
+
focusedId: focusedId.value,
|
|
905
|
+
sort: props.sort,
|
|
906
|
+
iconSize: props.iconSize,
|
|
907
|
+
checkable: props.checkable,
|
|
908
|
+
checked: checkedIds.value,
|
|
909
|
+
stats: stats.value,
|
|
910
|
+
showSize: props.showSize,
|
|
911
|
+
showModified: props.showModified,
|
|
912
|
+
selectedIds: props.multiple ? pickedIds.value : undefined,
|
|
913
|
+
multiple: props.multiple,
|
|
914
|
+
movable: props.movable,
|
|
915
|
+
dropTargetId: dropTargetId.value,
|
|
916
|
+
renamingId: renaming.value?.node.id,
|
|
917
|
+
renameValue: renaming.value?.value,
|
|
918
|
+
renameInvalid: renameInvalid.value,
|
|
919
|
+
renameLabel: text.value.rename,
|
|
920
|
+
}))
|
|
921
|
+
|
|
922
|
+
const rowEvents = {
|
|
923
|
+
select: onSelect,
|
|
924
|
+
toggle: onToggle,
|
|
925
|
+
focus: (node: FileNode) => (focusedId.value = node.id),
|
|
926
|
+
check: onCheck,
|
|
927
|
+
pick: onPick,
|
|
928
|
+
moveStart: onMoveStart,
|
|
929
|
+
moveOver: onMoveOver,
|
|
930
|
+
moveLeave: onMoveLeave,
|
|
931
|
+
moveDrop: onMoveDrop,
|
|
932
|
+
moveEnd: onMoveEnd,
|
|
933
|
+
renameInput: (value: string) => {
|
|
934
|
+
if (renaming.value) renaming.value = { ...renaming.value, value }
|
|
935
|
+
},
|
|
936
|
+
renameCommit: commitRename,
|
|
937
|
+
renameCancel: () => endRename(true),
|
|
938
|
+
}
|
|
532
939
|
|
|
533
940
|
// The tree fills its container; a consumer giving it a fixed height or width in a split pane must be able to say so.
|
|
534
941
|
const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
@@ -549,7 +956,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
549
956
|
</div>
|
|
550
957
|
|
|
551
958
|
<LpEmptyState
|
|
552
|
-
v-else-if="!roots.length"
|
|
959
|
+
v-else-if="!roots.length && !filtering"
|
|
553
960
|
icon="lucide:folder-search"
|
|
554
961
|
:title="emptyLabel ?? text.empty"
|
|
555
962
|
:description="emptyDescription ?? text.emptyDescription"
|
|
@@ -560,39 +967,73 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
560
967
|
container and nothing ever scrolls. min-w-0/w-full for the same reason
|
|
561
968
|
sideways: a long path must truncate inside the box rather than widen it. -->
|
|
562
969
|
<div v-else class="flex h-full w-full min-w-0 flex-col">
|
|
563
|
-
<
|
|
970
|
+
<LpInput
|
|
971
|
+
v-if="searchable"
|
|
972
|
+
type="search"
|
|
973
|
+
class="mb-1 shrink-0"
|
|
974
|
+
:model-value="queryText"
|
|
975
|
+
:placeholder="text.search"
|
|
976
|
+
:aria-label="text.search"
|
|
977
|
+
@update:model-value="setQuery($event ?? '')"
|
|
978
|
+
/>
|
|
979
|
+
<LpBreadcrumbs
|
|
980
|
+
v-if="crumbs.length"
|
|
981
|
+
class="mb-1 shrink-0 text-xs"
|
|
982
|
+
:items="crumbs"
|
|
983
|
+
:as="CrumbButton"
|
|
984
|
+
:labels="{ nav: text.path }"
|
|
985
|
+
@navigate="(_, i) => reveal(trail[i].id)"
|
|
986
|
+
/>
|
|
987
|
+
<LpEmptyState
|
|
988
|
+
v-if="!roots.length"
|
|
989
|
+
icon="lucide:search-x"
|
|
990
|
+
:title="text.noMatches"
|
|
991
|
+
/>
|
|
992
|
+
<LpScrollArea v-else ref="scrollArea" class="min-h-0 w-full min-w-0 flex-1" @scroll="onScroll">
|
|
564
993
|
<ul
|
|
565
994
|
ref="root"
|
|
566
995
|
role="tree"
|
|
567
|
-
:aria-multiselectable="checkable"
|
|
996
|
+
:aria-multiselectable="checkable || multiple"
|
|
568
997
|
class="flex min-w-0 flex-col"
|
|
998
|
+
:class="virtual ? 'relative' : ''"
|
|
999
|
+
:style="virtual ? { height: `${rows.length * rowHeight}px` } : undefined"
|
|
569
1000
|
@keydown="onKeydown"
|
|
570
1001
|
>
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
>
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
1002
|
+
<!-- Flat and windowed: each row says its own level and place, and sits at
|
|
1003
|
+
its own offset, so a row kept for focus needs no filler around it. -->
|
|
1004
|
+
<template v-if="virtual">
|
|
1005
|
+
<LpFileTreeNode
|
|
1006
|
+
v-for="{ row, index: at } in mountedRows"
|
|
1007
|
+
:key="row.node.id"
|
|
1008
|
+
class="absolute inset-x-0 top-0"
|
|
1009
|
+
:style="{ height: `${rowHeight}px`, transform: `translateY(${at * rowHeight}px)` }"
|
|
1010
|
+
:node="row.node"
|
|
1011
|
+
:depth="row.depth"
|
|
1012
|
+
flat
|
|
1013
|
+
:set-size="row.setSize"
|
|
1014
|
+
:pos-in-set="row.posInSet"
|
|
1015
|
+
v-bind="rowProps"
|
|
1016
|
+
v-on="rowEvents"
|
|
1017
|
+
>
|
|
1018
|
+
<template #row="slotProps">
|
|
1019
|
+
<slot name="row" v-bind="slotProps" />
|
|
1020
|
+
</template>
|
|
1021
|
+
</LpFileTreeNode>
|
|
1022
|
+
</template>
|
|
1023
|
+
<template v-else>
|
|
1024
|
+
<LpFileTreeNode
|
|
1025
|
+
v-for="node in roots"
|
|
1026
|
+
:key="node.id"
|
|
1027
|
+
:node="node"
|
|
1028
|
+
:depth="0"
|
|
1029
|
+
v-bind="rowProps"
|
|
1030
|
+
v-on="rowEvents"
|
|
1031
|
+
>
|
|
1032
|
+
<template #row="slotProps">
|
|
1033
|
+
<slot name="row" v-bind="slotProps" />
|
|
1034
|
+
</template>
|
|
1035
|
+
</LpFileTreeNode>
|
|
1036
|
+
</template>
|
|
596
1037
|
</ul>
|
|
597
1038
|
</LpScrollArea>
|
|
598
1039
|
|