@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
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { SidebarItem } from "./sidebar"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* What LpAppShell decides from numbers: which navigation a width gets, whether
|
|
5
|
+
* the header stays while a page scrolls, how tight the page sits. Kept out of
|
|
6
|
+
* the component so each rule is tested without a browser.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type NavBreakpoint = "sm" | "md" | "lg" | "xl" | "2xl"
|
|
10
|
+
|
|
11
|
+
/** Tailwind's default widths, which the shell's own classes follow. */
|
|
12
|
+
export const NAV_BREAKPOINT_PX: Record<NavBreakpoint, number> = { sm: 640, md: 768, lg: 1024, xl: 1280, "2xl": 1536 }
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The one navigation the shell shows at a width. `rail` the full side rail,
|
|
16
|
+
* `compact` its icons, `bottom` a phone's bottom bar, `drawer` a burger.
|
|
17
|
+
*/
|
|
18
|
+
export type NavMode = "rail" | "compact" | "bottom" | "drawer"
|
|
19
|
+
|
|
20
|
+
export interface AdaptiveNav {
|
|
21
|
+
/** Below this width (and above `mobileBreakpoint`) the rail folds to icons. */
|
|
22
|
+
compactBelow?: NavBreakpoint
|
|
23
|
+
/**
|
|
24
|
+
* Where the rail is gone, a row of the current page's `children` under the
|
|
25
|
+
* header, so a sub-page stays one tap away. The `context-nav` slot redraws it.
|
|
26
|
+
*/
|
|
27
|
+
contextNav?: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Padding around the page: `auto` tightens it on small screens only. */
|
|
31
|
+
export type ShellDensity = "comfortable" | "compact" | "auto"
|
|
32
|
+
|
|
33
|
+
export const DENSITY_CLASS: Record<ShellDensity, string> = {
|
|
34
|
+
comfortable: "px-4 py-6 md:px-6",
|
|
35
|
+
compact: "px-2 py-3 md:px-3",
|
|
36
|
+
auto: "px-2 py-3 sm:px-4 sm:py-4 md:px-6 md:py-6",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function resolveNavMode(
|
|
40
|
+
atLeast: (breakpoint: NavBreakpoint) => boolean,
|
|
41
|
+
options: { mobileBreakpoint: NavBreakpoint; bottomNav: boolean; compactBelow?: NavBreakpoint },
|
|
42
|
+
): NavMode {
|
|
43
|
+
if (!atLeast(options.mobileBreakpoint)) return options.bottomNav ? "bottom" : "drawer"
|
|
44
|
+
return options.compactBelow && !atLeast(options.compactBelow) ? "compact" : "rail"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** What the header height is, in px: where a page counts as being at its top. */
|
|
48
|
+
export const SHELL_HEADER_PX = 64
|
|
49
|
+
/* Below this a scroll is a tremble of the finger, not a direction. */
|
|
50
|
+
const SCROLL_SLOP_PX = 8
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The header leaves on the way down and returns on the way up. Near the end of
|
|
54
|
+
* a page it stays as it is: the viewport changes height with the header, the
|
|
55
|
+
* browser clamps the scroll, and that clamp would read as scrolling back up.
|
|
56
|
+
*/
|
|
57
|
+
export function headerCollapsedAfter(
|
|
58
|
+
collapsed: boolean,
|
|
59
|
+
from: number,
|
|
60
|
+
to: number,
|
|
61
|
+
maxTop: number,
|
|
62
|
+
slop = SCROLL_SLOP_PX,
|
|
63
|
+
): boolean {
|
|
64
|
+
if (to <= SHELL_HEADER_PX) return false
|
|
65
|
+
if (to >= maxTop - SHELL_HEADER_PX) return collapsed
|
|
66
|
+
if (to - from > slop) return true
|
|
67
|
+
if (from - to > slop) return false
|
|
68
|
+
return collapsed
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The sub-pages of the page in view: its own, or its siblings' when it is one of them. */
|
|
72
|
+
export function contextNavItems(items: SidebarItem[], isActive: (item: SidebarItem) => boolean): SidebarItem[] {
|
|
73
|
+
return items.find((item) => item.children?.length && (isActive(item) || item.children.some(isActive)))?.children ?? []
|
|
74
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* pair drifting apart again (one had translatable month buttons, the other
|
|
7
7
|
* English ones).
|
|
8
8
|
*/
|
|
9
|
+
import { CONTROL_SIZE } from "./controlSize"
|
|
9
10
|
|
|
10
11
|
export interface CalendarLabels extends Record<string, string> {
|
|
11
12
|
previous: string
|
|
@@ -24,11 +25,7 @@ export const CALENDAR_NAV =
|
|
|
24
25
|
export const PICKER_TRIGGER =
|
|
25
26
|
"group flex w-full items-center gap-2 rounded-control border bg-surface-soft px-3 text-left text-ink outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring data-[state=open]:border-brand disabled:cursor-not-allowed disabled:opacity-55"
|
|
26
27
|
|
|
27
|
-
export const PICKER_SIZE =
|
|
28
|
-
sm: "h-(--size-control-sm) text-xs",
|
|
29
|
-
md: "h-(--size-control-md) text-sm",
|
|
30
|
-
lg: "h-(--size-control-lg) text-sm",
|
|
31
|
-
} as const
|
|
28
|
+
export const PICKER_SIZE = CONTROL_SIZE
|
|
32
29
|
|
|
33
30
|
export function pickerTone(invalid: boolean): string {
|
|
34
31
|
return invalid ? "border-danger focus-visible:ring-danger-soft" : "border-line focus-visible:border-brand"
|
|
@@ -49,3 +46,13 @@ export const PICKER_PRESETS =
|
|
|
49
46
|
|
|
50
47
|
export const PICKER_PRESET =
|
|
51
48
|
"shrink-0 rounded-control px-2.5 py-1.5 text-left text-sm whitespace-nowrap text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:bg-surface-soft hover:text-ink focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40 aria-pressed:bg-brand-soft aria-pressed:text-ink"
|
|
49
|
+
|
|
50
|
+
/** The props the two pickers share. */
|
|
51
|
+
export interface PickerFieldProps {
|
|
52
|
+
clearable?: boolean
|
|
53
|
+
disabled?: boolean
|
|
54
|
+
invalid?: boolean
|
|
55
|
+
size?: "sm" | "md" | "lg"
|
|
56
|
+
/** Locale of the shown date and the calendar; defaults to reka's ConfigProvider, then the browser. */
|
|
57
|
+
locale?: string
|
|
58
|
+
}
|
package/src/components/chart.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* pixel box, return paths and tick positions.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { clamp } from "../composables/clamp"
|
|
7
8
|
import { compactNumber } from "./format"
|
|
8
9
|
|
|
9
10
|
export interface ChartPoint {
|
|
@@ -65,6 +66,29 @@ export interface ChartMarker {
|
|
|
65
66
|
color?: string
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
/**
|
|
70
|
+
* A stretch of the x axis: a maintenance window, an incident, off hours. `from`
|
|
71
|
+
* and `to` are point indices or x labels. Drawn under the data.
|
|
72
|
+
*/
|
|
73
|
+
export interface ChartBand {
|
|
74
|
+
from: number | string
|
|
75
|
+
to: number | string
|
|
76
|
+
label?: string
|
|
77
|
+
color?: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A note pinned to one reading. `at` is a point index or an x label; `value`
|
|
82
|
+
* places it on the y axis and defaults to the first visible series there.
|
|
83
|
+
*/
|
|
84
|
+
export interface ChartAnnotation {
|
|
85
|
+
at: number | string
|
|
86
|
+
value?: number
|
|
87
|
+
label: string
|
|
88
|
+
color?: string
|
|
89
|
+
axis?: ChartAxis
|
|
90
|
+
}
|
|
91
|
+
|
|
68
92
|
/**
|
|
69
93
|
* Which x labels to print with every `stride`-th one shown. The last point is
|
|
70
94
|
* always labelled (it is usually "now"), and when it lands within half a
|
|
@@ -307,8 +331,8 @@ const smoothSegment = (pts: { x: number; y: number }[], tension: number): string
|
|
|
307
331
|
let c2y = p2.y - ((p3.y - p1.y) / 6) * tension
|
|
308
332
|
const lo = Math.min(p1.y, p2.y)
|
|
309
333
|
const hi = Math.max(p1.y, p2.y)
|
|
310
|
-
c1y =
|
|
311
|
-
c2y =
|
|
334
|
+
c1y = clamp(c1y, lo, hi)
|
|
335
|
+
c2y = clamp(c2y, lo, hi)
|
|
312
336
|
d += `C${c1x},${c1y} ${c2x},${c2y} ${p2.x},${p2.y}`
|
|
313
337
|
}
|
|
314
338
|
return d
|
|
@@ -344,7 +368,7 @@ export const areaPath = (
|
|
|
344
368
|
s: Scale,
|
|
345
369
|
smooth: number,
|
|
346
370
|
): string => {
|
|
347
|
-
const base = yAt(
|
|
371
|
+
const base = yAt(clamp(0, s.min, s.max), s.min, s.max, s.box)
|
|
348
372
|
return runs(values, s.count)
|
|
349
373
|
.map((idx) => {
|
|
350
374
|
const d = segmentPath(idx, values, s, smooth)
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { clamp } from "../composables/clamp"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The part of a long series a chart draws: which points are in the window, and
|
|
5
|
+
* how many of them survive the width of the plot. No DOM and no Vue, so the
|
|
6
|
+
* promises it makes (a peak is never lost) can be tested on the numbers.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** A window into the data: indices of its first and last point, both included. */
|
|
10
|
+
export interface ChartRange {
|
|
11
|
+
start: number
|
|
12
|
+
end: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** How a series longer than the plot is thinned. */
|
|
16
|
+
export type ChartDownsample = "minmax" | "lttb"
|
|
17
|
+
|
|
18
|
+
/** What the chart draws: per series values, each slot traced back to its source. */
|
|
19
|
+
export interface ChartView {
|
|
20
|
+
values: (number | null)[][]
|
|
21
|
+
start: number
|
|
22
|
+
end: number
|
|
23
|
+
/** Source index of every slot; null while the slots are contiguous from `start`. */
|
|
24
|
+
indices: number[] | null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type Values = (number | null)[]
|
|
28
|
+
|
|
29
|
+
/* Below this a window has no line to read: two points are one segment. */
|
|
30
|
+
export const MIN_RANGE_POINTS = 2
|
|
31
|
+
/* Two slots a bucket: its lowest and its highest reading, in the order they came. */
|
|
32
|
+
const SLOTS_PER_BUCKET = 2
|
|
33
|
+
/* LTTB always keeps the first and last point, so anything less is not a choice. */
|
|
34
|
+
const LTTB_MIN_TARGET = 3
|
|
35
|
+
/* Share of the window one step of a keyboard or wheel zoom takes away. */
|
|
36
|
+
export const ZOOM_STEP = 0.8
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A range fit to the data: in bounds, ordered, at least `MIN_RANGE_POINTS` wide.
|
|
40
|
+
* Null when there is nothing to narrow, so the whole series stays the plain case.
|
|
41
|
+
*/
|
|
42
|
+
export function clampRange(range: ChartRange | null | undefined, count: number): ChartRange | null {
|
|
43
|
+
if (!range || count <= MIN_RANGE_POINTS || !Number.isFinite(range.start) || !Number.isFinite(range.end)) return null
|
|
44
|
+
const last = count - 1
|
|
45
|
+
let start = Math.max(0, Math.min(last, Math.round(Math.min(range.start, range.end))))
|
|
46
|
+
let end = Math.max(0, Math.min(last, Math.round(Math.max(range.start, range.end))))
|
|
47
|
+
if (end - start + 1 < MIN_RANGE_POINTS) {
|
|
48
|
+
end = Math.min(last, start + MIN_RANGE_POINTS - 1)
|
|
49
|
+
start = Math.max(0, end - MIN_RANGE_POINTS + 1)
|
|
50
|
+
}
|
|
51
|
+
return start === 0 && end === last ? null : { start, end }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The range after a zoom by `factor` (below 1 is in), keeping `anchor` (0–1 across it) in place. */
|
|
55
|
+
export function zoomRange(range: ChartRange | null, count: number, anchor: number, factor: number): ChartRange | null {
|
|
56
|
+
const from = range ?? { start: 0, end: count - 1 }
|
|
57
|
+
const span = from.end - from.start
|
|
58
|
+
const next = Math.max(MIN_RANGE_POINTS - 1, span * factor)
|
|
59
|
+
const at = from.start + span * clamp(anchor, 0, 1)
|
|
60
|
+
const start = at - (at - from.start) * (next / (span || 1))
|
|
61
|
+
return clampRange({ start, end: start + next }, count)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The range moved by `delta` points, keeping its width at the ends of the data. */
|
|
65
|
+
export function panRange(range: ChartRange, count: number, delta: number): ChartRange | null {
|
|
66
|
+
const span = range.end - range.start
|
|
67
|
+
const start = Math.max(0, Math.min(count - 1 - span, range.start + delta))
|
|
68
|
+
return clampRange({ start, end: start + span }, count)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Keeps each bucket's lowest and highest reading, per series. Slots stay shared
|
|
73
|
+
* across series, so a bucket is two x positions and each series fills them with
|
|
74
|
+
* its own extremes. A monitoring chart must not lose a one-point spike: thinning
|
|
75
|
+
* by average or by every n-th point does exactly that.
|
|
76
|
+
*/
|
|
77
|
+
export function minMaxBuckets(series: Values[], slots: number): { values: Values[]; indices: number[] } {
|
|
78
|
+
const count = series.reduce((n, values) => Math.max(n, values.length), 0)
|
|
79
|
+
const buckets = Math.floor(slots / SLOTS_PER_BUCKET)
|
|
80
|
+
if (count === 0 || buckets < 1) return { values: series.map(() => []), indices: [] }
|
|
81
|
+
const indices: number[] = []
|
|
82
|
+
const values: Values[] = series.map(() => [])
|
|
83
|
+
for (let bucket = 0; bucket < buckets; bucket++) {
|
|
84
|
+
const from = Math.floor((bucket * count) / buckets)
|
|
85
|
+
const to = Math.floor(((bucket + 1) * count) / buckets)
|
|
86
|
+
if (to <= from) continue
|
|
87
|
+
indices.push(from, Math.max(from, to - 1))
|
|
88
|
+
series.forEach((data, s) => {
|
|
89
|
+
let low = -1
|
|
90
|
+
let high = -1
|
|
91
|
+
for (let i = from; i < to && i < data.length; i++) {
|
|
92
|
+
const v = data[i]
|
|
93
|
+
if (v == null) continue
|
|
94
|
+
if (low < 0 || v < (data[low] as number)) low = i
|
|
95
|
+
if (high < 0 || v > (data[high] as number)) high = i
|
|
96
|
+
}
|
|
97
|
+
if (low < 0) values[s].push(null, null)
|
|
98
|
+
else values[s].push(data[Math.min(low, high)], data[Math.max(low, high)])
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
return { values, indices }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Largest-Triangle-Three-Buckets: the indices of `target` points that keep a
|
|
106
|
+
* line's shape, one a bucket, always with its first and last. It reads better
|
|
107
|
+
* than min/max on a smooth curve and can drop a short spike, so it is the
|
|
108
|
+
* option rather than the default.
|
|
109
|
+
*/
|
|
110
|
+
export function lttbIndices(values: Values, target: number): number[] {
|
|
111
|
+
const present = values.flatMap((v, i) => (v == null ? [] : [i]))
|
|
112
|
+
if (target >= present.length || target < LTTB_MIN_TARGET) return present
|
|
113
|
+
const y = (at: number) => values[present[at]] as number
|
|
114
|
+
const x = (at: number) => present[at]
|
|
115
|
+
const step = (present.length - 2) / (target - 2)
|
|
116
|
+
const kept = [0]
|
|
117
|
+
let previous = 0
|
|
118
|
+
for (let bucket = 0; bucket < target - 2; bucket++) {
|
|
119
|
+
const from = Math.floor(bucket * step) + 1
|
|
120
|
+
const to = Math.floor((bucket + 1) * step) + 1
|
|
121
|
+
const nextEnd = Math.min(present.length, Math.floor((bucket + 2) * step) + 1)
|
|
122
|
+
let meanX = 0
|
|
123
|
+
let meanY = 0
|
|
124
|
+
for (let i = to; i < nextEnd; i++) {
|
|
125
|
+
meanX += x(i)
|
|
126
|
+
meanY += y(i)
|
|
127
|
+
}
|
|
128
|
+
const size = Math.max(1, nextEnd - to)
|
|
129
|
+
meanX /= size
|
|
130
|
+
meanY /= size
|
|
131
|
+
let best = from
|
|
132
|
+
let largest = -1
|
|
133
|
+
for (let i = from; i < to; i++) {
|
|
134
|
+
const area = Math.abs((x(previous) - meanX) * (y(i) - y(previous)) - (x(previous) - x(i)) * (meanY - y(previous)))
|
|
135
|
+
if (area > largest) {
|
|
136
|
+
largest = area
|
|
137
|
+
best = i
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
kept.push(best)
|
|
141
|
+
previous = best
|
|
142
|
+
}
|
|
143
|
+
kept.push(present.length - 1)
|
|
144
|
+
return kept.map((at) => present[at])
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const contiguous = (series: Values[], range: ChartRange): Values[] =>
|
|
148
|
+
series.map((values) => values.slice(range.start, range.end + 1))
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The series as drawn. Without a range or a thinning it hands the input back, so
|
|
152
|
+
* a chart using neither does no work and draws what it always drew.
|
|
153
|
+
*
|
|
154
|
+
* LTTB picks by the first series that has data and the others follow those
|
|
155
|
+
* points, because slots are shared across series.
|
|
156
|
+
*/
|
|
157
|
+
export function chartView(
|
|
158
|
+
series: Values[],
|
|
159
|
+
count: number,
|
|
160
|
+
range: ChartRange | null,
|
|
161
|
+
mode: ChartDownsample | false | undefined,
|
|
162
|
+
slots: number,
|
|
163
|
+
): ChartView {
|
|
164
|
+
const start = range?.start ?? 0
|
|
165
|
+
const end = range?.end ?? Math.max(0, count - 1)
|
|
166
|
+
const windowed = range ? contiguous(series, range) : series
|
|
167
|
+
const shown = end - start + 1
|
|
168
|
+
if (!mode || slots <= 0 || shown <= slots) return { values: windowed, start, end, indices: null }
|
|
169
|
+
if (mode === "minmax") {
|
|
170
|
+
const { values, indices } = minMaxBuckets(windowed, slots)
|
|
171
|
+
return { values, start, end, indices: indices.map((i) => i + start) }
|
|
172
|
+
}
|
|
173
|
+
const lead = windowed.find((values) => values.some((v) => v != null)) ?? []
|
|
174
|
+
const picked = lttbIndices(lead, slots)
|
|
175
|
+
return {
|
|
176
|
+
values: windowed.map((values) => picked.map((i) => values[i] ?? null)),
|
|
177
|
+
start,
|
|
178
|
+
end,
|
|
179
|
+
indices: picked.map((i) => i + start),
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** The slot at or before a source index, or -1 when the window does not hold it. */
|
|
184
|
+
export function slotOf(view: ChartView, source: number): number {
|
|
185
|
+
if (source < view.start || source > view.end) return -1
|
|
186
|
+
if (!view.indices) return source - view.start
|
|
187
|
+
let low = 0
|
|
188
|
+
let high = view.indices.length - 1
|
|
189
|
+
while (low < high) {
|
|
190
|
+
const middle = Math.ceil((low + high) / 2)
|
|
191
|
+
if (view.indices[middle] <= source) low = middle
|
|
192
|
+
else high = middle - 1
|
|
193
|
+
}
|
|
194
|
+
return high < 0 ? -1 : low
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** The source index behind a slot. */
|
|
198
|
+
export const sourceOf = (view: ChartView, slot: number): number => (view.indices ? (view.indices[slot] ?? -1) : slot + view.start)
|
package/src/components/color.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { clamp } from "../composables/clamp"
|
|
2
|
+
|
|
1
3
|
/*
|
|
2
4
|
* Colour conversion for LpColorPicker — kept out of the component so the maths
|
|
3
5
|
* is testable on its own and reusable by anything that has to meet a colour
|
|
@@ -33,7 +35,6 @@ export interface Rgb {
|
|
|
33
35
|
/** The string shapes a caller can hand the picker, and ask back out of it. */
|
|
34
36
|
export type ColorFormat = "hex" | "rgb" | "hsl"
|
|
35
37
|
|
|
36
|
-
const clamp = (n: number, min: number, max: number) => Math.min(max, Math.max(min, n))
|
|
37
38
|
|
|
38
39
|
/** Wraps rather than clamps: hue is a circle, and 370° is 10°, not 360°. */
|
|
39
40
|
export function normalizeHue(h: number): number {
|
|
@@ -165,6 +165,52 @@ export interface RecentSource {
|
|
|
165
165
|
children?: unknown
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
/*
|
|
169
|
+
* Where a command's `href` may lead. Commands can come from a search or from
|
|
170
|
+
* stored history, so a `javascript:` link there would run in the app's origin.
|
|
171
|
+
* Mail and phone links are as harmless as a web one and common in a help menu.
|
|
172
|
+
* The base only lets a relative link parse: nothing here reads the window, so
|
|
173
|
+
* this holds on the server.
|
|
174
|
+
*/
|
|
175
|
+
const SAFE_LINK_SCHEMES = new Set(["http:", "https:", "mailto:", "tel:"])
|
|
176
|
+
const RELATIVE_BASE = "http://relative.invalid/"
|
|
177
|
+
|
|
178
|
+
export function isSafeHref(href: string): boolean {
|
|
179
|
+
try {
|
|
180
|
+
return SAFE_LINK_SCHEMES.has(new URL(href, RELATIVE_BASE).protocol)
|
|
181
|
+
} catch {
|
|
182
|
+
return false
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** The link as it would be pasted elsewhere: absolute, or as given if it cannot be read. */
|
|
187
|
+
export function absoluteHref(href: string, base: string): string {
|
|
188
|
+
try {
|
|
189
|
+
return new URL(href, base).href
|
|
190
|
+
} catch {
|
|
191
|
+
return href
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** The row that unfolds a group; the NUL keeps it out of any id an app would write. */
|
|
196
|
+
export function moreRowId(group: string): string {
|
|
197
|
+
return `\u0000lp-cmd-more\u0000${group}`
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const IMAGE_SHAPES: unknown[] = ["circle", "soft", "square"]
|
|
201
|
+
const isOptionalString = (value: unknown) => value === undefined || typeof value === "string"
|
|
202
|
+
|
|
203
|
+
function isStoredImage(value: unknown): boolean {
|
|
204
|
+
if (value === undefined || typeof value === "string") return true
|
|
205
|
+
if (!value || typeof value !== "object") return false
|
|
206
|
+
const { src, alt, fallback, icon, shape } = value as Record<string, unknown>
|
|
207
|
+
return (src === null || isOptionalString(src))
|
|
208
|
+
&& isOptionalString(alt)
|
|
209
|
+
&& isOptionalString(fallback)
|
|
210
|
+
&& isOptionalString(icon)
|
|
211
|
+
&& (shape === undefined || IMAGE_SHAPES.includes(shape))
|
|
212
|
+
}
|
|
213
|
+
|
|
168
214
|
function isEntry(value: unknown): value is RecentEntry {
|
|
169
215
|
if (!value || typeof value !== "object") return false
|
|
170
216
|
const v = value as Record<string, unknown>
|
|
@@ -173,6 +219,10 @@ function isEntry(value: unknown): value is RecentEntry {
|
|
|
173
219
|
&& typeof v.label === "string"
|
|
174
220
|
&& Array.isArray(v.path)
|
|
175
221
|
&& v.path.every((step) => typeof step === "string")
|
|
222
|
+
&& isOptionalString(v.description)
|
|
223
|
+
&& isOptionalString(v.icon)
|
|
224
|
+
&& isStoredImage(v.image)
|
|
225
|
+
&& (v.href === undefined || (typeof v.href === "string" && isSafeHref(v.href)))
|
|
176
226
|
)
|
|
177
227
|
}
|
|
178
228
|
|
|
@@ -182,17 +232,28 @@ export function readRecent(stored: unknown): RecentItem[] {
|
|
|
182
232
|
return stored.filter((v): v is RecentItem => typeof v === "string" || isEntry(v))
|
|
183
233
|
}
|
|
184
234
|
|
|
185
|
-
|
|
186
|
-
|
|
235
|
+
/**
|
|
236
|
+
* What makes a command itself: the same id may live in two modes
|
|
237
|
+
* (`services/restart`, `deploy/restart`). A root command's key is its bare id,
|
|
238
|
+
* so a stored id and a stored entry at the root count as one.
|
|
239
|
+
*/
|
|
240
|
+
export function commandKey(id: string, path: readonly string[] = []): string {
|
|
241
|
+
return [...path, id].join("\u0000")
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function recentKey(item: RecentItem): string {
|
|
245
|
+
return typeof item === "string" ? item : commandKey(item.id, item.path)
|
|
187
246
|
}
|
|
188
247
|
|
|
189
248
|
/**
|
|
190
249
|
* The history item for a pick. A root pick stays a bare id, the format older
|
|
191
250
|
* versions wrote and still read; anything deeper becomes an entry with only
|
|
192
|
-
* the fields that survive JSON.
|
|
251
|
+
* the fields that survive JSON. So does a `fetched` pick at the root: its id
|
|
252
|
+
* stands for nothing among the root commands, and only an entry reaches
|
|
253
|
+
* `resolveRecent` with what is needed to find it again.
|
|
193
254
|
*/
|
|
194
|
-
export function toRecentItem(command: RecentSource, path: readonly string[]): RecentItem {
|
|
195
|
-
if (!path.length) return command.id
|
|
255
|
+
export function toRecentItem(command: RecentSource, path: readonly string[], fetched = false): RecentItem {
|
|
256
|
+
if (!path.length && !fetched) return command.id
|
|
196
257
|
const entry: RecentEntry = { id: command.id, label: command.label, path: [...path] }
|
|
197
258
|
if (command.description) entry.description = command.description
|
|
198
259
|
if (command.icon) entry.icon = command.icon
|
|
@@ -71,6 +71,9 @@ export const optionStagger = (index: number): { animationDelay: string } => ({
|
|
|
71
71
|
* dip on press. Put on the <LpIcon>, with `group` on its button. Shared by
|
|
72
72
|
* Modal, Drawer, Toaster and the Autocomplete clear button.
|
|
73
73
|
*/
|
|
74
|
+
/* Where a combobox list sits under its field. */
|
|
75
|
+
export const COMBOBOX_PANEL = { collisionPadding: 8, position: "popper", sideOffset: 6 } as const
|
|
76
|
+
|
|
74
77
|
export const CLOSE_ICON =
|
|
75
78
|
"transition-transform duration-medium ease-[var(--ease-emphasized)] " +
|
|
76
79
|
"group-hover:rotate-90 group-active:scale-90 motion-reduce:group-hover:rotate-0"
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Data export shared by the table and the chart: CSV text, a file download,
|
|
3
3
|
* and an SVG rasterised with its theme resolved.
|
|
4
4
|
*/
|
|
5
|
+
import { isElement, nodeWindow } from "../composables/useView"
|
|
5
6
|
|
|
6
7
|
export interface CsvColumn<Row> {
|
|
7
8
|
key: string
|
|
@@ -64,19 +65,30 @@ export function toCsv<Row>(rows: readonly Row[], columns: readonly CsvColumn<Row
|
|
|
64
65
|
* Hand the viewer a file to save. Does nothing outside a browser, so a call
|
|
65
66
|
* that runs during SSR is harmless.
|
|
66
67
|
*/
|
|
67
|
-
export function downloadFile(
|
|
68
|
-
|
|
68
|
+
export function downloadFile(
|
|
69
|
+
name: string,
|
|
70
|
+
data: Blob | string,
|
|
71
|
+
type = "text/plain;charset=utf-8",
|
|
72
|
+
doc: Document | undefined = typeof document === "undefined" ? undefined : document,
|
|
73
|
+
): void {
|
|
74
|
+
if (!doc || typeof URL === "undefined") return
|
|
69
75
|
const blob = typeof data === "string" ? new Blob([data], { type }) : data
|
|
70
76
|
const url = URL.createObjectURL(blob)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
clickLink(doc, { href: url, download: name, rel: "noopener" })
|
|
78
|
+
setTimeout(() => URL.revokeObjectURL(url), 0)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Follow a link the way a click on it would, from the document it should open
|
|
83
|
+
* in: an LpFrame's, inside one.
|
|
84
|
+
*/
|
|
85
|
+
export function clickLink(doc: Document, attributes: Partial<Pick<HTMLAnchorElement, "href" | "download" | "target" | "rel">>): void {
|
|
86
|
+
const a = doc.createElement("a")
|
|
87
|
+
Object.assign(a, attributes)
|
|
75
88
|
a.style.display = "none"
|
|
76
|
-
|
|
89
|
+
doc.body.append(a)
|
|
77
90
|
a.click()
|
|
78
91
|
a.remove()
|
|
79
|
-
setTimeout(() => URL.revokeObjectURL(url), 0)
|
|
80
92
|
}
|
|
81
93
|
|
|
82
94
|
const PAINT_PROPS = [
|
|
@@ -111,13 +123,14 @@ const PAINT_PROPS = [
|
|
|
111
123
|
* are frozen at their end state.
|
|
112
124
|
*/
|
|
113
125
|
export function inlineSvg(svg: SVGSVGElement): SVGSVGElement {
|
|
126
|
+
const view = nodeWindow(svg)
|
|
114
127
|
const clone = svg.cloneNode(true) as SVGSVGElement
|
|
115
128
|
const source = [svg, ...svg.querySelectorAll("*")]
|
|
116
129
|
const target = [clone, ...clone.querySelectorAll("*")]
|
|
117
130
|
source.forEach((el, i) => {
|
|
118
131
|
const copy = target[i]
|
|
119
|
-
if (!(copy
|
|
120
|
-
const computed = getComputedStyle(el)
|
|
132
|
+
if (!isElement(copy)) return
|
|
133
|
+
const computed = view.getComputedStyle(el)
|
|
121
134
|
const style = PAINT_PROPS.map((p) => `${p}:${computed.getPropertyValue(p)}`)
|
|
122
135
|
.filter((d) => !d.endsWith(":"))
|
|
123
136
|
.join(";")
|
|
@@ -126,7 +139,7 @@ export function inlineSvg(svg: SVGSVGElement): SVGSVGElement {
|
|
|
126
139
|
})
|
|
127
140
|
// Comment nodes (Vue leaves them in development builds) may hold "--", which
|
|
128
141
|
// is invalid inside an XML comment and makes the whole image fail to load.
|
|
129
|
-
const comments = document.createTreeWalker(clone, NodeFilter.SHOW_COMMENT)
|
|
142
|
+
const comments = view.document.createTreeWalker(clone, view.NodeFilter.SHOW_COMMENT)
|
|
130
143
|
const dead: Node[] = []
|
|
131
144
|
while (comments.nextNode()) dead.push(comments.currentNode)
|
|
132
145
|
for (const node of dead) node.parentNode?.removeChild(node)
|
|
@@ -157,13 +170,14 @@ export interface RasterOptions {
|
|
|
157
170
|
|
|
158
171
|
/** Rasterise an SVG element to a PNG blob with its theme resolved. */
|
|
159
172
|
export async function svgToPng(svg: SVGSVGElement, options: RasterOptions = {}): Promise<Blob> {
|
|
173
|
+
const view = nodeWindow(svg)
|
|
160
174
|
const copy = inlineSvg(svg)
|
|
161
175
|
const width = Number(copy.getAttribute("width"))
|
|
162
176
|
const height = Number(copy.getAttribute("height"))
|
|
163
177
|
const markup = new XMLSerializer().serializeToString(copy)
|
|
164
178
|
const url = URL.createObjectURL(new Blob([markup], { type: "image/svg+xml;charset=utf-8" }))
|
|
165
179
|
try {
|
|
166
|
-
const img = new Image()
|
|
180
|
+
const img = new view.Image()
|
|
167
181
|
img.decoding = "async"
|
|
168
182
|
const loaded = new Promise<void>((resolve, reject) => {
|
|
169
183
|
img.onload = () => resolve()
|
|
@@ -172,11 +186,11 @@ export async function svgToPng(svg: SVGSVGElement, options: RasterOptions = {}):
|
|
|
172
186
|
img.src = url
|
|
173
187
|
await loaded
|
|
174
188
|
|
|
175
|
-
const scale = options.scale ?? Math.max(2,
|
|
189
|
+
const scale = options.scale ?? Math.max(2, view.devicePixelRatio || 1)
|
|
176
190
|
const pad = options.padding ?? 0
|
|
177
|
-
const measure = document.createElement("canvas").getContext("2d")
|
|
191
|
+
const measure = view.document.createElement("canvas").getContext("2d")
|
|
178
192
|
const headerHeight = measure && options.header ? options.header(measure, width) : 0
|
|
179
|
-
const canvas = document.createElement("canvas")
|
|
193
|
+
const canvas = view.document.createElement("canvas")
|
|
180
194
|
canvas.width = Math.ceil((width + pad * 2) * scale)
|
|
181
195
|
canvas.height = Math.ceil((height + headerHeight + pad * 2) * scale)
|
|
182
196
|
const ctx = canvas.getContext("2d")
|
|
@@ -200,7 +214,7 @@ export async function svgToPng(svg: SVGSVGElement, options: RasterOptions = {}):
|
|
|
200
214
|
/** The first non-transparent background at or above an element. */
|
|
201
215
|
export function backgroundOf(el: Element | null): string | undefined {
|
|
202
216
|
for (let node = el; node; node = node.parentElement) {
|
|
203
|
-
const bg = getComputedStyle(node).backgroundColor
|
|
217
|
+
const bg = nodeWindow(node).getComputedStyle(node).backgroundColor
|
|
204
218
|
if (bg && bg !== "transparent" && !/rgba\([^)]*,\s*0\)$/.test(bg)) return bg
|
|
205
219
|
}
|
|
206
220
|
return undefined
|
|
@@ -212,11 +226,12 @@ export function backgroundOf(el: Element | null): string | undefined {
|
|
|
212
226
|
*/
|
|
213
227
|
export function resolveCssColor(value: string, within: Element): string {
|
|
214
228
|
if (!value.includes("var(") && !value.toLowerCase().includes("currentcolor")) return value
|
|
215
|
-
const
|
|
229
|
+
const view = nodeWindow(within)
|
|
230
|
+
const probe = view.document.createElement("span")
|
|
216
231
|
probe.style.color = value
|
|
217
232
|
probe.style.display = "none"
|
|
218
233
|
within.append(probe)
|
|
219
|
-
const resolved = getComputedStyle(probe).color
|
|
234
|
+
const resolved = view.getComputedStyle(probe).color
|
|
220
235
|
probe.remove()
|
|
221
236
|
return resolved || value
|
|
222
237
|
}
|