@leavepulse/ui 0.70.1 → 0.71.1
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 +91 -0
- package/dist/assets/{LpCommandPalette-u-zOxKWa.css → LpCommandPalette-DgPZX0us.css} +24 -17
- package/dist/assets/{LpPaletteFacts-B9FdVmIO.css → LpPaletteFacts-CVNh5ICn.css} +16 -16
- package/dist/assets/{LpPaletteStats-ClE-37MK.css → LpPaletteStats-ZiZmR6vz.css} +5 -5
- package/dist/assets/{LpTable-DHxEQ_-3.css → LpTable-DHvorDiU.css} +19 -19
- package/dist/assets/LpTabs-CO3EcsNV.css +18 -0
- package/dist/chunks/{ContextMenuItems-6kxEivBI.js → ContextMenuItems-eS7qx9gD.js} +1 -1
- package/dist/chunks/{DropzoneLinkField-Bw8Flkhk.js → DropzoneLinkField-DkFhdfEJ.js} +2 -2
- package/dist/chunks/{FieldWrapper-BiennROB.js → FieldWrapper-YzbvvC3M.js} +1 -1
- package/dist/chunks/{LayoutCanvas-BsJIzhdd.js → LayoutCanvas-Ba4A9QC5.js} +1 -1
- package/dist/chunks/{LayoutNode-DJVhkSGk.js → LayoutNode-CTTTLMDM.js} +1 -1
- package/dist/chunks/{LpActionBar-CzOFar5t.js → LpActionBar-B7anGlkI.js} +2 -2
- package/dist/chunks/{LpAddressInput-Chv_Xf8q.js → LpAddressInput-CD0bzrt-.js} +7 -7
- package/dist/chunks/{LpAlert-C5igj802.js → LpAlert-BHi7ARqu.js} +1 -1
- package/dist/chunks/{LpAppShell-BiWByLmt.js → LpAppShell-1RmBp6xQ.js} +3 -3
- package/dist/chunks/{LpAutocomplete-D92niDHE.js → LpAutocomplete-DDqe2M5g.js} +5 -2
- package/dist/chunks/{LpAvatar-CLPl-qOF.js → LpAvatar-DqfKCIdr.js} +1 -1
- package/dist/chunks/{LpBadge-BBq1Er9T.js → LpBadge-C8fKEmTF.js} +1 -1
- package/dist/chunks/{LpBreadcrumbs-JKEi_yFF.js → LpBreadcrumbs-DhcoStFa.js} +1 -1
- package/dist/chunks/{LpButton-CVxBfGjd.js → LpButton-BQDAFXBd.js} +3 -0
- package/dist/chunks/{LpCalendar-pVL91-Pl.js → LpCalendar-NmrA47J9.js} +1 -1
- package/dist/chunks/{LpCheckbox-DHc2bHDe.js → LpCheckbox-BoOuLnGB.js} +5 -2
- package/dist/chunks/{LpCodeBlock-BGsQhxw8.js → LpCodeBlock-Czu-fq1M.js} +2 -2
- package/dist/chunks/{LpCodeDisplay-CbJMs-IK.js → LpCodeDisplay-BK2FHbtM.js} +5 -5
- package/dist/chunks/{LpColorPicker-B7HSIFmn.js → LpColorPicker-Ee1DZiRd.js} +2 -2
- package/dist/chunks/LpCommandScope-ubtWDMa0.js +53 -0
- package/dist/chunks/{LpConfirmDialog-CfGCux8m.js → LpConfirmDialog-i15pulMT.js} +3 -3
- package/dist/chunks/{LpConfirmHost-exB7JmcO.js → LpConfirmHost-D0oGavtd.js} +1 -1
- package/dist/chunks/{LpContextMenu-Cj96ENDC.js → LpContextMenu-DXpFJjlO.js} +13 -3
- package/dist/chunks/{LpCopyButton-DzHjnFs0.js → LpCopyButton-Dmq7DmFw.js} +3 -3
- package/dist/chunks/{LpCountdown-BQnmB4gq.js → LpCountdown-DTdMF8DL.js} +1 -1
- package/dist/chunks/{LpDataList-CHNYQw7h.js → LpDataList-CFPtSsBB.js} +3 -3
- package/dist/chunks/{LpDatePicker-ac1_6o5P.js → LpDatePicker-cC9K-Pk8.js} +4 -4
- package/dist/chunks/{LpDateRangePicker-B15gYrZJ.js → LpDateRangePicker-BVQKPbvo.js} +2 -2
- package/dist/chunks/{LpDisclosure-DOarzlAG.js → LpDisclosure-hRnX3FWL.js} +4 -1
- package/dist/chunks/{LpDrawer-DGNKRsGq.js → LpDrawer-u4rxnuR-.js} +1 -1
- package/dist/chunks/{LpDropdownMenu-R4aYVtVV.js → LpDropdownMenu-BgZ0Z_Im.js} +13 -3
- package/dist/chunks/{LpEditable-Ws3DNQHn.js → LpEditable-CjxuOVh_.js} +2 -2
- package/dist/chunks/{LpEmptyState-KTKipws2.js → LpEmptyState-CKFTtQgJ.js} +1 -1
- package/dist/chunks/{LpFieldNotice-CurbNuXI.js → LpFieldNotice-CPI8EwtU.js} +1 -1
- package/dist/chunks/{LpFileTree-DDNg34tJ.js → LpFileTree-BH0gNlSJ.js} +2 -2
- package/dist/chunks/{LpFileTreeNode-DmPuYNZ_.js → LpFileTreeNode-gPVB1oXM.js} +4 -4
- package/dist/chunks/{LpFormField-BWwgm1E4.js → LpFormField-gRASfyj2.js} +4 -1
- package/dist/chunks/{LpGauge-C14Z9503.js → LpGauge-BdjPGnqw.js} +1 -1
- package/dist/chunks/{LpHintLayer-HvwUA5Ec.js → LpHintLayer-C2asBIfs.js} +2 -2
- package/dist/chunks/{LpHints-DxbPKOzQ.js → LpHints-CNZQ76JV.js} +3 -3
- package/dist/chunks/{LpIcon-HJP5xMz4.js → LpIcon-DkP-9mzK.js} +1 -1
- package/dist/chunks/{LpInfraNode-D64hywJI.js → LpInfraNode-yTh4-5qa.js} +2 -2
- package/dist/chunks/{LpInput-CDuwODJG.js → LpInput-2H-5ILOB.js} +5 -2
- package/dist/chunks/{LpLightbox-XJ0LpTcZ.js → LpLightbox-lAt5uIfx.js} +2 -2
- package/dist/chunks/{LpLogViewer-DKK4ySZl.js → LpLogViewer-x78YfhJf.js} +5 -5
- package/dist/chunks/{LpMapPicker-BtIl7NOM.js → LpMapPicker-Dp5RSCi9.js} +1 -1
- package/dist/chunks/{LpMediaGallery-rmI1DjKN.js → LpMediaGallery-D92oJfY_.js} +3 -3
- package/dist/chunks/{LpModal-eBMt2Fqn.js → LpModal-CpH0bNo7.js} +1 -1
- package/dist/chunks/{LpModalNav-DiyFCHiZ.js → LpModalNav-BH4ANQGJ.js} +4 -4
- package/dist/chunks/{LpNotificationBell-D-WhE5m2.js → LpNotificationBell-BdUQqZhs.js} +6 -6
- package/dist/chunks/{LpNumberField-B9k-h2HE.js → LpNumberField-DZY3Z670.js} +4 -1
- package/dist/chunks/{LpNumberFlow-Cfyh033Q.js → LpNumberFlow-5khYxsfX.js} +6 -3
- package/dist/chunks/{LpPagination-VNCX6pFC.js → LpPagination-DbeACavM.js} +1 -1
- package/dist/chunks/LpPaletteTheme-CqWNRwHE.js +104 -0
- package/dist/chunks/{LpPasswordInput-GL2KOFK8.js → LpPasswordInput-BzAZsf5V.js} +4 -2
- package/dist/chunks/{LpPhoneInput-C60tx1tC.js → LpPhoneInput-BMpd-0QR.js} +2 -2
- package/dist/chunks/{LpPie-BgXZ3xmm.js → LpPie-DdFSTbGE.js} +4 -4
- package/dist/chunks/{LpPipeline-B31h41Mu.js → LpPipeline-zjEM1Lwc.js} +3 -3
- package/dist/chunks/{LpProgress-CE-m5OpD.js → LpProgress-BX33EKkN.js} +1 -1
- package/dist/chunks/{LpQuota-DaL8TgNe.js → LpQuota-DqNv1yjs.js} +2 -2
- package/dist/chunks/{LpRadioGroup-CGBr5nIp.js → LpRadioGroup-CsXeV9Ct.js} +4 -1
- package/dist/chunks/{LpRangeCalendar-c5qj7YcC.js → LpRangeCalendar-l-0nSFmY.js} +1 -1
- package/dist/chunks/{LpRangeSlider-Dv6lNvHY.js → LpRangeSlider-CM-0ZITZ.js} +3 -0
- package/dist/chunks/{LpRelativeTime-yin9uQP7.js → LpRelativeTime-UM8EkfrN.js} +1 -1
- package/dist/chunks/{LpRepeater-D0S7jjwr.js → LpRepeater-OvzByE5L.js} +2 -2
- package/dist/chunks/{LpSecretField-CfWyHv9m.js → LpSecretField-DwQiPOko.js} +9 -7
- package/dist/chunks/{LpSection-BW4xJ-Li.js → LpSection-DcQsieHG.js} +1 -1
- package/dist/chunks/{LpSegmented-BO99Sr4m.js → LpSegmented-D2VST0o3.js} +4 -1
- package/dist/chunks/{LpSelect-C_8cJtj6.js → LpSelect-DYX2-u5A.js} +36 -6
- package/dist/chunks/{LpSidebar-CZRXtvem.js → LpSidebar-D44peKq2.js} +3 -3
- package/dist/chunks/{LpSidebarNav-DBg1U1oW.js → LpSidebarNav--VwkXO_a.js} +3 -3
- package/dist/chunks/{LpSlider-DuoRpOrH.js → LpSlider-DxYoCJJO.js} +3 -0
- package/dist/chunks/{LpStat-DFKlPJgy.js → LpStat-DIsQfbeD.js} +3 -3
- package/dist/chunks/{LpStatus-BsGLrpor.js → LpStatus-BQU1TUsv.js} +1 -1
- package/dist/chunks/{LpStepMark-TrzOioa9.js → LpStepMark-CXa7DJy_.js} +1 -1
- package/dist/chunks/{LpStepper-BbnS5jUr.js → LpStepper-C9RM_yTG.js} +1 -1
- package/dist/chunks/{LpSwitch-DrzKrZZV.js → LpSwitch-bGZxUDSx.js} +3 -0
- package/dist/chunks/{LpTableFilter-DyvHGbOk.js → LpTableFilter-CTZMqFDU.js} +2 -2
- package/dist/chunks/{LpTableOfContents-rYvo8z5T.js → LpTableOfContents-CraicEs4.js} +2 -2
- package/dist/chunks/{LpTagsInput-DZg80W-B.js → LpTagsInput-DrjmrOge.js} +8 -5
- package/dist/chunks/{LpTextarea-DiWjeVoJ.js → LpTextarea-BbYqmG1S.js} +4 -1
- package/dist/chunks/{LpThemeStudio-DxVZMtzN.js → LpThemeStudio-B2nPaG-h.js} +18 -14
- package/dist/chunks/{LpThemeSwitcher-CadVBTBn.js → LpThemeSwitcher-C0A5sg_E.js} +5 -3
- package/dist/chunks/{LpTimeField-BFQVf_PW.js → LpTimeField-DQ8WvPr2.js} +4 -1
- package/dist/chunks/{LpToaster-Wv9ksj4q.js → LpToaster-BTTl_wuy.js} +2 -2
- package/dist/chunks/{LpTooltip-Cz8SQwRt.js → LpTooltip-BcBvIx4z.js} +23 -5
- package/dist/chunks/{LpTopologyCanvas-C17A1XCb.js → LpTopologyCanvas-B0wEvAoq.js} +1 -1
- package/dist/chunks/{LpTour-CY2bFXge.js → LpTour-BgCfNlaW.js} +1 -1
- package/dist/chunks/{LpTruncate-D_5Pfv5B.js → LpTruncate-TcqfkyJM.js} +2 -2
- package/dist/chunks/{LpUptimeBar-Tew1Ykx4.js → LpUptimeBar-DRadGq4Y.js} +136 -47
- package/dist/chunks/{MenuTitle-BjQZX8tQ.js → MenuTitle-Dab3EGOp.js} +1 -1
- package/dist/chunks/{OptionContent-CmYUUeWj.js → OptionContent-DPzNsybd.js} +1 -1
- package/dist/chunks/PalettePreviewBoundary-B0Hhu3Yw.js +20 -0
- package/dist/chunks/{PickerField-DoHv0WMH.js → PickerField-DwLw7j0L.js} +1 -1
- package/dist/chunks/{RevealToggle-C-KzaQGJ.js → RevealToggle-BVFM_FAU.js} +3 -3
- package/dist/chunks/{TourCard-BJ8i7dk0.js → TourCard--2IfgnUU.js} +2 -2
- package/dist/chunks/icons-CLu1iR1j.js +43 -0
- package/dist/chunks/palette-BiY7yQZL.js +29 -0
- 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.d.ts +1 -1
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.d.ts +3 -3
- package/dist/components/LpAlert.vue.js +1 -1
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpAutocomplete.vue.d.ts +7 -1
- package/dist/components/LpAutocomplete.vue.js +1 -1
- package/dist/components/LpAvatar.vue.js +1 -1
- package/dist/components/LpBadge.vue.d.ts +2 -2
- package/dist/components/LpBadge.vue.js +1 -1
- package/dist/components/LpBreadcrumbs.vue.js +1 -1
- package/dist/components/LpButton.vue.d.ts +4 -0
- package/dist/components/LpButton.vue.js +1 -1
- package/dist/components/LpCalendar.vue.js +1 -1
- package/dist/components/LpChart.vue.js +2 -2
- package/dist/components/LpCheckbox.vue.d.ts +8 -1
- 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 +2 -2
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.d.ts +43 -7
- package/dist/components/LpCommandPalette.vue.js +451 -126
- package/dist/components/LpCommandScope.vue.d.ts +28 -0
- package/dist/components/LpCommandScope.vue.js +4 -0
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpConfirmHost.vue.js +1 -1
- package/dist/components/LpContextMenu.vue.d.ts +5 -1
- package/dist/components/LpContextMenu.vue.js +1 -1
- package/dist/components/LpCopyButton.vue.d.ts +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.d.ts +4 -0
- package/dist/components/LpDisclosure.vue.js +1 -1
- package/dist/components/LpDock.vue.js +2 -2
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpDropdownMenu.vue.d.ts +5 -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.js +1 -1
- package/dist/components/LpFieldNotice.vue.js +1 -1
- package/dist/components/LpFileTree.vue.d.ts +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 +3 -2
- package/dist/components/LpInfraNode.vue.js +1 -1
- package/dist/components/LpInput.vue.d.ts +6 -0
- package/dist/components/LpInput.vue.js +1 -1
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpLoader.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.d.ts +2 -2
- 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.d.ts +6 -0
- 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.d.ts +1 -0
- package/dist/components/LpPaletteFacts.vue.js +40 -26
- package/dist/components/LpPaletteHeader.vue.js +2 -2
- package/dist/components/LpPaletteStats.vue.d.ts +1 -0
- package/dist/components/LpPaletteStats.vue.js +45 -21
- package/dist/components/LpPaletteTheme.vue.d.ts +7 -0
- package/dist/components/LpPaletteTheme.vue.js +4 -0
- 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.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.d.ts +6 -1
- package/dist/components/LpRadioGroup.vue.js +1 -1
- package/dist/components/LpRangeCalendar.vue.js +1 -1
- package/dist/components/LpRangeSlider.vue.d.ts +6 -0
- 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/LpSecretField.vue.js +1 -1
- package/dist/components/LpSection.vue.js +1 -1
- package/dist/components/LpSegmented.vue.d.ts +6 -0
- package/dist/components/LpSegmented.vue.js +1 -1
- package/dist/components/LpSelect.vue.d.ts +3 -0
- package/dist/components/LpSelect.vue.js +1 -1
- package/dist/components/LpShinyText.vue.d.ts +1 -1
- package/dist/components/LpShortcutsDialog.vue.js +1 -1
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.vue.js +1 -1
- package/dist/components/LpSlider.vue.d.ts +6 -0
- package/dist/components/LpSlider.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/LpSwitch.vue.d.ts +6 -0
- package/dist/components/LpSwitch.vue.js +1 -1
- package/dist/components/LpTable.vue.d.ts +6 -0
- package/dist/components/LpTable.vue.js +74 -35
- package/dist/components/LpTableFilter.vue.js +1 -1
- package/dist/components/LpTableOfContents.vue.js +1 -1
- package/dist/components/LpTabs.vue.d.ts +1 -1
- package/dist/components/LpTabs.vue.js +14 -5
- package/dist/components/LpTagsInput.vue.d.ts +6 -0
- package/dist/components/LpTagsInput.vue.js +1 -1
- package/dist/components/LpTextarea.vue.d.ts +8 -1
- package/dist/components/LpTextarea.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.d.ts +6 -0
- 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.js +1 -1
- package/dist/components/LpTour.vue.js +1 -1
- package/dist/components/LpTruncate.vue.js +1 -1
- package/dist/components/LpUptimeBar.vue.d.ts +18 -9
- 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/PalettePreviewBoundary.vue.d.ts +13 -0
- package/dist/components/PalettePreviewBoundary.vue.js +4 -0
- 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/commandPalette.d.ts +33 -0
- package/dist/components/commandPalette.js +21 -1
- package/dist/components/commandSearch.d.ts +5 -0
- package/dist/components/commandSearch.js +22 -2
- package/dist/components/format.d.ts +7 -0
- package/dist/components/format.js +4 -0
- package/dist/components/menuWhen.js +1 -1
- package/dist/components/uptimeBar.d.ts +45 -0
- package/dist/components/uptimeBar.js +51 -0
- package/dist/composables/scroll.d.ts +5 -0
- package/dist/composables/scroll.js +7 -0
- package/dist/composables/useField.d.ts +4 -0
- package/dist/composables/useField.js +3 -0
- package/dist/composables/usePaletteCommands.d.ts +61 -0
- package/dist/composables/usePaletteCommands.js +226 -0
- package/dist/composables/useReveal.js +21 -0
- package/dist/composables/useView.js +5 -1
- package/dist/icons-all.js +1 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +101 -85
- package/dist/theme/palette.d.ts +9 -0
- package/dist/theme/studio.d.ts +12 -1
- package/package.json +1 -1
- package/src/component-names.ts +2 -0
- package/src/components/LpAutocomplete.vue +6 -1
- package/src/components/LpButton.vue +5 -1
- package/src/components/LpCheckbox.vue +7 -2
- package/src/components/LpCommandPalette.vue +263 -29
- package/src/components/LpCommandScope.vue +35 -0
- package/src/components/LpContextMenu.vue +13 -2
- package/src/components/LpDisclosure.vue +5 -1
- package/src/components/LpDropdownMenu.vue +13 -2
- package/src/components/LpFormField.vue +3 -0
- package/src/components/LpInput.vue +6 -1
- package/src/components/LpNumberField.vue +6 -1
- package/src/components/LpNumberFlow.vue +12 -2
- package/src/components/LpPaletteFacts.vue +11 -6
- package/src/components/LpPaletteStats.vue +12 -5
- package/src/components/LpPaletteTheme.vue +48 -0
- package/src/components/LpPasswordInput.vue +3 -0
- package/src/components/LpRadioGroup.vue +4 -1
- package/src/components/LpRangeSlider.vue +6 -1
- package/src/components/LpSecretField.vue +3 -0
- package/src/components/LpSegmented.vue +6 -1
- package/src/components/LpSelect.vue +41 -2
- package/src/components/LpSlider.vue +6 -1
- package/src/components/LpSwitch.vue +6 -1
- package/src/components/LpTable.vue +35 -2
- package/src/components/LpTableOfContents.vue +2 -4
- package/src/components/LpTabs.vue +7 -0
- package/src/components/LpTagsInput.vue +6 -1
- package/src/components/LpTextarea.vue +7 -2
- package/src/components/LpThemeStudio.vue +7 -5
- package/src/components/LpThemeSwitcher.vue +3 -0
- package/src/components/LpTimeField.vue +6 -1
- package/src/components/LpTooltip.vue +23 -1
- package/src/components/LpUptimeBar.vue +69 -41
- package/src/components/PalettePreviewBoundary.vue +5 -0
- package/src/components/commandPalette.ts +22 -1
- package/src/components/commandSearch.ts +23 -1
- package/src/components/format.ts +10 -0
- package/src/components/uptimeBar.ts +98 -0
- package/src/composables/scroll.ts +11 -0
- package/src/composables/useField.ts +7 -0
- package/src/composables/usePaletteCommands.ts +226 -0
- package/src/composables/useReveal.ts +29 -0
- package/src/composables/useView.ts +5 -1
- package/src/index.ts +18 -1
- package/src/kit-icons.ts +1 -1
- package/src/theme/palette.ts +26 -0
- package/src/theme/studio.ts +15 -8
- package/dist/assets/LpTabs-BJ-nrJuI.css +0 -18
- package/dist/chunks/icons-4IywzkHk.js +0 -43
|
@@ -5,7 +5,10 @@ export default { inheritAttrs: false }
|
|
|
5
5
|
</script>
|
|
6
6
|
|
|
7
7
|
<script setup lang="ts" generic="T extends Record<string, unknown>">
|
|
8
|
-
import { computed, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, useAttrs, watch } from "vue"
|
|
8
|
+
import { computed, h, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, useAttrs, useId, watch } from "vue"
|
|
9
|
+
import { suppressPaletteDiscovery, usePaletteCommands, usePaletteScope } from "../composables/usePaletteCommands"
|
|
10
|
+
import { menuPaletteCommands } from "./commandPalette"
|
|
11
|
+
import LpDataList from "./LpDataList.vue"
|
|
9
12
|
import LpCheckbox from "./LpCheckbox.vue"
|
|
10
13
|
import LpContextMenu, { type ContextMenuItemDef } from "./LpContextMenu.vue"
|
|
11
14
|
import LpEmptyState from "./LpEmptyState.vue"
|
|
@@ -157,6 +160,8 @@ type RowKey = string | number
|
|
|
157
160
|
|
|
158
161
|
const props = withDefaults(
|
|
159
162
|
defineProps<{
|
|
163
|
+
/** Publish loaded rows and existing row actions to the enclosing command scope. */
|
|
164
|
+
palette?: false | { id?: string; label?: string; labelKey?: keyof T }
|
|
160
165
|
columns: TableColumn<T>[]
|
|
161
166
|
/** The rows, for a table that holds its data. Ignored when `load` is set. */
|
|
162
167
|
rows?: T[]
|
|
@@ -245,7 +250,7 @@ const props = withDefaults(
|
|
|
245
250
|
*/
|
|
246
251
|
allMatching?: boolean
|
|
247
252
|
}>(),
|
|
248
|
-
{
|
|
253
|
+
{ palette: undefined,
|
|
249
254
|
rows: () => [],
|
|
250
255
|
pageSize: 25,
|
|
251
256
|
skeletonRows: 5,
|
|
@@ -1075,6 +1080,34 @@ onBeforeUnmount(() => {
|
|
|
1075
1080
|
const barInsetTop = computed(() =>
|
|
1076
1081
|
props.stickyHeader && headHeight.value ? `${headHeight.value}px` : undefined,
|
|
1077
1082
|
)
|
|
1083
|
+
|
|
1084
|
+
const paletteScope = usePaletteScope()
|
|
1085
|
+
const paletteId = useId()
|
|
1086
|
+
suppressPaletteDiscovery()
|
|
1087
|
+
usePaletteCommands(() => {
|
|
1088
|
+
if (props.palette === false) return []
|
|
1089
|
+
const name = props.palette?.label ?? attrs["aria-label"] as string | undefined
|
|
1090
|
+
if (!name) return []
|
|
1091
|
+
const id = `${paletteScope?.id}/${props.palette?.id ?? paletteId}`
|
|
1092
|
+
return [{
|
|
1093
|
+
id, label: name, icon: "lucide:table", children: [
|
|
1094
|
+
...menuPaletteCommands(tableMenu(), `${id}/table`),
|
|
1095
|
+
...displayRows.value.map((row, index) => {
|
|
1096
|
+
const menu = resolveRowMenu(props.rowMenu?.(row))
|
|
1097
|
+
const rowId = `${id}/row/${keyFor(row, index)}`
|
|
1098
|
+
const labelKey = props.palette && props.palette.labelKey || props.columns[0]?.key
|
|
1099
|
+
return {
|
|
1100
|
+
id: rowId, label: menu.title ?? String(labelKey ? row[labelKey] ?? keyFor(row, index) : keyFor(row, index)),
|
|
1101
|
+
description: menu.subtitle, icon: menu.icon ?? "lucide:rows-3", image: menu.image,
|
|
1102
|
+
onSelect: () => emit("rowClick", row), actions: menuPaletteCommands(menu.items, rowId),
|
|
1103
|
+
previewComponent: { setup: () => () => h(LpDataList, { items: props.columns.map(column => ({
|
|
1104
|
+
label: column.label, value: typeof row[column.key] === "object" ? null : String(row[column.key] ?? ""),
|
|
1105
|
+
})) }) },
|
|
1106
|
+
}
|
|
1107
|
+
}),
|
|
1108
|
+
],
|
|
1109
|
+
}]
|
|
1110
|
+
}, { automatic: true })
|
|
1078
1111
|
</script>
|
|
1079
1112
|
|
|
1080
1113
|
<template>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { Motion } from "motion-v"
|
|
13
13
|
import { computed, onBeforeUnmount, onMounted, ref, useId, watch } from "vue"
|
|
14
14
|
import { useLabels } from "../composables/useLpConfig"
|
|
15
|
-
import { scrollParent } from "../composables/scroll"
|
|
15
|
+
import { atScrollEnd, scrollParent } from "../composables/scroll"
|
|
16
16
|
import { usePillTransition } from "../composables/usePillTransition"
|
|
17
17
|
|
|
18
18
|
export interface TocLink {
|
|
@@ -112,9 +112,7 @@ function setup() {
|
|
|
112
112
|
const line = Math.max(props.offset, view / 4)
|
|
113
113
|
for (const el of headings) if (el.getBoundingClientRect().top - top <= line + 1) current = el.id
|
|
114
114
|
const scrolled = scroller ? scroller.scrollTop : window.scrollY
|
|
115
|
-
const end =
|
|
116
|
-
? scroller.scrollTop + scroller.clientHeight >= scroller.scrollHeight - 2
|
|
117
|
-
: window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 2
|
|
115
|
+
const end = atScrollEnd(headings[0])
|
|
118
116
|
if (scrolled > 0 && end) current = headings[headings.length - 1]!.id
|
|
119
117
|
activeId.value = current
|
|
120
118
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { usePaletteCommands, usePaletteScope } from "../composables/usePaletteCommands"
|
|
2
3
|
import { Motion } from "motion-v"
|
|
3
4
|
import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "reka-ui"
|
|
4
5
|
import { computed, nextTick, ref, useId, watch } from "vue"
|
|
@@ -152,6 +153,12 @@ function releaseFocusAfterPointer(event: PointerEvent) {
|
|
|
152
153
|
const target = event.currentTarget
|
|
153
154
|
if (target instanceof HTMLElement) target.blur()
|
|
154
155
|
}
|
|
156
|
+
|
|
157
|
+
const paletteScope = usePaletteScope()
|
|
158
|
+
usePaletteCommands(() => props.items.map(item => ({
|
|
159
|
+
id: `${paletteScope?.id}/tabs/${pillId}/${item.value}`, label: props.label ? `${props.label}: ${item.label}` : item.label,
|
|
160
|
+
icon: item.icon, disabled: item.disabled, onSelect: () => select(item.value),
|
|
161
|
+
})), { automatic: true })
|
|
155
162
|
</script>
|
|
156
163
|
|
|
157
164
|
<template>
|
|
@@ -13,6 +13,7 @@ export type TagsInputLabels = {
|
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
15
|
<script setup lang="ts">
|
|
16
|
+
import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
|
|
16
17
|
/*
|
|
17
18
|
* Free-form list of short values: emails, labels, hostnames. Typing a
|
|
18
19
|
* delimiter or Enter turns the text into a tag, a paste is split into many,
|
|
@@ -56,6 +57,8 @@ import { useOutsideGuard, usePortalTarget } from "../composables/useView"
|
|
|
56
57
|
|
|
57
58
|
const props = withDefaults(
|
|
58
59
|
defineProps<{
|
|
60
|
+
/** Contextual palette metadata; false excludes this control. */
|
|
61
|
+
palette?: PaletteControlOptions
|
|
59
62
|
modelValue?: string[]
|
|
60
63
|
placeholder?: string
|
|
61
64
|
/** Characters (or a RegExp) that end a tag while typing and split a paste. */
|
|
@@ -99,7 +102,7 @@ const props = withDefaults(
|
|
|
99
102
|
/** Box height and text size, as LpInput. */
|
|
100
103
|
size?: "sm" | "md" | "lg"
|
|
101
104
|
}>(),
|
|
102
|
-
{
|
|
105
|
+
{ palette: undefined,
|
|
103
106
|
modelValue: () => [],
|
|
104
107
|
delimiter: () => [",", "\n", "\t"],
|
|
105
108
|
duplicates: "reject",
|
|
@@ -424,6 +427,8 @@ const portalTo = usePortalTarget()
|
|
|
424
427
|
// What happens in another document (the page around a frame, or a frame in
|
|
425
428
|
// the page) is not "outside" for this layer.
|
|
426
429
|
const guardOutside = useOutsideGuard()
|
|
430
|
+
|
|
431
|
+
usePaletteControl({ label: () => props.label, disabled: () => field.value.disabled, invalid: () => field.value.invalid, editorProps: () => ({ label: undefined, hint: undefined, error: undefined }) })
|
|
427
432
|
</script>
|
|
428
433
|
|
|
429
434
|
<template>
|
|
@@ -3,12 +3,15 @@ export default { inheritAttrs: false }
|
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<script setup lang="ts">
|
|
6
|
+
import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
|
|
6
7
|
import { computed, useAttrs } from "vue"
|
|
7
8
|
import { useFieldControl } from "../composables/useField"
|
|
8
9
|
import { mergeClass } from "../composables/useMergedClass"
|
|
9
10
|
import FieldWrapper from "./FieldWrapper.vue"
|
|
10
11
|
|
|
11
|
-
const props = defineProps<{
|
|
12
|
+
const props = withDefaults(defineProps<{
|
|
13
|
+
/** Contextual palette metadata; false excludes this control. */
|
|
14
|
+
palette?: PaletteControlOptions
|
|
12
15
|
modelValue?: string
|
|
13
16
|
placeholder?: string
|
|
14
17
|
rows?: number
|
|
@@ -22,7 +25,7 @@ const props = defineProps<{
|
|
|
22
25
|
* the <textarea> for native validation.
|
|
23
26
|
*/
|
|
24
27
|
required?: boolean
|
|
25
|
-
}>()
|
|
28
|
+
}>(), { palette: undefined })
|
|
26
29
|
|
|
27
30
|
defineEmits<{ (e: "update:modelValue", value: string): void }>()
|
|
28
31
|
|
|
@@ -53,6 +56,8 @@ const textareaClass = computed(() =>
|
|
|
53
56
|
hasField.value ? "" : (attrs.class as string),
|
|
54
57
|
),
|
|
55
58
|
)
|
|
59
|
+
|
|
60
|
+
usePaletteControl({ label: () => props.label, disabled: () => field.value.disabled, invalid: () => field.value.invalid, editorProps: () => ({ label: undefined, hint: undefined, error: undefined }) })
|
|
56
61
|
</script>
|
|
57
62
|
|
|
58
63
|
<template>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { usePaletteTheme } from "../theme/palette"
|
|
2
3
|
/*
|
|
3
4
|
* The theme, tuned in place: a base theme, any brand colour, corners, control
|
|
4
5
|
* size and motion speed, all applied live, and the result ready to take away
|
|
@@ -102,8 +103,9 @@ const TILE =
|
|
|
102
103
|
"flex min-w-0 flex-col items-center gap-1 rounded-control p-1.5 text-[11px] text-muted transition-colors duration-(--duration-fast) hover:bg-surface-soft hover:text-ink"
|
|
103
104
|
const SWATCH = "relative size-8 overflow-hidden rounded-full ring-2 transition-[box-shadow] duration-(--duration-fast)"
|
|
104
105
|
|
|
105
|
-
const tuneTo = <K extends keyof Omit<StudioState, "theme">>(key: K) =>
|
|
106
|
-
|
|
106
|
+
const tuneTo = <K extends keyof Omit<StudioState, "theme">>(key: K, value: string) => studio.tune({ [key]: value } as Partial<StudioState>)
|
|
107
|
+
|
|
108
|
+
usePaletteTheme(() => studio ?? undefined)
|
|
107
109
|
</script>
|
|
108
110
|
|
|
109
111
|
<template>
|
|
@@ -171,7 +173,7 @@ const tuneTo = <K extends keyof Omit<StudioState, "theme">>(key: K) =>
|
|
|
171
173
|
:model-value="state.corners"
|
|
172
174
|
size="sm"
|
|
173
175
|
:options="[{ value: 'sharp', label: text.sharp }, { value: 'default', label: text.default }, { value: 'round', label: text.round }]"
|
|
174
|
-
@update:model-value="tuneTo('corners')"
|
|
176
|
+
@update:model-value="tuneTo('corners', $event)"
|
|
175
177
|
/>
|
|
176
178
|
</LpThemeStudioSection>
|
|
177
179
|
|
|
@@ -180,7 +182,7 @@ const tuneTo = <K extends keyof Omit<StudioState, "theme">>(key: K) =>
|
|
|
180
182
|
:model-value="state.size"
|
|
181
183
|
size="sm"
|
|
182
184
|
:options="[{ value: 'compact', label: text.compact }, { value: 'default', label: text.default }, { value: 'comfortable', label: text.comfortable }]"
|
|
183
|
-
@update:model-value="tuneTo('size')"
|
|
185
|
+
@update:model-value="tuneTo('size', $event)"
|
|
184
186
|
/>
|
|
185
187
|
</LpThemeStudioSection>
|
|
186
188
|
|
|
@@ -189,7 +191,7 @@ const tuneTo = <K extends keyof Omit<StudioState, "theme">>(key: K) =>
|
|
|
189
191
|
:model-value="state.motion"
|
|
190
192
|
size="sm"
|
|
191
193
|
:options="[{ value: 'fast', label: text.fast }, { value: 'normal', label: text.normal }, { value: 'slow', label: text.slow }]"
|
|
192
|
-
@update:model-value="tuneTo('motion')"
|
|
194
|
+
@update:model-value="tuneTo('motion', $event)"
|
|
193
195
|
/>
|
|
194
196
|
</LpThemeStudioSection>
|
|
195
197
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { usePaletteTheme } from "../theme/palette"
|
|
2
3
|
/*
|
|
3
4
|
* Theme switcher. The kit convention for theming, so consumers stop hand-rolling
|
|
4
5
|
* their own. Default behaviour mirrors LeaveHosting: LEFT-click cycles to the
|
|
@@ -298,6 +299,8 @@ const labelName = computed(() =>
|
|
|
298
299
|
const triggerLabel = computed(
|
|
299
300
|
() => props["aria-label"] ?? text.value.trigger.replace("{name}", labelName.value),
|
|
300
301
|
)
|
|
302
|
+
|
|
303
|
+
usePaletteTheme(() => tuner ?? undefined)
|
|
301
304
|
</script>
|
|
302
305
|
|
|
303
306
|
<template>
|
|
@@ -6,6 +6,7 @@ export default { inheritAttrs: false }
|
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<script setup lang="ts">
|
|
9
|
+
import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
|
|
9
10
|
/*
|
|
10
11
|
* Time-of-day field: hour and minute segments, typed or stepped with the arrow
|
|
11
12
|
* keys (reka TimeField). The model is "HH:mm" (v-model), the same shape a
|
|
@@ -26,6 +27,8 @@ import LpIcon from "./LpIcon.vue"
|
|
|
26
27
|
|
|
27
28
|
const props = withDefaults(
|
|
28
29
|
defineProps<{
|
|
30
|
+
/** Contextual palette metadata; false excludes this control. */
|
|
31
|
+
palette?: PaletteControlOptions
|
|
29
32
|
/** Selected time "HH:mm" (v-model). */
|
|
30
33
|
modelValue?: string
|
|
31
34
|
disabled?: boolean
|
|
@@ -38,7 +41,7 @@ const props = withDefaults(
|
|
|
38
41
|
/** Show the clock icon at the start of the field. */
|
|
39
42
|
icon?: boolean
|
|
40
43
|
}>(),
|
|
41
|
-
{ size: "md", hourCycle: 24, icon: true },
|
|
44
|
+
{ palette: undefined, size: "md", hourCycle: 24, icon: true },
|
|
42
45
|
)
|
|
43
46
|
|
|
44
47
|
const emit = defineEmits<{
|
|
@@ -84,6 +87,8 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
84
87
|
field.value.invalid ? "border-danger focus-within:ring-danger-soft" : "border-line focus-within:border-brand",
|
|
85
88
|
].join(" "),
|
|
86
89
|
)
|
|
90
|
+
|
|
91
|
+
usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.value.invalid })
|
|
87
92
|
</script>
|
|
88
93
|
|
|
89
94
|
<template>
|
|
@@ -120,6 +120,27 @@ const PassThrough: FunctionalComponent = (_, { slots }) => slots.default?.()
|
|
|
120
120
|
|
|
121
121
|
const target = { x: 0, y: 0 }
|
|
122
122
|
const offset = ref({ x: 0, y: 0 })
|
|
123
|
+
const tipBody = ref<HTMLElement | null>(null)
|
|
124
|
+
|
|
125
|
+
/* reka's own collision padding, which the follow offset would otherwise undo. */
|
|
126
|
+
const EDGE = 8
|
|
127
|
+
|
|
128
|
+
/*
|
|
129
|
+
* reka keeps the tip inside the window for the box it was placed against; the
|
|
130
|
+
* follow offset is added after that, so near the right edge a wide tip (an
|
|
131
|
+
* uptime slice with its breakdown) slid half off screen. The offset is held to
|
|
132
|
+
* the room the window has on each side of where reka put the tip.
|
|
133
|
+
*/
|
|
134
|
+
function inView(x: number): number {
|
|
135
|
+
const panel = tipBody.value?.parentElement
|
|
136
|
+
if (!panel) return x
|
|
137
|
+
const rect = panel.getBoundingClientRect()
|
|
138
|
+
const placed = rect.left - offset.value.x
|
|
139
|
+
const width = panel.ownerDocument.defaultView?.innerWidth ?? window.innerWidth
|
|
140
|
+
const min = EDGE - placed
|
|
141
|
+
const max = width - EDGE - rect.width - placed
|
|
142
|
+
return max < min ? x : Math.min(max, Math.max(min, x))
|
|
143
|
+
}
|
|
123
144
|
|
|
124
145
|
/*
|
|
125
146
|
* The spring constants come from the kit's shared "float" feel — the same ones
|
|
@@ -172,6 +193,7 @@ const onPointerMove = (e: PointerEvent) => {
|
|
|
172
193
|
const half = props.side === "top" || props.side === "bottom" ? rect.height / 2 : 0
|
|
173
194
|
const dir = props.side === "bottom" ? -1 : 1
|
|
174
195
|
target.y = e.clientY - (rect.top + rect.height / 2) + half * dir
|
|
196
|
+
target.x = inView(target.x)
|
|
175
197
|
// Without motion the tip goes straight to the reading instead of trailing.
|
|
176
198
|
if (prefersReducedMotion()) {
|
|
177
199
|
offset.value = { x: target.x, y: target.y }
|
|
@@ -272,7 +294,7 @@ const guardOutside = useOutsideGuard()
|
|
|
272
294
|
]"
|
|
273
295
|
:style="followCursor ? { translate: `${offset.x}px ${offset.y}px` } : undefined"
|
|
274
296
|
>
|
|
275
|
-
<slot name="content">{{ content }}</slot>
|
|
297
|
+
<div ref="tipBody"><slot name="content">{{ content }}</slot></div>
|
|
276
298
|
</TooltipContent>
|
|
277
299
|
</TooltipPortal>
|
|
278
300
|
</TooltipRoot>
|
|
@@ -3,27 +3,24 @@
|
|
|
3
3
|
// appended to it, so opt out of the automatic pass-through that would add it
|
|
4
4
|
// a second time, unmerged.
|
|
5
5
|
export default { inheritAttrs: false }
|
|
6
|
+
export type { UptimeEvent, UptimeSegment, UptimeStatus } from "./uptimeBar"
|
|
6
7
|
</script>
|
|
7
8
|
|
|
8
9
|
<script setup lang="ts">
|
|
9
10
|
import { computed, ref } from "vue"
|
|
10
11
|
import LpNumberFlow from "./LpNumberFlow.vue"
|
|
11
12
|
import LpRollingText from "./LpRollingText.vue"
|
|
13
|
+
import LpShift from "./LpShift.vue"
|
|
12
14
|
import LpSkeleton from "./LpSkeleton.vue"
|
|
13
15
|
import LpSwap from "./LpSwap.vue"
|
|
14
16
|
import LpTooltip from "./LpTooltip.vue"
|
|
15
17
|
import { TONES } from "./tone"
|
|
16
18
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
17
19
|
import { useLabels } from "../composables/useLpConfig"
|
|
20
|
+
import { useLocale } from "../composables/useLocale"
|
|
21
|
+
import { DEFAULT_UNIT_LABELS, formatTime, toEpoch, type TimeUnitLabels } from "./timeSpan"
|
|
22
|
+
import { UPTIME_SEVERITY, uptimeFigure, uptimeRange, uptimeShare, type UptimeSegment, type UptimeStatus } from "./uptimeBar"
|
|
18
23
|
|
|
19
|
-
export type UptimeStatus = "operational" | "degraded" | "down" | "maintenance" | "empty"
|
|
20
|
-
|
|
21
|
-
export interface UptimeSegment {
|
|
22
|
-
/** Health of this slice. `empty` renders a faded placeholder (no data). */
|
|
23
|
-
status: UptimeStatus
|
|
24
|
-
/** Optional rich tooltip body; falls back to a humanised status. */
|
|
25
|
-
label?: string
|
|
26
|
-
}
|
|
27
24
|
|
|
28
25
|
const props = withDefaults(
|
|
29
26
|
defineProps<{
|
|
@@ -35,6 +32,13 @@ const props = withDefaults(
|
|
|
35
32
|
endLabel?: string
|
|
36
33
|
/** Show the computed uptime % above the bar. */
|
|
37
34
|
showUptime?: boolean
|
|
35
|
+
/**
|
|
36
|
+
* The uptime figure, when the app has a more exact one than the slices
|
|
37
|
+
* (a server that counted every minute). Otherwise it is worked out from
|
|
38
|
+
* the slices: their `durations`, else their spans, else their count.
|
|
39
|
+
*/
|
|
40
|
+
uptime?: number | null
|
|
41
|
+
locale?: string
|
|
38
42
|
/** Override the headline shown next to the uptime %. */
|
|
39
43
|
title?: string
|
|
40
44
|
/**
|
|
@@ -44,7 +48,7 @@ const props = withDefaults(
|
|
|
44
48
|
*/
|
|
45
49
|
colors?: Partial<Record<UptimeStatus, string>>
|
|
46
50
|
/** Status names, for the tooltip and screen readers. */
|
|
47
|
-
labels?: Partial<Record<UptimeStatus | "loading", string>>
|
|
51
|
+
labels?: Partial<Record<UptimeStatus | "loading" | "uptime", string>>
|
|
48
52
|
/**
|
|
49
53
|
* The history is on its way. The row of ticks pulses in place of the
|
|
50
54
|
* slices (as many as `segments` already holds, else `skeletonSlices`) at
|
|
@@ -88,26 +92,18 @@ const statusLabel = useLabels(
|
|
|
88
92
|
maintenance: "Maintenance",
|
|
89
93
|
empty: "No data",
|
|
90
94
|
loading: "Loading",
|
|
95
|
+
uptime: "uptime",
|
|
91
96
|
},
|
|
92
97
|
() => props.labels,
|
|
93
98
|
)
|
|
94
99
|
|
|
95
100
|
const skeletonCount = computed(() => props.segments.length || props.skeletonSlices)
|
|
96
101
|
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const up = tracked.filter((s) => s.status === "operational" || s.status === "maintenance").length
|
|
102
|
-
return (up / tracked.length) * 100
|
|
103
|
-
})
|
|
102
|
+
const locale = useLocale(() => props.locale)
|
|
103
|
+
const units = useLabels<TimeUnitLabels>("time", DEFAULT_UNIT_LABELS)
|
|
104
|
+
|
|
105
|
+
const uptimePct = computed(() => (props.uptime ?? null) !== null ? props.uptime! : uptimeShare(props.segments))
|
|
104
106
|
|
|
105
|
-
/*
|
|
106
|
-
* Places actually needed, so trailing zeros stay trimmed: 100 rather than
|
|
107
|
-
* 100.00, 99.9 rather than 99.90. The counter needs a fixed count per render —
|
|
108
|
-
* it cannot "trim" mid-roll without changing the row's shape — so it is derived
|
|
109
|
-
* from the value instead of hard-coded at two.
|
|
110
|
-
*/
|
|
111
107
|
/*
|
|
112
108
|
* The gap is drawn INSIDE each slice, not between slices.
|
|
113
109
|
*
|
|
@@ -136,7 +132,24 @@ const tip = computed(() => {
|
|
|
136
132
|
if (i == null) return null
|
|
137
133
|
const seg = props.segments[i]
|
|
138
134
|
if (!seg) return null
|
|
139
|
-
return {
|
|
135
|
+
return {
|
|
136
|
+
status: seg.status,
|
|
137
|
+
text: seg.label ?? statusLabel.value[seg.status],
|
|
138
|
+
range: seg.start !== undefined ? uptimeRange(seg.start, seg.end, locale.value) : "",
|
|
139
|
+
parts: UPTIME_SEVERITY.filter((st) => (seg.durations?.[st] ?? 0) > 0).map((st) => ({
|
|
140
|
+
status: st,
|
|
141
|
+
text: statusLabel.value[st],
|
|
142
|
+
time: formatTime(seg.durations![st]!, "units", units.value, 2),
|
|
143
|
+
})),
|
|
144
|
+
events: (seg.events ?? []).slice(0, 4).map((e) => ({
|
|
145
|
+
status: e.status ?? seg.status,
|
|
146
|
+
text: e.text,
|
|
147
|
+
at: e.at !== undefined && Number.isFinite(toEpoch(e.at))
|
|
148
|
+
? new Intl.DateTimeFormat(locale.value, { hour: "2-digit", minute: "2-digit" }).format(toEpoch(e.at))
|
|
149
|
+
: "",
|
|
150
|
+
})),
|
|
151
|
+
more: Math.max(0, (seg.events?.length ?? 0) - 4),
|
|
152
|
+
}
|
|
140
153
|
})
|
|
141
154
|
|
|
142
155
|
/*
|
|
@@ -165,12 +178,7 @@ function onSlotKeydown(e: KeyboardEvent, i: number) {
|
|
|
165
178
|
slots.value[next]?.focus()
|
|
166
179
|
}
|
|
167
180
|
|
|
168
|
-
const
|
|
169
|
-
const v = uptimePct.value
|
|
170
|
-
if (v == null) return 0
|
|
171
|
-
const trimmed = Number(v.toFixed(2))
|
|
172
|
-
return Number.isInteger(trimmed) ? 0 : String(trimmed).split(".")[1].length
|
|
173
|
-
})
|
|
181
|
+
const figure = computed(() => (uptimePct.value == null ? null : uptimeFigure(uptimePct.value)))
|
|
174
182
|
|
|
175
183
|
// The bar sets its own gap between the strip and its caption, which a compact status row has to close.
|
|
176
184
|
const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
@@ -210,13 +218,32 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
210
218
|
</LpSkeleton>
|
|
211
219
|
<LpTooltip v-else :delay="80" follow-cursor side="top" :disabled="hovered === null">
|
|
212
220
|
<template #content>
|
|
213
|
-
<
|
|
214
|
-
<span
|
|
215
|
-
class="
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
221
|
+
<LpShift v-if="tip" axis="both" class="flex">
|
|
222
|
+
<span class="flex min-w-36 flex-col gap-1">
|
|
223
|
+
<span class="flex items-center gap-1.5">
|
|
224
|
+
<span
|
|
225
|
+
class="size-1.5 shrink-0 rounded-full transition-colors duration-[var(--duration-fast)]"
|
|
226
|
+
:style="{ backgroundColor: colorFor(tip.status) }"
|
|
227
|
+
/>
|
|
228
|
+
<LpRollingText :value="tip.text" :duration="180" align="start" class="font-medium" />
|
|
229
|
+
</span>
|
|
230
|
+
<LpRollingText v-if="tip.range" :value="tip.range" :duration="180" align="start" class="text-[0.7rem] opacity-70 tabular-nums" />
|
|
231
|
+
<span v-if="tip.parts.length > 1 || (tip.parts.length === 1 && tip.parts[0].status !== tip.status)" class="mt-0.5 flex flex-col gap-0.5">
|
|
232
|
+
<span v-for="part in tip.parts" :key="part.status" class="flex items-center gap-1.5 tabular-nums">
|
|
233
|
+
<span class="size-1.5 shrink-0 rounded-full" :style="{ backgroundColor: colorFor(part.status) }" />
|
|
234
|
+
<span class="flex-1">{{ part.text }}</span>
|
|
235
|
+
<LpRollingText :value="part.time" :duration="180" align="end" class="opacity-80" />
|
|
236
|
+
</span>
|
|
237
|
+
</span>
|
|
238
|
+
<span v-if="tip.events.length" class="mt-0.5 flex flex-col gap-0.5 border-t border-current/15 pt-1">
|
|
239
|
+
<span v-for="(event, n) in tip.events" :key="n" class="flex items-baseline gap-1.5">
|
|
240
|
+
<LpRollingText v-if="event.at" :value="event.at" :duration="180" align="start" class="shrink-0 tabular-nums opacity-70" />
|
|
241
|
+
<LpRollingText :value="event.text" :duration="180" align="start" wrap />
|
|
242
|
+
</span>
|
|
243
|
+
<span v-if="tip.more" class="opacity-70">+{{ tip.more }}</span>
|
|
244
|
+
</span>
|
|
245
|
+
</span>
|
|
246
|
+
</LpShift>
|
|
220
247
|
</template>
|
|
221
248
|
|
|
222
249
|
<!--
|
|
@@ -235,7 +262,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
235
262
|
type="button"
|
|
236
263
|
class="lp-uptime__slot min-w-0 flex-1 outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
237
264
|
:tabindex="i === tabStop ? 0 : -1"
|
|
238
|
-
:aria-label="seg.label ?? statusLabel[seg.status]"
|
|
265
|
+
:aria-label="[seg.label ?? statusLabel[seg.status], seg.start !== undefined ? uptimeRange(seg.start, seg.end, locale) : ''].filter(Boolean).join(', ')"
|
|
239
266
|
@pointerenter="hovered = i"
|
|
240
267
|
@focus="focusIndex = i; hovered = i"
|
|
241
268
|
@blur="hovered === i && (hovered = null)"
|
|
@@ -275,13 +302,14 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
275
302
|
class="inline-block h-[0.9em] w-8 align-middle"
|
|
276
303
|
/>
|
|
277
304
|
<LpNumberFlow
|
|
278
|
-
v-else-if="
|
|
279
|
-
:value="
|
|
280
|
-
:decimals="
|
|
305
|
+
v-else-if="figure"
|
|
306
|
+
:value="figure.value"
|
|
307
|
+
:decimals="figure.decimals"
|
|
308
|
+
grouped
|
|
281
309
|
suffix="%"
|
|
282
310
|
/>
|
|
283
311
|
<template v-else>—</template>
|
|
284
|
-
<span class="text-muted"> uptime</span>
|
|
312
|
+
<span class="ml-1 text-muted">{{ statusLabel.uptime }}</span>
|
|
285
313
|
</span>
|
|
286
314
|
<span class="h-px flex-1 bg-line" />
|
|
287
315
|
<span class="shrink-0">{{ endLabel }}</span>
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { applyQueryFilters, parsePaletteQuery, type CommandSearchTerms, type HighlightPart, type SearchableCommand } from "./commandSearch"
|
|
2
|
+
import type { Command } from "./LpCommandPalette.vue"
|
|
3
|
+
import type { ContextMenuItemDef } from "./LpContextMenu.vue"
|
|
4
|
+
|
|
5
|
+
/** The same action definitions serve a row menu and the command palette. */
|
|
6
|
+
export function menuPaletteCommands(items: ContextMenuItemDef[], prefix: string): Command[] {
|
|
7
|
+
return items.map((item, index) => ({
|
|
8
|
+
id: `${prefix}/${index}`, label: item.label, icon: item.icon, disabled: item.disabled,
|
|
9
|
+
onSelect: item.onSelect, keepOpen: item.checked !== undefined,
|
|
10
|
+
children: item.children ? menuPaletteCommands(item.children, `${prefix}/${index}`) : undefined,
|
|
11
|
+
}))
|
|
12
|
+
}
|
|
2
13
|
|
|
3
14
|
/*
|
|
4
15
|
* The parts of LpCommandPalette that are plain logic, kept out of the SFC so
|
|
@@ -7,6 +18,7 @@ import { applyQueryFilters, parsePaletteQuery, type CommandSearchTerms, type Hig
|
|
|
7
18
|
|
|
8
19
|
/** The fields of a command that decide whether the preview pane opens for it. */
|
|
9
20
|
export interface PreviewableCommand {
|
|
21
|
+
previewComponent?: unknown
|
|
10
22
|
preview?: string
|
|
11
23
|
excerpt?: string
|
|
12
24
|
noPreview?: boolean
|
|
@@ -23,9 +35,18 @@ export function commandHasPreview<T extends PreviewableCommand>(
|
|
|
23
35
|
): boolean {
|
|
24
36
|
if (!command || command.noPreview) return false
|
|
25
37
|
if (options.hasPreview) return options.hasPreview(command)
|
|
26
|
-
return options.slot || !!(command.preview || command.excerpt)
|
|
38
|
+
return options.slot || !!(command.previewComponent || command.preview || command.excerpt)
|
|
27
39
|
}
|
|
28
40
|
|
|
41
|
+
export const PALETTE_MODES = {
|
|
42
|
+
people: { prefix: "@", label: "People", icon: "lucide:users" },
|
|
43
|
+
channels: { prefix: "#", label: "Channels", icon: "lucide:hash" },
|
|
44
|
+
roles: { prefix: "&", label: "Roles", icon: "lucide:shield" },
|
|
45
|
+
commands: { prefix: ">", label: "Commands", icon: "lucide:terminal" },
|
|
46
|
+
help: { prefix: "?", label: "Help", icon: "lucide:circle-help" },
|
|
47
|
+
} as const
|
|
48
|
+
export type PaletteMode = keyof typeof PALETTE_MODES
|
|
49
|
+
|
|
29
50
|
/** One line of LpPaletteFacts: what it is, and what it is set to now. */
|
|
30
51
|
export interface PaletteFact {
|
|
31
52
|
/** Text, or the preview slot's `highlight(text)` to mark the query. */
|
|
@@ -72,7 +72,7 @@ function score(command: SearchableCommand, terms: CommandSearchTerms): number {
|
|
|
72
72
|
return total
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
function rankExact<T extends SearchableCommand>(items: T[], terms: CommandSearchTerms): T[] {
|
|
76
76
|
return items
|
|
77
77
|
.map((command) => ({ command, score: score(command, terms) }))
|
|
78
78
|
.filter((entry) => entry.score > 0)
|
|
@@ -80,6 +80,28 @@ export function rankCommandItems<T extends SearchableCommand>(items: T[], terms:
|
|
|
80
80
|
.map((entry) => entry.command)
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
const LATIN_KEYS = "`qwertyuiop[]asdfghjkl;'zxcvbnm,."
|
|
84
|
+
const CYRILLIC_KEYS = "ёйцукенгшщзхъфывапролджэячсмитьбю"
|
|
85
|
+
const LAYOUT_KEYS = new Map([...LATIN_KEYS].flatMap((key, i) => [[key, CYRILLIC_KEYS[i]!], [CYRILLIC_KEYS[i]!, key]]))
|
|
86
|
+
|
|
87
|
+
export function swapCommandLayout(value: string): string {
|
|
88
|
+
return [...value].map(char => {
|
|
89
|
+
const replacement = LAYOUT_KEYS.get(char.toLowerCase())
|
|
90
|
+
return replacement ? char === char.toUpperCase() ? replacement.toUpperCase() : replacement : char
|
|
91
|
+
}).join("")
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function searchCommandItems<T extends SearchableCommand>(items: T[], terms: CommandSearchTerms): { items: T[]; terms: string[] } {
|
|
95
|
+
const exact = rankExact(items, terms)
|
|
96
|
+
if (exact.length || !terms.include.length) return { items: exact, terms: terms.include }
|
|
97
|
+
const alternate = { include: terms.include.map(swapCommandLayout), exclude: terms.exclude, groups: terms.groups }
|
|
98
|
+
return { items: rankExact(items, alternate), terms: alternate.include }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function rankCommandItems<T extends SearchableCommand>(items: T[], terms: CommandSearchTerms): T[] {
|
|
102
|
+
return searchCommandItems(items, terms).items
|
|
103
|
+
}
|
|
104
|
+
|
|
83
105
|
/**
|
|
84
106
|
* A query as a fetcher receives it. `text` is what a remote search should be
|
|
85
107
|
* sent: the positive words and phrases, in the case they were typed, without
|
package/src/components/format.ts
CHANGED
|
@@ -134,3 +134,13 @@ export function compactNumber(value: number, options: CompactNumberOptions = {})
|
|
|
134
134
|
}).format(scaled)
|
|
135
135
|
return `${number}${COMPACT_UNITS[i][1]}`
|
|
136
136
|
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The character a locale writes between the whole and the fractional part:
|
|
140
|
+
* "." in English, "," in Russian and German. A counter that splits a grouped
|
|
141
|
+
* figure into columns has to find the fraction by this mark, not by ".",
|
|
142
|
+
* because in German "." is the thousands separator.
|
|
143
|
+
*/
|
|
144
|
+
export function decimalMark(locale?: string): string {
|
|
145
|
+
return numberFormat(locale, {}).formatToParts(1.5).find((p) => p.type === "decimal")?.value ?? "."
|
|
146
|
+
}
|