@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.
- 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-E8KtwU7-.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-C-SpPfBG.js → LpAutocomplete-DkY6CSyD.js} +24 -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 +13 -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,7 @@
|
|
|
3
3
|
* so they can be tested without a media element.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { clamp } from "../composables/clamp"
|
|
6
7
|
import { splitTime } from "./timeSpan"
|
|
7
8
|
|
|
8
9
|
/** The stretch of a clip that loops, and how long two passes overlap at the seam. */
|
|
@@ -29,16 +30,16 @@ export function planLoop(
|
|
|
29
30
|
options: { start?: number; end?: number; crossfade?: number; minLength?: number } = {},
|
|
30
31
|
): LoopPlan {
|
|
31
32
|
if (!Number.isFinite(duration) || duration <= 0) return { from: 0, to: 0, fade: 0 }
|
|
32
|
-
const
|
|
33
|
-
let from =
|
|
34
|
-
let to =
|
|
33
|
+
const within = (value: number) => clamp(value, 0, duration)
|
|
34
|
+
let from = within(options.start ?? 0)
|
|
35
|
+
let to = within(options.end ?? duration)
|
|
35
36
|
if (to < from) [from, to] = [to, from]
|
|
36
|
-
const minLength =
|
|
37
|
+
const minLength = clamp(options.minLength ?? 0.5, 0, duration)
|
|
37
38
|
if (to - from < minLength) {
|
|
38
39
|
to = Math.min(duration, from + minLength)
|
|
39
40
|
from = Math.max(0, to - minLength)
|
|
40
41
|
}
|
|
41
|
-
const fade =
|
|
42
|
+
const fade = clamp(options.crossfade ?? 0, 0, (to - from) / 2)
|
|
42
43
|
return { from, to, fade }
|
|
43
44
|
}
|
|
44
45
|
|
|
@@ -55,7 +56,7 @@ export function seamReached(plan: LoopPlan, time: number): boolean {
|
|
|
55
56
|
*/
|
|
56
57
|
export function seamProgress(plan: LoopPlan, incomingTime: number): number {
|
|
57
58
|
if (plan.fade <= 0) return 1
|
|
58
|
-
return
|
|
59
|
+
return clamp((incomingTime - plan.from) / plan.fade, 0, 1)
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
/** Whether the whole clip loops with nothing cut and no overlap, which the media element does natively. */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare const process: { env: { NODE_ENV?: string } }
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Written as the literal `process.env.NODE_ENV` so the consumer's bundler
|
|
5
|
+
* replaces it (Vite and webpack both do for dependencies); where nothing
|
|
6
|
+
* replaces it and no `process` exists, warnings simply stay off.
|
|
7
|
+
*/
|
|
8
|
+
export function isDev(): boolean {
|
|
9
|
+
try {
|
|
10
|
+
return process.env.NODE_ENV !== "production"
|
|
11
|
+
} catch {
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** A warning for the developer only. */
|
|
17
|
+
export function devWarn(message: string): void {
|
|
18
|
+
if (isDev()) console.warn(`[@leavepulse/ui] ${message}`)
|
|
19
|
+
}
|
|
@@ -28,10 +28,33 @@
|
|
|
28
28
|
* A plain function, not a reactive composable: it is read at the moment an
|
|
29
29
|
* animation starts, and a component that also needs to RE-RENDER when the
|
|
30
30
|
* setting changes mid-session should use `usePrefersReducedMotion`.
|
|
31
|
+
*
|
|
32
|
+
* Which one to use: a one-off animation reads this as it starts; anything that
|
|
33
|
+
* keeps running (a loop, a spring, a video) follows the reactive one or the
|
|
34
|
+
* media query's own change event, so it stops without a new action; a CSS
|
|
35
|
+
* transition uses `prefers-reduced-motion` in CSS.
|
|
36
|
+
*
|
|
37
|
+
* `win` is the window to ask, for a caller inside an LpFrame.
|
|
31
38
|
*/
|
|
32
|
-
export const prefersReducedMotion = (): boolean =>
|
|
33
|
-
typeof window
|
|
34
|
-
|
|
39
|
+
export const prefersReducedMotion = (win?: Window): boolean => {
|
|
40
|
+
const target = win ?? (typeof window === "undefined" ? undefined : window)
|
|
41
|
+
return target?.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
* Where a motion token is read from. A scoped theme (LpThemeProvider) or an
|
|
46
|
+
* LpFrame declares its tokens on its own element or document, so a caller with
|
|
47
|
+
* an element at hand passes it; without one it is the page's root.
|
|
48
|
+
*/
|
|
49
|
+
type TokenScope = Element | Document
|
|
50
|
+
|
|
51
|
+
function readToken(name: string, scope?: TokenScope): string | null {
|
|
52
|
+
if (typeof window === "undefined") return null
|
|
53
|
+
const el = scope ? ("documentElement" in scope ? scope.documentElement : scope) : document.documentElement
|
|
54
|
+
const view = el.ownerDocument?.defaultView ?? window
|
|
55
|
+
if (!view.getComputedStyle) return null
|
|
56
|
+
return view.getComputedStyle(el).getPropertyValue(`--${name}`).trim()
|
|
57
|
+
}
|
|
35
58
|
|
|
36
59
|
/**
|
|
37
60
|
* A motion token's value, for an API that cannot take a `var()`.
|
|
@@ -47,20 +70,21 @@ export const prefersReducedMotion = (): boolean =>
|
|
|
47
70
|
export const easingToken = (
|
|
48
71
|
name: "ease-emphasized" | "ease-travel" | "ease-settle",
|
|
49
72
|
fallback = "cubic-bezier(0.2, 0, 0, 1)",
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const value = getComputedStyle(document.documentElement).getPropertyValue(`--${name}`).trim()
|
|
53
|
-
return value || fallback
|
|
54
|
-
}
|
|
73
|
+
scope?: TokenScope,
|
|
74
|
+
): string => readToken(name, scope) || fallback
|
|
55
75
|
|
|
56
76
|
/**
|
|
57
77
|
* A duration token in milliseconds, for an animation or a timer driven in
|
|
58
78
|
* script. Same reasoning as `easingToken`: a typed-out 220 would not follow a
|
|
59
79
|
* theme that retunes the token.
|
|
60
80
|
*/
|
|
61
|
-
export const durationToken = (
|
|
62
|
-
|
|
63
|
-
|
|
81
|
+
export const durationToken = (
|
|
82
|
+
name: "duration-fast" | "duration-medium",
|
|
83
|
+
fallback = 220,
|
|
84
|
+
scope?: TokenScope,
|
|
85
|
+
): number => {
|
|
86
|
+
const raw = readToken(name, scope)
|
|
87
|
+
if (raw === null) return fallback
|
|
64
88
|
const ms = Number.parseFloat(raw)
|
|
65
89
|
return Number.isFinite(ms) ? (raw.endsWith("ms") || !raw.endsWith("s") ? ms : ms * 1000) : fallback
|
|
66
90
|
}
|
|
@@ -156,8 +180,9 @@ export const cubicBezier = (x1: number, y1: number, x2: number, y2: number) => {
|
|
|
156
180
|
*/
|
|
157
181
|
export const tokenEase = (
|
|
158
182
|
name: "ease-emphasized" | "ease-travel" | "ease-settle",
|
|
183
|
+
scope?: TokenScope,
|
|
159
184
|
): ((t: number) => number) => {
|
|
160
|
-
const match = /cubic-bezier\(([^)]+)\)/.exec(easingToken(name))
|
|
185
|
+
const match = /cubic-bezier\(([^)]+)\)/.exec(easingToken(name, undefined, scope))
|
|
161
186
|
const n = match?.[1].split(",").map((p) => Number.parseFloat(p))
|
|
162
187
|
return n && n.length === 4 && n.every(Number.isFinite)
|
|
163
188
|
? cubicBezier(n[0], n[1], n[2], n[3])
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
|
|
41
41
|
const KEY = "__lpOverlay"
|
|
42
42
|
const SETTLE_MS = 300
|
|
43
|
+
/* Far longer than a traversal takes: past it, the step the kit asked for is not coming. */
|
|
44
|
+
const STEP_TIMEOUT_MS = 1500
|
|
43
45
|
|
|
44
46
|
export interface HistoryEntry {
|
|
45
47
|
/** Back left this entry: undo what it recorded (close the overlay, restore the tab). */
|
|
@@ -91,6 +93,8 @@ interface EngineState {
|
|
|
91
93
|
serial: number
|
|
92
94
|
internal: boolean
|
|
93
95
|
settle: number
|
|
96
|
+
/** Timer that gives up on `silent` pops the browser never delivers. */
|
|
97
|
+
stalled: number
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
const STATE_KEY = Symbol.for("@leavepulse/ui:overlay-history")
|
|
@@ -111,6 +115,7 @@ const fresh: EngineState = {
|
|
|
111
115
|
serial: 0,
|
|
112
116
|
internal: false,
|
|
113
117
|
settle: 0,
|
|
118
|
+
stalled: 0,
|
|
114
119
|
}
|
|
115
120
|
const s = (g[STATE_KEY] ??= fresh)
|
|
116
121
|
|
|
@@ -142,10 +147,25 @@ function own<T>(run: () => T): T {
|
|
|
142
147
|
function silentGo(delta: number) {
|
|
143
148
|
if (delta === 0) return
|
|
144
149
|
s.silent++
|
|
150
|
+
window.clearTimeout(s.stalled)
|
|
151
|
+
s.stalled = window.setTimeout(abandonSteps, STEP_TIMEOUT_MS)
|
|
145
152
|
mute()
|
|
146
153
|
own(() => history.go(delta))
|
|
147
154
|
}
|
|
148
155
|
|
|
156
|
+
/*
|
|
157
|
+
* `go` reports nothing: with no entry to go to, or a traversal the browser
|
|
158
|
+
* dropped, no pop ever comes. Left waiting, the engine would swallow the user's
|
|
159
|
+
* next Back, hold every push in `waiting` and keep the router muted for good.
|
|
160
|
+
*/
|
|
161
|
+
function abandonSteps() {
|
|
162
|
+
s.silent = 0
|
|
163
|
+
s.keep = null
|
|
164
|
+
s.href = location.href
|
|
165
|
+
flushWaiting()
|
|
166
|
+
mute()
|
|
167
|
+
}
|
|
168
|
+
|
|
149
169
|
/* Pushes wait for a pop the kit started: a push between `go` and its arrival would land on the wrong entry. */
|
|
150
170
|
function whenSettled(run: () => void) {
|
|
151
171
|
if (s.silent > 0) s.waiting.push(run)
|
|
@@ -182,6 +202,7 @@ function handlePop(event: PopStateEvent) {
|
|
|
182
202
|
s.href = location.href
|
|
183
203
|
if (s.silent > 0) {
|
|
184
204
|
s.silent--
|
|
205
|
+
if (s.silent === 0) window.clearTimeout(s.stalled)
|
|
185
206
|
const keep = s.silent === 0 ? s.keep : null
|
|
186
207
|
if (s.silent === 0) s.keep = null
|
|
187
208
|
if (keep && keep.href !== location.href) restore(keep, event)
|
|
@@ -322,6 +343,9 @@ export function setHistoryRouter(router: HistoryRouter | null): void {
|
|
|
322
343
|
if (s.router && s.router !== router) s.router.listening = true
|
|
323
344
|
s.router = router
|
|
324
345
|
if (typeof window === "undefined") return
|
|
346
|
+
// Now, not at the first overlay: a page reloaded on a kit entry mutes the
|
|
347
|
+
// router below, and only the engine's listeners ever switch it back on.
|
|
348
|
+
install()
|
|
325
349
|
// After the router's own listener, which it added when it was created.
|
|
326
350
|
window.removeEventListener("popstate", afterPop)
|
|
327
351
|
window.addEventListener("popstate", afterPop)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { nodeWindow } from "./useView"
|
|
2
|
+
|
|
3
|
+
export interface PointerDragHandlers {
|
|
4
|
+
move: (event: PointerEvent) => void
|
|
5
|
+
/** `completed` is false for a cancelled or lost pointer: nothing should be committed. */
|
|
6
|
+
end: (completed: boolean) => void
|
|
7
|
+
/** Hold this cursor over the whole page for the drag, the way a resize grip wants. */
|
|
8
|
+
cursor?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Follow a pointer from the handle it pressed until it lets go, however that
|
|
13
|
+
* ends: release, a cancelled touch, a lost capture or the returned stop. The
|
|
14
|
+
* end runs once. Everything happens on the handle, which holds the capture, so
|
|
15
|
+
* it works inside an LpFrame, and the body's text selection is off meanwhile,
|
|
16
|
+
* in the handle's own window, and put back as it was.
|
|
17
|
+
*/
|
|
18
|
+
export function startPointerDrag(event: PointerEvent, handlers: PointerDragHandlers): () => void {
|
|
19
|
+
const handle = event.currentTarget as HTMLElement
|
|
20
|
+
const body = nodeWindow(handle).document.body.style
|
|
21
|
+
const { userSelect, cursor } = body
|
|
22
|
+
let active = true
|
|
23
|
+
|
|
24
|
+
const finish = (completed: boolean) => {
|
|
25
|
+
if (!active) return
|
|
26
|
+
active = false
|
|
27
|
+
handle.removeEventListener("pointermove", onMove)
|
|
28
|
+
handle.removeEventListener("pointerup", onUp)
|
|
29
|
+
handle.removeEventListener("pointercancel", onCancel)
|
|
30
|
+
handle.removeEventListener("lostpointercapture", onCancel)
|
|
31
|
+
body.userSelect = userSelect
|
|
32
|
+
body.cursor = cursor
|
|
33
|
+
handlers.end(completed)
|
|
34
|
+
}
|
|
35
|
+
function onMove(ev: PointerEvent) {
|
|
36
|
+
if (ev.pointerId === event.pointerId) handlers.move(ev)
|
|
37
|
+
}
|
|
38
|
+
function onUp(ev: PointerEvent) {
|
|
39
|
+
if (ev.pointerId === event.pointerId) finish(true)
|
|
40
|
+
}
|
|
41
|
+
function onCancel(ev: PointerEvent) {
|
|
42
|
+
if (ev.pointerId === event.pointerId) finish(false)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
event.preventDefault()
|
|
46
|
+
body.userSelect = "none"
|
|
47
|
+
if (handlers.cursor) body.cursor = handlers.cursor
|
|
48
|
+
handle.setPointerCapture(event.pointerId)
|
|
49
|
+
handle.addEventListener("pointermove", onMove)
|
|
50
|
+
handle.addEventListener("pointerup", onUp)
|
|
51
|
+
handle.addEventListener("pointercancel", onCancel)
|
|
52
|
+
handle.addEventListener("lostpointercapture", onCancel)
|
|
53
|
+
return () => finish(false)
|
|
54
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { ComponentPublicInstance, Plugin } from "vue"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Development check for a misspelt prop on a kit component.
|
|
5
|
+
*
|
|
6
|
+
* A prop a component does not declare falls through as an attribute with no
|
|
7
|
+
* error, and vue-tsc's own `checkUnknownProps` cannot tell that apart from the
|
|
8
|
+
* `id`, `title` or `aria-*` an app rightly passes through. Here the attribute
|
|
9
|
+
* is checked against the element it would land on, so `<LpTooltip text>` is
|
|
10
|
+
* reported and `<LpInput autocomplete>` is not.
|
|
11
|
+
*
|
|
12
|
+
* Install it in development only:
|
|
13
|
+
*
|
|
14
|
+
* if (import.meta.env.DEV) app.use(lpPropCheck)
|
|
15
|
+
*
|
|
16
|
+
* The Nuxt module does that on its own. Attributes of a third-party library
|
|
17
|
+
* that nothing here can know go in `ignore`.
|
|
18
|
+
*/
|
|
19
|
+
export interface PropCheckOptions {
|
|
20
|
+
/** Attributes that are not a mistake: exact names or patterns. */
|
|
21
|
+
ignore?: (string | RegExp)[]
|
|
22
|
+
/** Where a report goes. Defaults to console.warn. */
|
|
23
|
+
warn?: (message: string) => void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const KIT_COMPONENT = /^Lp[A-Z]/
|
|
27
|
+
const ALWAYS_ALLOWED = /^(data-|aria-|on[A-Z])/
|
|
28
|
+
const GLOBAL_ATTRIBUTES = new Set([
|
|
29
|
+
"accesskey", "class", "contenteditable", "dir", "draggable", "enterkeyhint", "for", "hidden",
|
|
30
|
+
"id", "inert", "inputmode", "is", "key", "lang", "nonce", "popover", "ref", "role", "slot",
|
|
31
|
+
"spellcheck", "style", "tabindex", "title", "translate",
|
|
32
|
+
])
|
|
33
|
+
/* Where a component with `inheritAttrs: false` hands its attributes on to. */
|
|
34
|
+
const FORWARD_TARGETS = "input, textarea, select, button, a, img, form, label"
|
|
35
|
+
|
|
36
|
+
const propertiesByTag = new Map<string, Set<string>>()
|
|
37
|
+
|
|
38
|
+
function propertiesOf(el: Element): Set<string> {
|
|
39
|
+
let known = propertiesByTag.get(el.tagName)
|
|
40
|
+
if (!known) {
|
|
41
|
+
known = new Set<string>()
|
|
42
|
+
for (const name in el) known.add(name.toLowerCase())
|
|
43
|
+
propertiesByTag.set(el.tagName, known)
|
|
44
|
+
}
|
|
45
|
+
return known
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const normalized = (name: string) => name.replaceAll("-", "").toLowerCase()
|
|
49
|
+
|
|
50
|
+
/** The attributes no element under this component could take. */
|
|
51
|
+
export function unknownAttributes(
|
|
52
|
+
attributes: string[],
|
|
53
|
+
elements: Element[],
|
|
54
|
+
ignore: PropCheckOptions["ignore"] = [],
|
|
55
|
+
): string[] {
|
|
56
|
+
return attributes.filter((name) => {
|
|
57
|
+
if (ALWAYS_ALLOWED.test(name) || GLOBAL_ATTRIBUTES.has(name.toLowerCase())) return false
|
|
58
|
+
if (ignore.some((rule) => (typeof rule === "string" ? rule === name : rule.test(name)))) return false
|
|
59
|
+
const key = normalized(name)
|
|
60
|
+
return !elements.some((el) => propertiesOf(el).has(key))
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function renderedElements(instance: ComponentPublicInstance): Element[] {
|
|
65
|
+
const root = instance.$el as Node | null
|
|
66
|
+
if (root?.nodeType !== 1) return []
|
|
67
|
+
const el = root as Element
|
|
68
|
+
return [el, ...el.querySelectorAll(FORWARD_TARGETS)]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const lpPropCheck: Plugin<[PropCheckOptions?]> = {
|
|
72
|
+
install(app, options = {}) {
|
|
73
|
+
const warn = options.warn ?? ((message: string) => console.warn(message))
|
|
74
|
+
const reported = new Set<string>()
|
|
75
|
+
app.mixin({
|
|
76
|
+
mounted(this: ComponentPublicInstance) {
|
|
77
|
+
const name = this.$options.__name
|
|
78
|
+
if (!name || !KIT_COMPONENT.test(name)) return
|
|
79
|
+
const attributes = Object.keys(this.$attrs)
|
|
80
|
+
if (!attributes.length) return
|
|
81
|
+
for (const attribute of unknownAttributes(attributes, renderedElements(this), options.ignore)) {
|
|
82
|
+
const id = `${name}:${attribute}`
|
|
83
|
+
if (reported.has(id)) continue
|
|
84
|
+
reported.add(id)
|
|
85
|
+
warn(`[@leavepulse/ui] <${name}> has no prop "${attribute}". Props: ${Object.keys(this.$props).join(", ")}`)
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
},
|
|
90
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* `instanceof Element` is false for a node of an LpFrame: each window has its
|
|
3
|
+
* own classes. These go by what the node is, and what it belongs to.
|
|
4
|
+
*/
|
|
5
|
+
export function isNode(value: unknown): value is Node {
|
|
6
|
+
return typeof (value as Node | null | undefined)?.nodeType === "number"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isElement(value: unknown): value is Element {
|
|
10
|
+
return (value as Node | null | undefined)?.nodeType === 1
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isInputElement(value: unknown): value is HTMLInputElement {
|
|
14
|
+
return isElement(value) && value.tagName === "INPUT"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isHTMLElement(value: unknown): value is HTMLElement {
|
|
18
|
+
return isElement(value) && "style" in value
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** The window a node lives in, with that realm's constructors (`MouseEvent`, `ResizeObserver`). */
|
|
22
|
+
export function nodeWindow(node: Node): Window & typeof globalThis {
|
|
23
|
+
const doc = node.nodeType === 9 ? (node as Document) : node.ownerDocument
|
|
24
|
+
return (doc?.defaultView ?? globalThis) as Window & typeof globalThis
|
|
25
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { nodeWindow } from "./useView"
|
|
2
|
+
|
|
1
3
|
/*
|
|
2
4
|
* The element that actually scrolls, and scrolling it to show a child. Inside an
|
|
3
5
|
* app shell that is a scroll region, not the window, and `scrollIntoView` moves
|
|
@@ -18,7 +20,7 @@ export interface ScrollParentOptions {
|
|
|
18
20
|
export function scrollParent(el: Element | null | undefined, { axis = "y", self = false }: ScrollParentOptions = {}): HTMLElement | null {
|
|
19
21
|
for (let node = self ? (el as HTMLElement | null | undefined) : el?.parentElement; node; node = node.parentElement) {
|
|
20
22
|
if (node === node.ownerDocument.body || node === node.ownerDocument.documentElement) return null
|
|
21
|
-
const style = getComputedStyle(node)
|
|
23
|
+
const style = nodeWindow(node).getComputedStyle(node)
|
|
22
24
|
const y = axis !== "x" && SCROLLS.test(style.overflowY) && node.scrollHeight > node.clientHeight
|
|
23
25
|
const x = axis !== "y" && SCROLLS.test(style.overflowX) && node.scrollWidth > node.clientWidth
|
|
24
26
|
if (y || x) return node
|
|
@@ -48,7 +50,7 @@ export interface ScrollToElementOptions {
|
|
|
48
50
|
/** Scroll the nearest scroller, and only it, so that `el` is in view. */
|
|
49
51
|
/** The element's own `scroll-margin-top`, the gap it asks to keep above it when scrolled to. */
|
|
50
52
|
export function scrollMarginTop(el: Element): number {
|
|
51
|
-
return Number.parseFloat(getComputedStyle(el).scrollMarginTop) || 0
|
|
53
|
+
return Number.parseFloat(nodeWindow(el).getComputedStyle(el).scrollMarginTop) || 0
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
export function scrollToElement(
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { nodeWindow } from "./realm"
|
|
1
2
|
import { nextTick, onBeforeUnmount, onMounted, watch } from "vue"
|
|
2
3
|
import { scrollMarginTop } from "./scroll"
|
|
3
4
|
|
|
4
5
|
/*
|
|
5
|
-
* Where a page lands when the shell
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
6
|
+
* Where a page lands when the shell changes page. In its own scroll region a
|
|
7
|
+
* router's scrollBehavior never reaches the page, and the shell has to answer
|
|
8
|
+
* the questions a browser answers for a plain site. When the document scrolls
|
|
9
|
+
* (the viewport is the document's scrollingElement) it answers them the same
|
|
10
|
+
* way, so a router without a scrollBehavior behaves as one with it, and one
|
|
11
|
+
* with it lands in the same place:
|
|
8
12
|
*
|
|
9
13
|
* - back and forward return to where the page was left;
|
|
10
14
|
* - an address with a hash lands on that anchor, and stays on it while the
|
|
@@ -22,20 +26,56 @@ import { scrollMarginTop } from "./scroll"
|
|
|
22
26
|
const SETTLE_MS = 1500
|
|
23
27
|
const DIALOG_WAIT_MS = 2000
|
|
24
28
|
const SETTLE_STOP = ["wheel", "touchstart", "keydown", "pointerdown"] as const
|
|
29
|
+
/* Addresses remembered; a long session over /devices/:id must not grow forever. */
|
|
30
|
+
const MAX_POSITIONS = 200
|
|
25
31
|
|
|
26
32
|
const positions = new Map<string, number>()
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
/** Records a position, dropping the address visited longest ago past `limit`. */
|
|
35
|
+
export function rememberPosition(store: Map<string, number>, key: string, top: number, limit = MAX_POSITIONS): void {
|
|
36
|
+
store.delete(key)
|
|
37
|
+
store.set(key, top)
|
|
38
|
+
if (store.size > limit) store.delete(store.keys().next().value as string)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The element id a hash names. A malformed escape ("#%E0%A4%A") is taken as written. */
|
|
42
|
+
/* The document's own scroller: its box moves with the scroll, the window's does not. */
|
|
43
|
+
function isDocument(view: Element): boolean {
|
|
44
|
+
return view === view.ownerDocument.scrollingElement
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function frameTop(view: Element): number {
|
|
48
|
+
return isDocument(view) ? 0 : view.getBoundingClientRect().top
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function hashTargetId(value: string): string {
|
|
52
|
+
const id = value.replace(/^#/, "")
|
|
53
|
+
try {
|
|
54
|
+
return decodeURIComponent(id)
|
|
55
|
+
} catch {
|
|
56
|
+
return id
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function addressKey(scope: string): string {
|
|
61
|
+
return `${scope}${location.pathname}${location.search}`
|
|
30
62
|
}
|
|
31
63
|
|
|
32
64
|
export function useScrollMemory(
|
|
33
65
|
viewport: () => HTMLElement | null | undefined,
|
|
34
66
|
path: () => string | undefined,
|
|
35
67
|
hash: () => string | undefined,
|
|
68
|
+
/** Sets a second region's positions apart from the first's on the same address. */
|
|
69
|
+
scope = "",
|
|
70
|
+
/**
|
|
71
|
+
* What covers the top of the viewport (a sticky header), in px: an anchor
|
|
72
|
+
* lands below it. Not scroll-padding-top on the document, which also makes
|
|
73
|
+
* the browser scroll the page whenever a control in that header takes focus.
|
|
74
|
+
*/
|
|
75
|
+
covered: () => number = () => 0,
|
|
36
76
|
): void {
|
|
37
77
|
if (typeof window === "undefined") return
|
|
38
|
-
let current = addressKey()
|
|
78
|
+
let current = addressKey(scope)
|
|
39
79
|
let shown = path()
|
|
40
80
|
let travelled = false
|
|
41
81
|
let stopHold: (() => void) | undefined
|
|
@@ -48,15 +88,14 @@ export function useScrollMemory(
|
|
|
48
88
|
|
|
49
89
|
function anchor(value: string | undefined): HTMLElement | null {
|
|
50
90
|
if (!value || value === "#") return null
|
|
51
|
-
|
|
52
|
-
return document.getElementById(id)
|
|
91
|
+
return document.getElementById(hashTargetId(value))
|
|
53
92
|
}
|
|
54
93
|
|
|
55
94
|
function reach(value: string | undefined, behavior: ScrollBehavior): boolean {
|
|
56
95
|
const view = viewport()
|
|
57
96
|
const target = anchor(value)
|
|
58
97
|
if (!view || !target || !view.contains(target)) return false
|
|
59
|
-
const top = target.getBoundingClientRect().top - view
|
|
98
|
+
const top = target.getBoundingClientRect().top - frameTop(view) + view.scrollTop - scrollMarginTop(target) - covered()
|
|
60
99
|
view.scrollTo({ top: Math.max(0, top), behavior })
|
|
61
100
|
return true
|
|
62
101
|
}
|
|
@@ -65,10 +104,10 @@ export function useScrollMemory(
|
|
|
65
104
|
function hold(land: () => void) {
|
|
66
105
|
stopHold?.()
|
|
67
106
|
const view = viewport()
|
|
68
|
-
const content = view
|
|
107
|
+
const content = view && (isDocument(view) ? view.ownerDocument.body : view.firstElementChild)
|
|
69
108
|
land()
|
|
70
109
|
if (!content) return
|
|
71
|
-
const watcher = new ResizeObserver(land)
|
|
110
|
+
const watcher = new (nodeWindow(content).ResizeObserver)(land)
|
|
72
111
|
const timer = window.setTimeout(() => stop(), SETTLE_MS)
|
|
73
112
|
const stop = () => {
|
|
74
113
|
watcher.disconnect()
|
|
@@ -84,7 +123,7 @@ export function useScrollMemory(
|
|
|
84
123
|
function arrive() {
|
|
85
124
|
const back = travelled
|
|
86
125
|
travelled = false
|
|
87
|
-
current = addressKey()
|
|
126
|
+
current = addressKey(scope)
|
|
88
127
|
shown = path()
|
|
89
128
|
const view = viewport()
|
|
90
129
|
if (!view) return
|
|
@@ -108,7 +147,9 @@ export function useScrollMemory(
|
|
|
108
147
|
const mine = ++pending
|
|
109
148
|
const tick = () => {
|
|
110
149
|
if (mine !== pending) return
|
|
111
|
-
|
|
150
|
+
// The document the shell scrolls in: a dialog of the page is not one of a frame's.
|
|
151
|
+
const doc = viewport()?.ownerDocument ?? document
|
|
152
|
+
const locked = doc.querySelector('[role="dialog"]') || nodeWindow(doc).getComputedStyle(doc.body).overflow === "hidden"
|
|
112
153
|
if (locked && performance.now() - since < DIALOG_WAIT_MS) requestAnimationFrame(tick)
|
|
113
154
|
else run()
|
|
114
155
|
}
|
|
@@ -119,7 +160,7 @@ export function useScrollMemory(
|
|
|
119
160
|
path,
|
|
120
161
|
() => {
|
|
121
162
|
const view = viewport()
|
|
122
|
-
if (view) positions
|
|
163
|
+
if (view) rememberPosition(positions, current, view.scrollTop)
|
|
123
164
|
void nextTick(() => requestAnimationFrame(arrive))
|
|
124
165
|
},
|
|
125
166
|
{ flush: "pre" },
|
|
@@ -140,6 +181,7 @@ export function useScrollMemory(
|
|
|
140
181
|
|
|
141
182
|
onBeforeUnmount(() => {
|
|
142
183
|
stopHold?.()
|
|
184
|
+
pending += 1
|
|
143
185
|
window.removeEventListener("popstate", onPop)
|
|
144
186
|
})
|
|
145
187
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nodeWindow } from "./realm"
|
|
1
2
|
/*
|
|
2
3
|
* Moving focus by direction rather than by document order: what a D-pad, a
|
|
3
4
|
* stick or a TV remote needs, where "next" means "the thing to the right", not
|
|
@@ -113,7 +114,7 @@ export function focusableIn(root: ParentNode): HTMLElement[] {
|
|
|
113
114
|
if (element.getAttribute("tabindex") === "-1" && !element.matches(ROVING_ITEMS)) return false
|
|
114
115
|
const rect = element.getBoundingClientRect()
|
|
115
116
|
if (rect.width === 0 || rect.height === 0) return false
|
|
116
|
-
const style = getComputedStyle(element)
|
|
117
|
+
const style = nodeWindow(element).getComputedStyle(element)
|
|
117
118
|
return style.visibility !== "hidden" && style.display !== "none"
|
|
118
119
|
})
|
|
119
120
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isElement, isHTMLElement } from "./realm"
|
|
2
|
+
|
|
3
|
+
const TYPED_INPUTS = new Set(["text", "search", "email", "number", "password", "tel", "url"])
|
|
4
|
+
|
|
5
|
+
/** Where text can be typed, for a target that may sit inside one. */
|
|
6
|
+
export const TEXT_FIELD_SELECTOR = "input, textarea, [contenteditable]"
|
|
7
|
+
|
|
8
|
+
export interface TextEntryOptions {
|
|
9
|
+
/** Which `<input>` types count. Defaults to the ones that take typed text. */
|
|
10
|
+
inputType?: (type: string) => boolean
|
|
11
|
+
/** A `<select>` counts too: it keeps letters and arrows for itself. */
|
|
12
|
+
select?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Whether the event lands in something the user is typing into. Checked off the
|
|
17
|
+
* event target rather than `document.activeElement` so it stays right inside a
|
|
18
|
+
* dialog's focus trap, and by tag rather than class so a field of an LpFrame
|
|
19
|
+
* counts too.
|
|
20
|
+
*/
|
|
21
|
+
export function isTextEntry(target: EventTarget | null, options: TextEntryOptions = {}): boolean {
|
|
22
|
+
if (!isHTMLElement(target)) return false
|
|
23
|
+
if (target.isContentEditable || target.tagName === "TEXTAREA") return true
|
|
24
|
+
if (options.select && target.tagName === "SELECT") return true
|
|
25
|
+
if (target.tagName !== "INPUT") return false
|
|
26
|
+
const { type } = target as HTMLInputElement
|
|
27
|
+
return options.inputType ? options.inputType(type) : TYPED_INPUTS.has(type)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Whether the target is, or sits inside, what the selector names. */
|
|
31
|
+
export function inTextField(target: EventTarget | null, selector = TEXT_FIELD_SELECTOR): boolean {
|
|
32
|
+
return isElement(target) && !!target.closest(selector)
|
|
33
|
+
}
|
|
@@ -8,6 +8,12 @@ import { getCurrentScope, onScopeDispose, ref } from "vue"
|
|
|
8
8
|
* fresher one, and a response landing after the control moved on. The token
|
|
9
9
|
* guards both — the abort signal only asks the consumer to stop, it cannot
|
|
10
10
|
* promise they did.
|
|
11
|
+
*
|
|
12
|
+
* What is guarded is `apply` and `fail`, not the task: whatever it does past
|
|
13
|
+
* the point it honours the signal (a cache write after the fetch) still runs,
|
|
14
|
+
* and an error from a superseded run is dropped unseen. It is also one slot:
|
|
15
|
+
* a new `run` replaces the last, and `pending` speaks for the latest only, so
|
|
16
|
+
* independent requests each need their own `useAsyncTask`.
|
|
11
17
|
*/
|
|
12
18
|
export function useAsyncTask<T>() {
|
|
13
19
|
const pending = ref(false)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { computed, onScopeDispose, shallowRef, toValue, type MaybeRefOrGetter, type ShallowRef } from "vue"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* One crosshair across charts of the same period: the x label hovered in one is
|
|
5
|
+
* marked in the others. Matched by label rather than by index, because two
|
|
6
|
+
* charts can hold different points or different windows of the same timeline.
|
|
7
|
+
*
|
|
8
|
+
* Pinned to a global symbol so a barrel and a deep import share the groups.
|
|
9
|
+
*/
|
|
10
|
+
interface Hovered {
|
|
11
|
+
owner: symbol
|
|
12
|
+
label: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const SYNC_KEY = Symbol.for("@leavepulse/ui:chart-sync")
|
|
16
|
+
type GlobalWithSync = typeof globalThis & { [SYNC_KEY]?: Map<string, ShallowRef<Hovered | null>> }
|
|
17
|
+
const groups = ((globalThis as GlobalWithSync)[SYNC_KEY] ??= new Map())
|
|
18
|
+
|
|
19
|
+
function groupOf(name: string): ShallowRef<Hovered | null> {
|
|
20
|
+
let group = groups.get(name)
|
|
21
|
+
if (!group) {
|
|
22
|
+
group = shallowRef<Hovered | null>(null)
|
|
23
|
+
groups.set(name, group)
|
|
24
|
+
}
|
|
25
|
+
return group
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function useChartSync(group: MaybeRefOrGetter<string | undefined>) {
|
|
29
|
+
const owner = Symbol("chart")
|
|
30
|
+
const shared = computed(() => {
|
|
31
|
+
const name = toValue(group)
|
|
32
|
+
return name ? groupOf(name) : null
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
/** The label another chart of the group is hovering. */
|
|
36
|
+
const peerLabel = computed(() => {
|
|
37
|
+
const hovered = shared.value?.value
|
|
38
|
+
return hovered && hovered.owner !== owner ? hovered.label : null
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
/** Tell the group what this chart hovers; null when the pointer left. */
|
|
42
|
+
function publish(label: string | null) {
|
|
43
|
+
const target = shared.value
|
|
44
|
+
if (!target) return
|
|
45
|
+
if (label != null) target.value = { owner, label }
|
|
46
|
+
else if (target.value?.owner === owner) target.value = null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
onScopeDispose(() => publish(null))
|
|
50
|
+
|
|
51
|
+
return { peerLabel, publish }
|
|
52
|
+
}
|