@leavepulse/ui 0.71.1 → 0.72.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/dist/assets/{LpCommandPalette-DgPZX0us.css → LpCommandPalette-Rtbg669l.css} +19 -19
- package/dist/assets/{LpNumberFlow-rZkwQwlU.css → LpNumberFlow-Chp9pEDe.css} +8 -0
- package/dist/assets/LpTabs-CDchzoRL.css +18 -0
- package/dist/chunks/{ContextMenuItems-eS7qx9gD.js → ContextMenuItems-BXumicbh.js} +1 -1
- package/dist/chunks/{DropzoneLinkField-DkFhdfEJ.js → DropzoneLinkField-D2Rnkcys.js} +2 -2
- package/dist/chunks/{FieldWrapper-YzbvvC3M.js → FieldWrapper-DnA1rxZ8.js} +1 -1
- package/dist/chunks/{LayoutCanvas-Ba4A9QC5.js → LayoutCanvas-CNOAUk7D.js} +1 -1
- package/dist/chunks/{LayoutNode-CTTTLMDM.js → LayoutNode-DF5BMN0J.js} +1 -1
- package/dist/chunks/{LpActionBar-B7anGlkI.js → LpActionBar-DfiBL1dE.js} +3 -3
- package/dist/chunks/{LpAddressInput-CD0bzrt-.js → LpAddressInput-B1bB3F6M.js} +7 -7
- package/dist/chunks/{LpAlert-BHi7ARqu.js → LpAlert-CpRF9eYr.js} +3 -3
- package/dist/chunks/{LpAppShell-1RmBp6xQ.js → LpAppShell-D8Q0Jv9d.js} +68 -18
- package/dist/chunks/{LpAutocomplete-DDqe2M5g.js → LpAutocomplete-DOu7FN5P.js} +2 -2
- package/dist/chunks/{LpAvatar-DqfKCIdr.js → LpAvatar-Cg6Az3wz.js} +1 -1
- package/dist/chunks/{LpBadge-C8fKEmTF.js → LpBadge-vtCpsDmv.js} +3 -3
- package/dist/chunks/LpBottomNav-VXJf0cKc.js +89 -0
- package/dist/chunks/{LpBreadcrumbs-DhcoStFa.js → LpBreadcrumbs-BdzjQcql.js} +3 -3
- package/dist/chunks/{LpButton-BQDAFXBd.js → LpButton-Dk7eT4ro.js} +1 -1
- package/dist/chunks/{LpCalendar-NmrA47J9.js → LpCalendar-CuoWks-0.js} +1 -1
- package/dist/chunks/{LpCard-CqKLnEhl.js → LpCard-Ce3rw8zm.js} +1 -1
- package/dist/chunks/{LpCheckbox-BoOuLnGB.js → LpCheckbox-DLX4amba.js} +2 -2
- package/dist/chunks/{LpCodeBlock-Czu-fq1M.js → LpCodeBlock-j0W5bpmF.js} +2 -2
- package/dist/chunks/{LpCodeDisplay-BK2FHbtM.js → LpCodeDisplay-COdiv4xu.js} +6 -6
- package/dist/chunks/{LpColorPicker-Ee1DZiRd.js → LpColorPicker-1y1QJHZR.js} +56 -28
- package/dist/chunks/{LpCommandScope-ubtWDMa0.js → LpCommandScope-BlKcgBnK.js} +1 -1
- package/dist/chunks/{LpConfirmDialog-i15pulMT.js → LpConfirmDialog-w1ni5nxh.js} +3 -3
- package/dist/chunks/{LpConfirmHost-D0oGavtd.js → LpConfirmHost-Br_-Gn-6.js} +1 -1
- package/dist/chunks/{LpContextMenu-DXpFJjlO.js → LpContextMenu-rx2tCjwk.js} +2 -2
- package/dist/chunks/{LpCopyButton-Dmq7DmFw.js → LpCopyButton-DaD1KCn6.js} +2 -2
- package/dist/chunks/{LpCountdown-DTdMF8DL.js → LpCountdown-Cgy0mVEq.js} +3 -3
- package/dist/chunks/{LpDataList-CFPtSsBB.js → LpDataList-CkcGFJgv.js} +3 -3
- package/dist/chunks/{LpDatePicker-cC9K-Pk8.js → LpDatePicker-CA0QTfBC.js} +4 -4
- package/dist/chunks/{LpDateRangePicker-BVQKPbvo.js → LpDateRangePicker-u3J51xXk.js} +2 -2
- package/dist/chunks/{LpDisclosure-hRnX3FWL.js → LpDisclosure-Cpa7Hc5T.js} +1 -1
- package/dist/chunks/{LpDrawer-u4rxnuR-.js → LpDrawer-RRamxbLV.js} +1 -1
- package/dist/chunks/{LpDropdownMenu-BgZ0Z_Im.js → LpDropdownMenu-Dl7Z4ZhS.js} +2 -2
- package/dist/chunks/{LpEditable-CjxuOVh_.js → LpEditable-D3PvD6Zm.js} +5 -5
- package/dist/chunks/{LpEmptyState-CKFTtQgJ.js → LpEmptyState-EQHiRYhG.js} +7 -3
- package/dist/chunks/{LpFieldNotice-CPI8EwtU.js → LpFieldNotice-CzQ2_5rQ.js} +1 -1
- package/dist/chunks/{LpFileTree-BH0gNlSJ.js → LpFileTree-D7KTaJiM.js} +2 -2
- package/dist/chunks/{LpFileTreeNode-gPVB1oXM.js → LpFileTreeNode-14GVqQTd.js} +5 -5
- package/dist/chunks/{LpFormField-gRASfyj2.js → LpFormField-MSQhRCTz.js} +2 -2
- package/dist/chunks/{LpGauge-BdjPGnqw.js → LpGauge-BO1PYYqD.js} +2 -2
- package/dist/chunks/{LpHintLayer-C2asBIfs.js → LpHintLayer-CmYm37z7.js} +2 -2
- package/dist/chunks/{LpHints-CNZQ76JV.js → LpHints-iLIRUkCj.js} +3 -3
- package/dist/chunks/{LpIcon-DkP-9mzK.js → LpIcon--5WSb3jJ.js} +1 -1
- package/dist/chunks/{LpInfraNode-yTh4-5qa.js → LpInfraNode-BT0VQrOa.js} +2 -2
- package/dist/chunks/{LpInput-2H-5ILOB.js → LpInput-1eNvtzXs.js} +2 -2
- package/dist/chunks/{LpLightbox-lAt5uIfx.js → LpLightbox-DWw6OSA4.js} +3 -3
- package/dist/chunks/{LpLoader-DjJEX8hL.js → LpLoader-B82_CcV0.js} +1 -1
- package/dist/chunks/{LpLogViewer-x78YfhJf.js → LpLogViewer-pzanZASC.js} +6 -6
- package/dist/chunks/{LpMapPicker-Dp5RSCi9.js → LpMapPicker-DNIUvF6V.js} +2 -2
- package/dist/chunks/{LpMediaGallery-D92oJfY_.js → LpMediaGallery-Dx8xzg5N.js} +3 -3
- package/dist/chunks/{LpModal-CpH0bNo7.js → LpModal-BnjZ2iem.js} +1 -1
- package/dist/chunks/{LpModalNav-BH4ANQGJ.js → LpModalNav-Cu04W1pV.js} +4 -4
- package/dist/chunks/{LpNotificationBell-BdUQqZhs.js → LpNotificationBell-PSQqFqEv.js} +8 -8
- package/dist/chunks/{LpNumberField-DZY3Z670.js → LpNumberField-e0IBkWU1.js} +1 -1
- package/dist/chunks/{LpNumberFlow-5khYxsfX.js → LpNumberFlow-CcxEGUy0.js} +10 -7
- package/dist/chunks/{LpPagination-DbeACavM.js → LpPagination-BIIiYHXR.js} +1 -1
- package/dist/chunks/{LpPaletteTheme-CqWNRwHE.js → LpPaletteTheme-D05ywM1W.js} +8 -6
- package/dist/chunks/{LpPasswordInput-BzAZsf5V.js → LpPasswordInput-vdcw-koG.js} +2 -2
- package/dist/chunks/{LpPhoneInput-BMpd-0QR.js → LpPhoneInput-CETEp5W7.js} +2 -2
- package/dist/chunks/{LpPie-DdFSTbGE.js → LpPie-BlWDxCtG.js} +6 -6
- package/dist/chunks/{LpPipeline-zjEM1Lwc.js → LpPipeline-BXjoxkq_.js} +4 -4
- package/dist/chunks/{LpProgress-BX33EKkN.js → LpProgress-rOlyafqp.js} +1 -1
- package/dist/chunks/{LpQuota-DqNv1yjs.js → LpQuota-B12yijdB.js} +3 -3
- package/dist/chunks/{LpRadioGroup-CsXeV9Ct.js → LpRadioGroup-ggaPL92T.js} +1 -1
- package/dist/chunks/{LpRangeCalendar-l-0nSFmY.js → LpRangeCalendar-CYYUYqJ7.js} +1 -1
- package/dist/chunks/{LpRelativeTime-UM8EkfrN.js → LpRelativeTime-DwgCb5-8.js} +1 -1
- package/dist/chunks/{LpRepeater-OvzByE5L.js → LpRepeater-fliVBcN7.js} +3 -3
- package/dist/chunks/LpRollingText-B54Pje7P.js +86 -0
- package/dist/chunks/{LpScramble-TDO5Kdou.js → LpScramble-Cw-af0Yh.js} +1 -1
- package/dist/chunks/{LpSecretField-DwQiPOko.js → LpSecretField-D_IMxEGW.js} +7 -7
- package/dist/chunks/{LpSection-DcQsieHG.js → LpSection-DFK_uoJE.js} +1 -1
- package/dist/chunks/{LpSegmented-D2VST0o3.js → LpSegmented-CeACsMMA.js} +4 -2
- package/dist/chunks/{LpSelect-DYX2-u5A.js → LpSelect-Cxq4yBGd.js} +7 -7
- package/dist/chunks/LpShift-BAp8lGoa.js +160 -0
- package/dist/chunks/LpSidebar-BftYsk5d.js +494 -0
- package/dist/chunks/LpSidebarNav-BzNWMZfc.js +357 -0
- package/dist/chunks/LpSidebarRow-BIReMT1f.js +302 -0
- package/dist/chunks/{LpStat-DIsQfbeD.js → LpStat-BFCM6fUn.js} +9 -7
- package/dist/chunks/{LpStatus-BQU1TUsv.js → LpStatus-BWTLVPVh.js} +3 -3
- package/dist/chunks/{LpStepMark-CXa7DJy_.js → LpStepMark-DkdkebLC.js} +3 -3
- package/dist/chunks/{LpStepper-C9RM_yTG.js → LpStepper-BgqjKrqg.js} +1 -1
- package/dist/chunks/{LpSwap-DzE6M3Mg.js → LpSwap-B5YOXgtx.js} +1 -1
- package/dist/chunks/{LpSwitch-bGZxUDSx.js → LpSwitch-DyrRes7s.js} +1 -1
- package/dist/chunks/LpTabFace-D6BMCbAP.js +77 -0
- package/dist/chunks/{LpTableFilter-CTZMqFDU.js → LpTableFilter-Dkzamzkl.js} +3 -3
- package/dist/chunks/{LpTagsInput-DrjmrOge.js → LpTagsInput-DAZNNehI.js} +5 -5
- package/dist/chunks/{LpTextarea-BbYqmG1S.js → LpTextarea-DbmVPj2X.js} +32 -7
- package/dist/chunks/{useTheme-BOV_-AiY.js → LpThemeProvider-8MG6yqU9.js} +66 -1
- package/dist/chunks/{LpThemeStudio-B2nPaG-h.js → LpThemeStudio-DWtG6OZZ.js} +140 -106
- package/dist/chunks/LpThemeStudioSection-B7bt8Iyg.js +32 -0
- package/dist/chunks/{LpThemeSwitcher-C0A5sg_E.js → LpThemeSwitcher-Dcx-VkSk.js} +9 -8
- package/dist/chunks/{LpTimeField-DQ8WvPr2.js → LpTimeField-BawSVsmt.js} +1 -1
- package/dist/chunks/{LpToaster-BTTl_wuy.js → LpToaster-50hp_B5O.js} +2 -2
- package/dist/chunks/{LpTopologyCanvas-B0wEvAoq.js → LpTopologyCanvas-CnUh8uzS.js} +43 -13
- package/dist/chunks/{LpTour-BgCfNlaW.js → LpTour-DC3LTlxg.js} +1 -1
- package/dist/chunks/{LpTruncate-TcqfkyJM.js → LpTruncate-Ced4gquB.js} +1 -1
- package/dist/chunks/{LpUptimeBar-DRadGq4Y.js → LpUptimeBar-CuB0LX8K.js} +4 -4
- package/dist/chunks/{MenuTitle-Dab3EGOp.js → MenuTitle-BmCNNTKH.js} +1 -1
- package/dist/chunks/{OptionContent-DPzNsybd.js → OptionContent-22UT8zLn.js} +1 -1
- package/dist/chunks/{PickerField-DwLw7j0L.js → PickerField-Bfh8id3d.js} +2 -2
- package/dist/chunks/{RevealToggle-BVFM_FAU.js → RevealToggle-B0ITuIX-.js} +2 -2
- package/dist/chunks/{TourCard--2IfgnUU.js → TourCard-CBkj1GYi.js} +5 -5
- package/dist/chunks/{icons-CLu1iR1j.js → icons-DMITlfw5.js} +1 -1
- package/dist/chunks/{palette-BiY7yQZL.js → palette-Cvxl_1tM.js} +1 -1
- package/dist/component-names.d.ts +1 -1
- package/dist/components/ContextMenuItems.vue.js +1 -1
- package/dist/components/DropzoneLinkField.vue.js +1 -1
- package/dist/components/FieldWrapper.vue.js +1 -1
- 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/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.js +1 -1
- package/dist/components/LpAppShell.vue.d.ts +67 -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 +28 -0
- package/dist/components/LpBottomNav.vue.js +4 -0
- package/dist/components/LpBreadcrumbs.vue.js +1 -1
- 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.js +26 -8
- 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/LpColorPicker.vue.d.ts +15 -2
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.js +47 -26
- 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/LpCopyButton.vue.js +1 -1
- package/dist/components/LpCountdown.vue.js +1 -1
- package/dist/components/LpDataList.vue.js +1 -1
- package/dist/components/LpDatePicker.vue.js +1 -1
- package/dist/components/LpDateRangePicker.vue.js +1 -1
- package/dist/components/LpDisclosure.vue.js +1 -1
- package/dist/components/LpDock.vue.js +1 -1
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpDropdownMenu.vue.js +1 -1
- package/dist/components/LpDropzone.vue.js +2 -2
- package/dist/components/LpEditable.vue.js +1 -1
- package/dist/components/LpEmptyState.vue.d.ts +2 -0
- package/dist/components/LpEmptyState.vue.js +1 -1
- package/dist/components/LpFieldNotice.vue.js +1 -1
- package/dist/components/LpFileTree.vue.js +1 -1
- package/dist/components/LpFileTreeNode.vue.js +1 -1
- package/dist/components/LpFormField.vue.js +1 -1
- package/dist/components/LpGauge.vue.js +1 -1
- package/dist/components/LpHintLayer.vue.js +1 -1
- package/dist/components/LpHints.vue.js +1 -1
- package/dist/components/LpIcon.vue.js +1 -1
- package/dist/components/LpImageEditor.vue.js +2 -2
- package/dist/components/LpInfraNode.vue.js +1 -1
- package/dist/components/LpInput.vue.js +1 -1
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpLoader.vue.js +1 -1
- package/dist/components/LpLogViewer.vue.d.ts +1 -1
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpMapPicker.vue.js +1 -1
- package/dist/components/LpMediaGallery.vue.js +1 -1
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.js +1 -1
- package/dist/components/LpNumberField.vue.js +1 -1
- package/dist/components/LpNumberFlow.vue.js +1 -1
- package/dist/components/LpPagination.vue.js +1 -1
- package/dist/components/LpPaletteFacts.vue.js +2 -2
- package/dist/components/LpPaletteHeader.vue.js +2 -2
- package/dist/components/LpPaletteStats.vue.js +2 -2
- package/dist/components/LpPaletteTheme.vue.d.ts +2 -0
- 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.js +1 -1
- package/dist/components/LpPipeline.vue.js +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/LpRelativeTime.vue.js +1 -1
- package/dist/components/LpRepeater.vue.js +1 -1
- package/dist/components/LpRollingText.vue.d.ts +2 -2
- package/dist/components/LpRollingText.vue.js +1 -1
- package/dist/components/LpScramble.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/LpShift.vue.d.ts +20 -0
- package/dist/components/LpShift.vue.js +1 -1
- package/dist/components/LpShortcutsDialog.vue.js +1 -1
- package/dist/components/LpSidebar.vue.d.ts +74 -23
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.vue.d.ts +30 -2
- package/dist/components/LpSidebarNav.vue.js +1 -1
- package/dist/components/LpSidebarRow.vue.d.ts +49 -0
- package/dist/components/LpSidebarRow.vue.js +4 -0
- package/dist/components/LpStat.vue.d.ts +11 -0
- 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.d.ts +14 -0
- package/dist/components/LpTabFace.vue.js +4 -0
- package/dist/components/LpTable.vue.js +11 -11
- package/dist/components/LpTableFilter.vue.js +1 -1
- package/dist/components/LpTabs.vue.d.ts +12 -4
- package/dist/components/LpTabs.vue.js +22 -74
- package/dist/components/LpTagsInput.vue.js +1 -1
- package/dist/components/LpTextarea.vue.d.ts +19 -1
- package/dist/components/LpTextarea.vue.js +1 -1
- package/dist/components/LpThemeProvider.vue.js +1 -1
- package/dist/components/LpThemeStudio.vue.d.ts +2 -2
- package/dist/components/LpThemeStudio.vue.js +1 -1
- package/dist/components/LpThemeStudioSection.vue.d.ts +4 -1
- package/dist/components/LpThemeStudioSection.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/LpTopologyCanvas.vue.d.ts +3 -1
- 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/LpUptimeBar.vue.js +1 -1
- package/dist/components/MenuTitle.vue.js +1 -1
- package/dist/components/OptionContent.vue.js +1 -1
- package/dist/components/PickerField.vue.js +1 -1
- package/dist/components/RevealToggle.vue.js +1 -1
- package/dist/components/TourCard.vue.js +1 -1
- package/dist/components/chart.d.ts +14 -0
- package/dist/components/chart.js +13 -0
- package/dist/components/format.d.ts +10 -3
- package/dist/components/format.js +14 -2
- package/dist/components/menuWhen.js +1 -1
- package/dist/components/rolling.d.ts +5 -0
- package/dist/components/rolling.js +23 -0
- package/dist/components/sidebar.d.ts +33 -0
- package/dist/components/tabs.d.ts +3 -0
- package/dist/components/tabs.js +6 -0
- package/dist/composables/usePaletteCommands.js +1 -0
- package/dist/composables/useReveal.js +1 -22
- package/dist/icons-all.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +156 -154
- package/dist/theme/palette.d.ts +3 -0
- package/package.json +1 -1
- package/src/component-names.ts +1 -0
- package/src/components/LpAppShell.vue +75 -7
- package/src/components/LpBottomNav.vue +84 -0
- package/src/components/LpChart.vue +23 -3
- package/src/components/LpColorPicker.vue +32 -1
- package/src/components/LpCommandPalette.vue +23 -5
- package/src/components/LpEmptyState.vue +4 -1
- package/src/components/LpNumberFlow.vue +23 -9
- package/src/components/LpPaletteTheme.vue +3 -3
- package/src/components/LpRollingText.vue +27 -2
- package/src/components/LpSegmented.vue +3 -1
- package/src/components/LpSelect.vue +5 -4
- package/src/components/LpShift.vue +174 -2
- package/src/components/LpSidebar.vue +304 -18
- package/src/components/LpSidebarNav.vue +200 -70
- package/src/components/LpSidebarRow.vue +238 -0
- package/src/components/LpStat.vue +12 -4
- package/src/components/LpTabFace.vue +52 -0
- package/src/components/LpTabs.vue +15 -42
- package/src/components/LpTextarea.vue +34 -4
- package/src/components/LpThemeStudio.vue +92 -56
- package/src/components/LpThemeStudioSection.vue +15 -7
- package/src/components/LpThemeSwitcher.vue +4 -1
- package/src/components/LpTopologyCanvas.vue +35 -4
- package/src/components/chart.ts +26 -0
- package/src/components/format.ts +31 -5
- package/src/components/rolling.ts +28 -0
- package/src/components/sidebar.ts +33 -0
- package/src/components/tabs.ts +9 -0
- package/src/composables/usePaletteCommands.ts +3 -0
- package/src/composables/useReveal.ts +7 -30
- package/src/index.ts +2 -1
- package/src/kit-icons.ts +1 -1
- package/src/theme/palette.ts +4 -2
- package/dist/assets/LpTabs-CO3EcsNV.css +0 -18
- package/dist/chunks/LpRollingText-DiL717LK.js +0 -51
- package/dist/chunks/LpShift-DXNsauQZ.js +0 -40
- package/dist/chunks/LpSidebar-D44peKq2.js +0 -256
- package/dist/chunks/LpSidebarNav--VwkXO_a.js +0 -211
- package/dist/chunks/LpThemeProvider-DcbfOxsU.js +0 -69
- package/dist/chunks/LpThemeStudioSection-BwhqYDLf.js +0 -20
|
@@ -33,6 +33,7 @@ export default { inheritAttrs: false }
|
|
|
33
33
|
*/
|
|
34
34
|
import { computed, ref, useId, watch } from "vue"
|
|
35
35
|
import { useLabels } from "../composables/useLpConfig"
|
|
36
|
+
import LpBottomNav from "./LpBottomNav.vue"
|
|
36
37
|
import LpButton from "./LpButton.vue"
|
|
37
38
|
import LpIcon from "./LpIcon.vue"
|
|
38
39
|
import LpScrollArea from "./LpScrollArea.vue"
|
|
@@ -82,11 +83,37 @@ const props = withDefaults(
|
|
|
82
83
|
loading?: boolean
|
|
83
84
|
/** Text overrides: the burger's name and the skip link. */
|
|
84
85
|
labels?: Partial<{ openMenu: string; skipToContent: string }>
|
|
86
|
+
/** Icon-only rail (v-model:compact). Forwarded to LpSidebar. */
|
|
87
|
+
compact?: boolean
|
|
88
|
+
/** The person sizes the rail and folds it to icons. Forwarded to LpSidebar. */
|
|
89
|
+
resizable?: boolean
|
|
90
|
+
/** Rows can be pinned to the top (v-model:pinned). Forwarded to LpSidebar. */
|
|
91
|
+
pinnable?: boolean
|
|
92
|
+
pinned?: string[]
|
|
93
|
+
/** Remember folded groups, width, compact and pins under this key. Forwarded to LpSidebar. */
|
|
94
|
+
persist?: string
|
|
95
|
+
/** Rest time before a row's `preview` card opens. Forwarded to LpSidebar. */
|
|
96
|
+
previewDelay?: number
|
|
97
|
+
/** The key that folds the rail; false turns it off. Forwarded to LpSidebar. */
|
|
98
|
+
compactHotkey?: string | false
|
|
99
|
+
/** Marks a non-production copy along the top of the rail. Forwarded to LpSidebar. */
|
|
100
|
+
environment?: string | { label: string; tone?: "warning" | "danger" | "action" | "brand" }
|
|
101
|
+
/** The app's hint system; rows with a `hintPage` show unread news as a dot. Forwarded to LpSidebar. */
|
|
102
|
+
hints?: { unreadNews: (page?: string) => readonly unknown[] }
|
|
103
|
+
/**
|
|
104
|
+
* On phones, the main pages along the bottom instead of the burger, with
|
|
105
|
+
* "More" for the rest (it opens the drawer). `true` takes the first four
|
|
106
|
+
* pages, a number that many, a list of ids those pages.
|
|
107
|
+
*/
|
|
108
|
+
bottomNav?: boolean | number | string[]
|
|
85
109
|
}>(),
|
|
86
110
|
{
|
|
87
111
|
mobileBreakpoint: "lg",
|
|
88
112
|
divider: true,
|
|
89
113
|
contentClass: "mx-auto w-full max-w-6xl",
|
|
114
|
+
// Unset, not false: an absent boolean would hand the sidebar "expanded"
|
|
115
|
+
// and override the state it remembers.
|
|
116
|
+
compact: undefined,
|
|
90
117
|
},
|
|
91
118
|
)
|
|
92
119
|
|
|
@@ -94,11 +121,13 @@ const emit = defineEmits<{
|
|
|
94
121
|
(e: "update:modelValue", id: string): void
|
|
95
122
|
(e: "update:collapsed", keys: string[]): void
|
|
96
123
|
(e: "select", item: SidebarItem): void
|
|
124
|
+
(e: "update:compact", value: boolean): void
|
|
125
|
+
(e: "update:pinned", ids: string[]): void
|
|
97
126
|
}>()
|
|
98
127
|
|
|
99
128
|
// Normalise items/sections to a flat list for the title computation.
|
|
100
129
|
const flatItems = computed<SidebarItem[]>(() =>
|
|
101
|
-
props.sections ? props.sections.flatMap((s) => s.items) : (props.items ?? []),
|
|
130
|
+
(props.sections ? props.sections.flatMap((s) => s.items) : (props.items ?? [])).flatMap((i) => [i, ...(i.children ?? [])]),
|
|
102
131
|
)
|
|
103
132
|
|
|
104
133
|
// Default active test: exact match for "/", prefix match (on a segment
|
|
@@ -179,7 +208,23 @@ const burgerHidden: Record<NonNullable<typeof props.mobileBreakpoint>, string> =
|
|
|
179
208
|
lg: "lg:hidden",
|
|
180
209
|
xl: "xl:hidden",
|
|
181
210
|
}
|
|
182
|
-
const burgerClass = computed(() => burgerHidden[props.mobileBreakpoint])
|
|
211
|
+
const burgerClass = computed(() => (props.bottomNav ? "hidden" : burgerHidden[props.mobileBreakpoint]))
|
|
212
|
+
|
|
213
|
+
const topItems = computed<SidebarItem[]>(() => (props.sections ? props.sections.flatMap((s) => s.items) : (props.items ?? [])))
|
|
214
|
+
const bottomItems = computed<SidebarItem[]>(() => {
|
|
215
|
+
const want = props.bottomNav
|
|
216
|
+
if (!want) return []
|
|
217
|
+
if (Array.isArray(want)) return want.map((id) => topItems.value.find((i) => i.id === id)).filter((i): i is SidebarItem => !!i)
|
|
218
|
+
return topItems.value.slice(0, want === true ? 4 : want)
|
|
219
|
+
})
|
|
220
|
+
const moreActive = computed(() => {
|
|
221
|
+
const shown = new Set(bottomItems.value.map((i) => i.id))
|
|
222
|
+
return flatItems.value.some((i) => !shown.has(i.id) && activeTest.value(i)) && !bottomItems.value.some((i) => activeTest.value(i))
|
|
223
|
+
})
|
|
224
|
+
function onBottom(item: SidebarItem) {
|
|
225
|
+
onModel(item.id)
|
|
226
|
+
onSelect(item)
|
|
227
|
+
}
|
|
183
228
|
|
|
184
229
|
// Expose the burger trigger so a host can omit its own header entirely.
|
|
185
230
|
const _openDrawer = () => (drawerOpen.value = true)
|
|
@@ -215,20 +260,31 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
215
260
|
:loading="loading"
|
|
216
261
|
:collapsed="collapsed"
|
|
217
262
|
:auto-collapse-after="autoCollapseAfter"
|
|
263
|
+
:compact="compact"
|
|
264
|
+
:resizable="resizable"
|
|
265
|
+
:pinnable="pinnable"
|
|
266
|
+
:pinned="pinned"
|
|
267
|
+
:persist="persist"
|
|
268
|
+
:preview-delay="previewDelay"
|
|
269
|
+
:compact-hotkey="compactHotkey"
|
|
270
|
+
:environment="environment"
|
|
271
|
+
:hints="hints"
|
|
218
272
|
class="shrink-0"
|
|
219
273
|
@update:model-value="onModel"
|
|
274
|
+
@update:compact="(v) => emit('update:compact', v)"
|
|
275
|
+
@update:pinned="(v) => emit('update:pinned', v)"
|
|
220
276
|
@update:open="(v) => (drawerOpen = v)"
|
|
221
277
|
@update:collapsed="(v) => emit('update:collapsed', v)"
|
|
222
278
|
@select="onSelect"
|
|
223
279
|
>
|
|
224
|
-
<template v-if="$slots.logo" #header>
|
|
225
|
-
<slot name="logo" />
|
|
280
|
+
<template v-if="$slots.logo" #header="{ compact: folded }">
|
|
281
|
+
<slot name="logo" :compact="folded" />
|
|
226
282
|
</template>
|
|
227
283
|
<template v-if="$slots['sidebar-actions']" #actions>
|
|
228
284
|
<slot name="sidebar-actions" />
|
|
229
285
|
</template>
|
|
230
|
-
<template v-if="$slots['sidebar-footer']" #footer>
|
|
231
|
-
<slot name="sidebar-footer" />
|
|
286
|
+
<template v-if="$slots['sidebar-footer']" #footer="{ compact: folded }">
|
|
287
|
+
<slot name="sidebar-footer" :compact="folded" />
|
|
232
288
|
</template>
|
|
233
289
|
<template v-if="$slots['sidebar-item']" #item="slotProps">
|
|
234
290
|
<slot name="sidebar-item" v-bind="slotProps" />
|
|
@@ -261,7 +317,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
261
317
|
translation that wraps a long title onto a second line, otherwise
|
|
262
318
|
re-lays the whole bar in one frame. Width is left free so the
|
|
263
319
|
truncation still resolves against the space actually available. -->
|
|
264
|
-
<LpShift axis="height" class="flex min-w-0 flex-1 items-center">
|
|
320
|
+
<LpShift axis="height" presence class="flex min-w-0 flex-1 items-center">
|
|
265
321
|
<slot name="header-title">
|
|
266
322
|
<h1 class="truncate text-lg font-semibold">{{ pageTitle }}</h1>
|
|
267
323
|
</slot>
|
|
@@ -283,6 +339,8 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
283
339
|
the title keeps a third and every button stays reachable. -->
|
|
284
340
|
<LpShift
|
|
285
341
|
axis="width"
|
|
342
|
+
anchor="end"
|
|
343
|
+
presence
|
|
286
344
|
class="ml-auto flex max-w-[66%] shrink items-center gap-2 overflow-x-auto lp-scrollbar-none md:max-w-none md:shrink-0 md:overflow-visible"
|
|
287
345
|
>
|
|
288
346
|
<slot name="header-actions" />
|
|
@@ -304,6 +362,16 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
304
362
|
<slot />
|
|
305
363
|
</LpScrollArea>
|
|
306
364
|
</main>
|
|
365
|
+
<LpBottomNav
|
|
366
|
+
v-if="bottomNav"
|
|
367
|
+
:class="burgerHidden[mobileBreakpoint]"
|
|
368
|
+
:items="bottomItems"
|
|
369
|
+
:is-active="activeTest"
|
|
370
|
+
more
|
|
371
|
+
:more-active="moreActive"
|
|
372
|
+
@select="onBottom"
|
|
373
|
+
@more="drawerOpen = true"
|
|
374
|
+
/>
|
|
307
375
|
</div>
|
|
308
376
|
|
|
309
377
|
<slot name="overlays" />
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* A phone app's bottom navigation: the main pages along the bottom edge, each
|
|
4
|
+
* a real link (or a button that emits `select`), the current one marked by a
|
|
5
|
+
* bar that slides between them, and "More" last for everything else.
|
|
6
|
+
*
|
|
7
|
+
* It looks exactly like LpTabs' bottom variant, sharing its bar and its tab
|
|
8
|
+
* face, but it is navigation between pages, not tabs within one: links that a
|
|
9
|
+
* screen reader announces as links, that open in a new tab when asked to, and
|
|
10
|
+
* that need no router call from the app. LpAppShell shows one on phones with
|
|
11
|
+
* `bottomNav`; an app with a shell of its own uses it directly.
|
|
12
|
+
*/
|
|
13
|
+
import { Motion } from "motion-v"
|
|
14
|
+
import { useId } from "vue"
|
|
15
|
+
import { usePillTransition } from "../composables/usePillTransition"
|
|
16
|
+
import { useLabels, useLinkComponent } from "../composables/useLpConfig"
|
|
17
|
+
import { resolveLink, type ResolvedLink } from "./link"
|
|
18
|
+
import LpTabFace from "./LpTabFace.vue"
|
|
19
|
+
import type { SidebarItem } from "./sidebar"
|
|
20
|
+
import { BOTTOM_BAR, BOTTOM_ITEM, BOTTOM_MARKER } from "./tabs"
|
|
21
|
+
|
|
22
|
+
const props = withDefaults(
|
|
23
|
+
defineProps<{
|
|
24
|
+
items: SidebarItem[]
|
|
25
|
+
/** Active page id. Ignored when `isActive` is given. */
|
|
26
|
+
modelValue?: string
|
|
27
|
+
isActive?: (item: SidebarItem) => boolean
|
|
28
|
+
/** A last "More" entry that emits `more` (the shell opens its drawer). */
|
|
29
|
+
more?: boolean
|
|
30
|
+
/** Marks "More" current: the page shown is one of those behind it. */
|
|
31
|
+
moreActive?: boolean
|
|
32
|
+
label?: string
|
|
33
|
+
labels?: Partial<{ nav: string; more: string }>
|
|
34
|
+
}>(),
|
|
35
|
+
{ more: false, moreActive: false },
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const emit = defineEmits<{
|
|
39
|
+
(e: "select", item: SidebarItem): void
|
|
40
|
+
(e: "more"): void
|
|
41
|
+
}>()
|
|
42
|
+
|
|
43
|
+
const text = useLabels("bottomNav", { nav: "Main", more: "More" }, () => props.labels)
|
|
44
|
+
const linkComponent = useLinkComponent()
|
|
45
|
+
const pillTransition = usePillTransition()
|
|
46
|
+
const markerId = `lp-bottom-nav-${useId()}`
|
|
47
|
+
|
|
48
|
+
function active(item: SidebarItem): boolean {
|
|
49
|
+
return props.isActive ? props.isActive(item) : item.id === props.modelValue
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function rowOf(item: SidebarItem): ResolvedLink {
|
|
53
|
+
const link = item.disabled ? null : resolveLink(linkComponent.value, { to: item.to, href: item.href })
|
|
54
|
+
return link ?? { is: "button", props: { type: "button", disabled: item.disabled || undefined } }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const ROW = "relative flex items-center justify-center outline-none transition-[color,opacity] duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring"
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<template>
|
|
61
|
+
<nav :aria-label="label ?? text.nav" :class="BOTTOM_BAR">
|
|
62
|
+
<component
|
|
63
|
+
:is="rowOf(item).is"
|
|
64
|
+
v-for="item in items"
|
|
65
|
+
:key="item.id"
|
|
66
|
+
v-bind="rowOf(item).props"
|
|
67
|
+
:aria-current="active(item) ? 'page' : undefined"
|
|
68
|
+
:class="[ROW, BOTTOM_ITEM, item.disabled ? 'cursor-not-allowed opacity-45' : active(item) ? 'text-brand' : 'text-muted']"
|
|
69
|
+
@click="emit('select', item)"
|
|
70
|
+
>
|
|
71
|
+
<Motion v-if="active(item)" :layout-id="markerId" :transition="pillTransition" :class="[BOTTOM_MARKER, 'bg-brand']" />
|
|
72
|
+
<LpTabFace :item="item" bottom :active="active(item)" />
|
|
73
|
+
</component>
|
|
74
|
+
<button
|
|
75
|
+
v-if="more"
|
|
76
|
+
type="button"
|
|
77
|
+
:class="[ROW, BOTTOM_ITEM, moreActive ? 'text-brand' : 'text-muted']"
|
|
78
|
+
@click="emit('more')"
|
|
79
|
+
>
|
|
80
|
+
<Motion v-if="moreActive" :layout-id="markerId" :transition="pillTransition" :class="[BOTTOM_MARKER, 'bg-brand']" />
|
|
81
|
+
<LpTabFace :item="{ label: text.more, icon: 'lucide:menu' }" bottom :active="moreActive" />
|
|
82
|
+
</button>
|
|
83
|
+
</nav>
|
|
84
|
+
</template>
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
pointCount,
|
|
42
42
|
resolveLabels,
|
|
43
43
|
xAt,
|
|
44
|
+
xLabelIndices,
|
|
44
45
|
yAt,
|
|
45
46
|
type Box,
|
|
46
47
|
type ChartMarker,
|
|
@@ -255,6 +256,23 @@ const allSeries = computed(() =>
|
|
|
255
256
|
)
|
|
256
257
|
const visible = computed(() => allSeries.value.filter((s) => !hidden.value.has(s.id)))
|
|
257
258
|
|
|
259
|
+
// A series asked to start hidden is hidden once, when it first appears: the
|
|
260
|
+
// legend owns it from then on, and a data refresh must not switch it back off.
|
|
261
|
+
const seeded = new Set<string>()
|
|
262
|
+
watch(
|
|
263
|
+
() => props.series.map((s, i) => [`${i}:${s.name}`, !!s.hidden] as const),
|
|
264
|
+
(entries) => {
|
|
265
|
+
let next: Set<string> | null = null
|
|
266
|
+
for (const [id, off] of entries) {
|
|
267
|
+
if (seeded.has(id)) continue
|
|
268
|
+
seeded.add(id)
|
|
269
|
+
if (off) (next ??= new Set(hidden.value)).add(id)
|
|
270
|
+
}
|
|
271
|
+
if (next && next.size < entries.length) hidden.value = next
|
|
272
|
+
},
|
|
273
|
+
{ immediate: true },
|
|
274
|
+
)
|
|
275
|
+
|
|
258
276
|
/*
|
|
259
277
|
* How present each series is, 1 to 0, driven per frame rather than by CSS.
|
|
260
278
|
*
|
|
@@ -664,9 +682,11 @@ const xLabelStride = computed(() => {
|
|
|
664
682
|
})
|
|
665
683
|
|
|
666
684
|
const xLabels = computed(() =>
|
|
667
|
-
labels.value
|
|
668
|
-
|
|
669
|
-
|
|
685
|
+
xLabelIndices(labels.value.length, xLabelStride.value).map((i) => ({
|
|
686
|
+
label: labels.value[i],
|
|
687
|
+
i,
|
|
688
|
+
x: xAt(i, count.value, box.value, banded.value),
|
|
689
|
+
})),
|
|
670
690
|
)
|
|
671
691
|
|
|
672
692
|
// ── marks ──────────────────────────────────────
|
|
@@ -104,6 +104,17 @@ const props = withDefaults(
|
|
|
104
104
|
size?: "sm" | "md" | "lg"
|
|
105
105
|
/** Trigger text beside the swatch. Defaults to the colour's own value. */
|
|
106
106
|
label?: string
|
|
107
|
+
/**
|
|
108
|
+
* `swatch` makes the trigger a round chip of the colour alone, for a row of
|
|
109
|
+
* presets that ends in "any colour" or a toolbar with no room for a field.
|
|
110
|
+
* The panel is the same.
|
|
111
|
+
*/
|
|
112
|
+
variant?: "field" | "swatch"
|
|
113
|
+
/**
|
|
114
|
+
* The swatch is the current choice. False draws it as "any colour" (a
|
|
115
|
+
* colour wheel), for when the value came from presets beside it.
|
|
116
|
+
*/
|
|
117
|
+
selected?: boolean
|
|
107
118
|
/** Accessible name for the trigger when it carries no visible text. */
|
|
108
119
|
ariaLabel?: string
|
|
109
120
|
/** Popover side, as LpPopover. */
|
|
@@ -156,6 +167,8 @@ const props = withDefaults(
|
|
|
156
167
|
recent: false,
|
|
157
168
|
recentKey: "lp.color.recent",
|
|
158
169
|
recentLimit: 8,
|
|
170
|
+
variant: "field",
|
|
171
|
+
selected: true,
|
|
159
172
|
},
|
|
160
173
|
)
|
|
161
174
|
|
|
@@ -372,6 +385,9 @@ function tickClass(value: string) {
|
|
|
372
385
|
return rgb && isLight(rgbToHsv(rgb)) ? "text-black" : "text-white"
|
|
373
386
|
}
|
|
374
387
|
|
|
388
|
+
const SWATCH_SIZE = { sm: "size-6", md: "size-7", lg: "size-8" }
|
|
389
|
+
const ANY_COLOUR = "conic-gradient(from 90deg, #ff5c5c, #ffd23f, #39e58c, #2dd4bf, #00bcff, #7c5cff, #ff5ca8, #ff5c5c)"
|
|
390
|
+
|
|
375
391
|
const triggerSize = {
|
|
376
392
|
sm: "h-(--size-control-sm) text-xs",
|
|
377
393
|
md: "h-(--size-control-md) text-sm",
|
|
@@ -634,10 +650,25 @@ const guardOutside = useOutsideGuard()
|
|
|
634
650
|
menu's trigger and the popover's trigger both. LpContextMenu renders
|
|
635
651
|
just its slot when `items` is empty, so `menu="none"` with no items of
|
|
636
652
|
your own leaves the native browser menu in place. -->
|
|
637
|
-
<PopoverAnchor class="block w-full">
|
|
653
|
+
<PopoverAnchor :class="variant === 'swatch' ? 'inline-block' : 'block w-full'">
|
|
638
654
|
<LpContextMenu :items="menuItems">
|
|
639
655
|
<PopoverTrigger as-child>
|
|
640
656
|
<button
|
|
657
|
+
v-if="variant === 'swatch'"
|
|
658
|
+
type="button"
|
|
659
|
+
:id="field.id"
|
|
660
|
+
:disabled="field.disabled"
|
|
661
|
+
:aria-label="ariaLabel ?? current"
|
|
662
|
+
:title="current"
|
|
663
|
+
class="relative shrink-0 overflow-hidden rounded-full outline-none ring-2 ring-offset-2 ring-offset-surface-raised transition-[box-shadow,scale] duration-(--duration-fast) hover:scale-110 focus-visible:ring-ring data-[state=open]:ring-brand disabled:cursor-not-allowed disabled:opacity-55"
|
|
664
|
+
:class="[SWATCH_SIZE[size], selected ? 'ring-ink' : 'ring-line']"
|
|
665
|
+
:style="{ background: CHECKERBOARD }"
|
|
666
|
+
>
|
|
667
|
+
<span class="block size-full" :style="{ background: selected ? previewCss : ANY_COLOUR }" />
|
|
668
|
+
<LpIcon v-if="!selected" name="lucide:pipette" :size="12" class="absolute inset-0 m-auto text-white drop-shadow" />
|
|
669
|
+
</button>
|
|
670
|
+
<button
|
|
671
|
+
v-else
|
|
641
672
|
type="button"
|
|
642
673
|
:id="field.id"
|
|
643
674
|
:disabled="field.disabled"
|
|
@@ -1519,7 +1519,10 @@ const guardOutside = useOutsideGuard()
|
|
|
1519
1519
|
<div class="flex gap-2"><LpButton :loading="executing || validating" @click="submitPrompt">{{ text.submit }}</LpButton><LpButton variant="ghost" @click="promptCommand = undefined; inputRef?.focus()">{{ text.backToResults }}</LpButton></div>
|
|
1520
1520
|
</div>
|
|
1521
1521
|
<p v-if="actionError" role="alert" class="px-4 py-2 text-sm text-danger">{{ actionError }}</p>
|
|
1522
|
-
|
|
1522
|
+
<!-- Touch only: there the footer with its keys is hidden, and these are
|
|
1523
|
+
the only way to the row's actions and pin. On a pointer the same
|
|
1524
|
+
buttons live in the footer, so the list never shifts under this strip. -->
|
|
1525
|
+
<div v-if="active && (canPin || selectedActions.length) && !promptCommand" class="hidden items-center justify-end gap-2 border-b border-line px-3 py-1 [@media(hover:none)_and_(pointer:coarse)]:flex">
|
|
1523
1526
|
<LpButton v-if="selectedActions.length" size="xs" variant="ghost" @click="openActions(active)">{{ text.actions }} <span class="text-muted">Tab</span></LpButton>
|
|
1524
1527
|
<LpButton v-if="canPin" size="xs" variant="ghost" :aria-label="isPinned(active) ? text.unpin : text.pin" :aria-pressed="isPinned(active)" @click="togglePin(active)"><LpIcon name="lucide:star" :size="14" />{{ isPinned(active) ? text.unpin : text.pin }} <span class="text-muted">Alt+P</span></LpButton>
|
|
1525
1528
|
</div>
|
|
@@ -1727,17 +1730,32 @@ const guardOutside = useOutsideGuard()
|
|
|
1727
1730
|
</span>
|
|
1728
1731
|
<!-- Only inside a mode: at the root Backspace just edits the query,
|
|
1729
1732
|
and advertising it there would be a lie. -->
|
|
1730
|
-
<
|
|
1731
|
-
v-if="selectedActions.length"
|
|
1732
|
-
|
|
1733
|
+
<button
|
|
1734
|
+
v-if="selectedActions.length && active"
|
|
1735
|
+
type="button"
|
|
1736
|
+
class="flex items-center gap-1 rounded-small outline-none transition-colors hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
|
|
1733
1737
|
:class="showActionsKey && 'font-medium text-brand'"
|
|
1738
|
+
:aria-label="text.actions"
|
|
1739
|
+
@click="openActions(active)"
|
|
1734
1740
|
>
|
|
1735
1741
|
<kbd
|
|
1736
1742
|
class="rounded-small border px-1 text-[10px] transition-colors"
|
|
1737
1743
|
:class="showActionsKey ? 'border-brand' : 'border-line'"
|
|
1738
1744
|
>{{ text.tab }}</kbd>
|
|
1739
1745
|
{{ text.actions }}
|
|
1740
|
-
</
|
|
1746
|
+
</button>
|
|
1747
|
+
<button
|
|
1748
|
+
v-if="canPin && active && !promptCommand"
|
|
1749
|
+
type="button"
|
|
1750
|
+
class="flex items-center gap-1 rounded-small outline-none transition-colors hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
|
|
1751
|
+
:aria-label="isPinned(active) ? text.unpin : text.pin"
|
|
1752
|
+
:aria-pressed="isPinned(active)"
|
|
1753
|
+
@click="togglePin(active)"
|
|
1754
|
+
>
|
|
1755
|
+
<kbd class="rounded-small border border-line px-1 text-[10px]">Alt+P</kbd>
|
|
1756
|
+
<LpIcon name="lucide:star" :size="11" :class="isPinned(active) && 'fill-current text-brand'" />
|
|
1757
|
+
{{ isPinned(active) ? text.unpin : text.pin }}
|
|
1758
|
+
</button>
|
|
1741
1759
|
<span v-if="trail.length" class="flex items-center gap-1">
|
|
1742
1760
|
<kbd class="rounded-small border border-line px-1 text-[10px]">⌫</kbd>
|
|
1743
1761
|
{{ text.back }}
|
|
@@ -45,7 +45,10 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
45
45
|
</div>
|
|
46
46
|
<div class="flex flex-col gap-1">
|
|
47
47
|
<p class="font-semibold text-ink" :class="compact ? 'text-sm' : ''">{{ title }}</p>
|
|
48
|
-
|
|
48
|
+
<!-- The slot is for a line a string cannot carry: a command, a link. -->
|
|
49
|
+
<p v-if="description || $slots.description" class="max-w-sm text-sm text-muted">
|
|
50
|
+
<slot name="description">{{ description }}</slot>
|
|
51
|
+
</p>
|
|
49
52
|
</div>
|
|
50
53
|
<div v-if="$slots.default" class="mt-1"><slot /></div>
|
|
51
54
|
</div>
|
|
@@ -229,8 +229,8 @@ onBeforeUnmount(stop)
|
|
|
229
229
|
* own, the same rule the dates follow: a Russian app read "4,594" otherwise.
|
|
230
230
|
*/
|
|
231
231
|
const locale = useLocale(() => props.locale)
|
|
232
|
-
//
|
|
233
|
-
const fractionMark = computed(() => (props.grouped
|
|
232
|
+
// The grouped and compact forms are written in the locale; the plain one always uses ".".
|
|
233
|
+
const fractionMark = computed(() => ((props.grouped || props.compact) && !props.format ? decimalMark(locale.value) : "."))
|
|
234
234
|
|
|
235
235
|
const formatted = computed(() => formatAt(round(displayed.value), !settled.value))
|
|
236
236
|
|
|
@@ -249,10 +249,10 @@ function formatAt(v: number, padded: boolean): string {
|
|
|
249
249
|
*
|
|
250
250
|
* The decimal count is what stays fixed: one place whenever a unit is in
|
|
251
251
|
* play, so the row's shape only changes at a rollover, not on every frame.
|
|
252
|
-
*
|
|
253
|
-
* locale's
|
|
252
|
+
* Written in the app's locale: the cells find the fraction by that
|
|
253
|
+
* locale's own mark (fractionMark), so "1,2 тыс." keeps its columns.
|
|
254
254
|
*/
|
|
255
|
-
return compactNumber(v, { plainDecimals: 0, fixed: true })
|
|
255
|
+
return compactNumber(v, { plainDecimals: 0, fixed: true, locale: locale.value, words: true })
|
|
256
256
|
}
|
|
257
257
|
const pad = padded ? Math.max(1, padTo.value) : 1
|
|
258
258
|
if (props.grouped)
|
|
@@ -280,6 +280,8 @@ interface Cell {
|
|
|
280
280
|
char: string
|
|
281
281
|
/** A leading zero held open for a column the value hasn't reached yet. */
|
|
282
282
|
isPad: boolean
|
|
283
|
+
/** A space between two digits: the locale's thousands separator. */
|
|
284
|
+
isGap: boolean
|
|
283
285
|
}
|
|
284
286
|
|
|
285
287
|
/*
|
|
@@ -338,7 +340,8 @@ const cells = computed<Cell[]>(() => {
|
|
|
338
340
|
const digit = isDigit(char) ? char.charCodeAt(0) - 48 : null
|
|
339
341
|
const isPad = leading && digit === 0 && i < intEnd - 1
|
|
340
342
|
if (digit !== null && digit !== 0) leading = false
|
|
341
|
-
|
|
343
|
+
const isGap = digit === null && /\s/.test(char) && isDigit(chars[i - 1] ?? "") && isDigit(chars[i + 1] ?? "")
|
|
344
|
+
return { key: keys[i], digit, char, isPad, isGap }
|
|
342
345
|
})
|
|
343
346
|
})
|
|
344
347
|
|
|
@@ -372,7 +375,9 @@ const label = computed(
|
|
|
372
375
|
class="lp-nf inline-flex items-baseline tabular-nums"
|
|
373
376
|
>
|
|
374
377
|
<span class="sr-only">{{ label }}</span>
|
|
375
|
-
|
|
378
|
+
<!-- `pre`: a flex item drops its leading and trailing spaces, so " ms"
|
|
379
|
+
as a suffix printed "12.8ms". -->
|
|
380
|
+
<span v-if="prefix" aria-hidden="true" class="whitespace-pre">{{ prefix }}</span>
|
|
376
381
|
|
|
377
382
|
<span class="lp-nf__track inline-flex items-baseline" aria-hidden="true">
|
|
378
383
|
<TransitionGroup name="lp-nf-cell">
|
|
@@ -380,7 +385,7 @@ const label = computed(
|
|
|
380
385
|
v-for="cell in cells"
|
|
381
386
|
:key="cell.key"
|
|
382
387
|
class="lp-nf__cell"
|
|
383
|
-
:class="cell.isPad
|
|
388
|
+
:class="{ 'lp-nf__cell--pad': cell.isPad, 'lp-nf__cell--gap': cell.isGap }"
|
|
384
389
|
>
|
|
385
390
|
<!-- A digit is a window onto its strip; a separator is just a glyph. -->
|
|
386
391
|
<template v-if="cell.digit !== null">
|
|
@@ -399,7 +404,7 @@ const label = computed(
|
|
|
399
404
|
</TransitionGroup>
|
|
400
405
|
</span>
|
|
401
406
|
|
|
402
|
-
<span v-if="suffix" aria-hidden="true">{{ suffix }}</span>
|
|
407
|
+
<span v-if="suffix" aria-hidden="true" class="whitespace-pre">{{ suffix }}</span>
|
|
403
408
|
</span>
|
|
404
409
|
</template>
|
|
405
410
|
|
|
@@ -486,6 +491,15 @@ const label = computed(
|
|
|
486
491
|
|
|
487
492
|
/* Faded, so a held-open column reads as an empty wheel rather than a zero the
|
|
488
493
|
number actually has. */
|
|
494
|
+
/*
|
|
495
|
+
* A space separator (ru, fr, …) takes a whole column in a monospaced or
|
|
496
|
+
* tabular face, so "7 271" read as "7 271". It is drawn as the narrow gap
|
|
497
|
+
* the locale means.
|
|
498
|
+
*/
|
|
499
|
+
.lp-nf__cell--gap {
|
|
500
|
+
width: 0.25em;
|
|
501
|
+
}
|
|
502
|
+
|
|
489
503
|
.lp-nf__cell--pad {
|
|
490
504
|
opacity: 0.18;
|
|
491
505
|
transition: opacity var(--duration-fast, 160ms) linear;
|
|
@@ -3,12 +3,12 @@ import { computed, ref } from "vue"
|
|
|
3
3
|
import type { StudioState, PreviewableThemeStudio } from "../theme/studio"
|
|
4
4
|
import { useLabels } from "../composables/useLpConfig"
|
|
5
5
|
import { suppressPaletteDiscovery } from "../composables/usePaletteCommands"
|
|
6
|
-
import LpThemeStudio from "./LpThemeStudio.vue"
|
|
6
|
+
import LpThemeStudio, { type ThemeStudioPart } from "./LpThemeStudio.vue"
|
|
7
7
|
import LpThemeProvider from "./LpThemeProvider.vue"
|
|
8
8
|
import LpButton from "./LpButton.vue"
|
|
9
9
|
import LpInput from "./LpInput.vue"
|
|
10
10
|
|
|
11
|
-
const props = defineProps<{ studio: PreviewableThemeStudio }>()
|
|
11
|
+
const props = defineProps<{ studio: PreviewableThemeStudio; hide?: ThemeStudioPart[] }>()
|
|
12
12
|
suppressPaletteDiscovery()
|
|
13
13
|
const text = useLabels("paletteTheme", { title: "Theme", apply: "Apply theme", revert: "Revert draft", sample: "Preview", action: "Continue", placeholder: "A field in this theme" })
|
|
14
14
|
const snapshot = ref({ ...props.studio.state.value })
|
|
@@ -43,6 +43,6 @@ function apply() {
|
|
|
43
43
|
<LpInput :placeholder="text.placeholder" readonly :tabindex="-1" />
|
|
44
44
|
<LpButton :tabindex="-1">{{ text.action }}</LpButton>
|
|
45
45
|
</LpThemeProvider>
|
|
46
|
-
<LpThemeStudio :studio="draft" :labels="{ reset: text.revert }" />
|
|
46
|
+
<LpThemeStudio :studio="draft" :hide="hide" :labels="{ reset: text.revert }" />
|
|
47
47
|
</div>
|
|
48
48
|
</template>
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* <LpRollingText :value="label" />
|
|
21
21
|
* <LpRollingText :value="status" mode="flip" />
|
|
22
22
|
*/
|
|
23
|
-
import { computed } from "vue"
|
|
23
|
+
import { computed, ref, useSlots, watch } from "vue"
|
|
24
|
+
import { splitChange } from "./rolling"
|
|
24
25
|
|
|
25
26
|
const props = withDefaults(
|
|
26
27
|
defineProps<{
|
|
@@ -45,6 +46,21 @@ const props = withDefaults(
|
|
|
45
46
|
text patch — without it there is nothing to transition between. */
|
|
46
47
|
const key = computed(() => String(props.value))
|
|
47
48
|
|
|
49
|
+
/*
|
|
50
|
+
* Only what changed rolls. "Valid for 2:54" turning into "2:53" rolled every
|
|
51
|
+
* word of the line once a second (a countdown badge), so the whole badge
|
|
52
|
+
* flickered; now "Valid for 2:5" stays and the "4" rolls to "3". With a slot
|
|
53
|
+
* of its own, or wrapping text, the whole value rolls as before.
|
|
54
|
+
*/
|
|
55
|
+
const slots = useSlots()
|
|
56
|
+
const partial = computed(() => !slots.default && !props.wrap)
|
|
57
|
+
const parts = ref({ head: "", mid: String(props.value), tail: "" })
|
|
58
|
+
watch(
|
|
59
|
+
() => String(props.value),
|
|
60
|
+
(next, was) => (parts.value = splitChange(was, next)),
|
|
61
|
+
)
|
|
62
|
+
const midKey = computed(() => `${parts.value.head.length}:${parts.value.mid}`)
|
|
63
|
+
|
|
48
64
|
const name = computed(() =>
|
|
49
65
|
props.mode === "flip" ? "lp-roll-flip" : `lp-roll-${props.direction}`,
|
|
50
66
|
)
|
|
@@ -68,7 +84,16 @@ const style = computed(() => ({
|
|
|
68
84
|
:class="[justify, wrap && 'lp-roll--wrap']"
|
|
69
85
|
:style="style"
|
|
70
86
|
>
|
|
71
|
-
<
|
|
87
|
+
<template v-if="partial">
|
|
88
|
+
<span v-if="parts.head" class="whitespace-pre">{{ parts.head }}</span>
|
|
89
|
+
<span class="relative inline-flex overflow-hidden">
|
|
90
|
+
<Transition :name="name">
|
|
91
|
+
<span :key="midKey" class="lp-roll__item whitespace-pre">{{ parts.mid }}</span>
|
|
92
|
+
</Transition>
|
|
93
|
+
</span>
|
|
94
|
+
<span v-if="parts.tail" class="whitespace-pre">{{ parts.tail }}</span>
|
|
95
|
+
</template>
|
|
96
|
+
<Transition v-else :name="name">
|
|
72
97
|
<span :key="key" class="lp-roll__item">
|
|
73
98
|
<slot :value="value">{{ value }}</slot>
|
|
74
99
|
</span>
|
|
@@ -79,7 +79,9 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
79
79
|
[
|
|
80
80
|
"inline-flex items-center gap-1 rounded-control border border-line bg-surface-soft p-1",
|
|
81
81
|
sizeCls[props.size],
|
|
82
|
-
|
|
82
|
+
// `w-fit`, not left to the parent: in a column a flex item stretches, and
|
|
83
|
+
// the options sat on the left of an empty bar (the theme studio met it).
|
|
84
|
+
props.block ? "flex w-full" : "w-fit",
|
|
83
85
|
].join(" "),
|
|
84
86
|
)
|
|
85
87
|
|
|
@@ -104,10 +104,11 @@ const props = withDefaults(
|
|
|
104
104
|
},
|
|
105
105
|
)
|
|
106
106
|
|
|
107
|
+
/* The text steps with the height, as in every other kit field: a small select beside a small input read a size larger. */
|
|
107
108
|
const SIZE_HEIGHT = {
|
|
108
|
-
sm: "h-(--size-control-sm)",
|
|
109
|
-
md: "h-(--size-control-md)",
|
|
110
|
-
lg: "h-(--size-control-lg)",
|
|
109
|
+
sm: "h-(--size-control-sm) text-xs",
|
|
110
|
+
md: "h-(--size-control-md) text-sm",
|
|
111
|
+
lg: "h-(--size-control-lg) text-sm",
|
|
111
112
|
} as const
|
|
112
113
|
const anchorHeight = computed(() => SIZE_HEIGHT[props.size])
|
|
113
114
|
|
|
@@ -431,7 +432,7 @@ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.v
|
|
|
431
432
|
@focusout="onFocusOut"
|
|
432
433
|
:class="[
|
|
433
434
|
anchorHeight,
|
|
434
|
-
'group flex w-full items-center gap-2 rounded-control border bg-surface-soft px-3 text-
|
|
435
|
+
'group flex w-full items-center gap-2 rounded-control border bg-surface-soft px-3 text-ink transition-colors duration-[var(--duration-fast)] focus-within:ring-2',
|
|
435
436
|
field.disabled && 'cursor-not-allowed opacity-55',
|
|
436
437
|
field.invalid
|
|
437
438
|
? 'border-danger focus-within:border-danger focus-within:ring-danger-soft'
|