@leavepulse/ui 0.68.0 → 0.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/assets/LoaderMark-DxBV2iDJ.css +43 -0
- package/dist/assets/LpActivityLine-B3EwNQEB.css +33 -0
- package/dist/assets/{LpCommandPalette-DxyjhYn_.css → LpCommandPalette-u-zOxKWa.css} +21 -31
- package/dist/assets/LpDock-Cbh0wQUv.css +17 -0
- package/dist/assets/{LpDropzone-Qb8k7cg4.css → LpDropzone-Bb1vBTeU.css} +12 -17
- package/dist/assets/{LpImageEditor-CElPDzpF.css → LpImageEditor-XM5bAziW.css} +6 -6
- package/dist/assets/LpLogViewer-BDNW6P52.css +38 -0
- package/dist/assets/{LpPalettePreview-CJmAKgYW.css → LpPalettePreview-B9VKDrvA.css} +2 -7
- package/dist/assets/LpShinyText-D69cBYDR.css +61 -0
- package/dist/assets/LpStatus-430OGTZY.css +17 -0
- package/dist/assets/LpStepMark-Drvx33Vg.css +10 -0
- package/dist/assets/{LpTable-BPERfJIk.css → LpTable-DHxEQ_-3.css} +19 -19
- package/dist/assets/LpTabs-BJ-nrJuI.css +18 -0
- package/dist/assets/LpTextReveal-DjI1MsDS.css +35 -0
- package/dist/assets/LpTypewriter-CXAm9Nca.css +27 -0
- package/dist/chunks/AttentionDot-DCBf4aWg.js +25 -0
- package/dist/chunks/{ContextMenuItems-B7XFHksm.js → ContextMenuItems-B5uE8Znw.js} +1 -1
- package/dist/chunks/{DropzoneLinkField-CjkrpDqO.js → DropzoneLinkField-Bw8Flkhk.js} +2 -2
- package/dist/chunks/FieldWrapper-BiennROB.js +43 -0
- package/dist/chunks/{LayoutCanvas-DF9CovaK.js → LayoutCanvas-BsJIzhdd.js} +1 -1
- package/dist/chunks/{LayoutNode-D9bIFzk0.js → LayoutNode-DJVhkSGk.js} +6 -8
- package/dist/chunks/LegendSwatch-BhRjrtzv.js +18 -0
- package/dist/chunks/LoaderMark-C1dubiKn.js +85 -0
- package/dist/chunks/{LpActionBar-MPKMu3pz.js → LpActionBar-CzOFar5t.js} +14 -20
- package/dist/chunks/LpActivityLine-CJvzrNpj.js +95 -0
- package/dist/chunks/{LpAddressInput-DG0BTqdH.js → LpAddressInput-CNnLTDZg.js} +7 -7
- package/dist/chunks/LpAlert-C5igj802.js +119 -0
- package/dist/chunks/{LpAppShell-DPh1756q.js → LpAppShell-CEUAB8kK.js} +15 -16
- package/dist/chunks/{LpAutocomplete-Dgn6hw3p.js → LpAutocomplete-D92niDHE.js} +15 -32
- package/dist/chunks/{LpAvatar-Bs0Fb1lC.js → LpAvatar-CLPl-qOF.js} +18 -6
- package/dist/chunks/LpBadge-HOEPBYmi.js +126 -0
- package/dist/chunks/LpBreadcrumbs-DYoPc3P4.js +144 -0
- package/dist/chunks/{LpButton-DNR-YAq5.js → LpButton-CVxBfGjd.js} +12 -4
- package/dist/chunks/{LpCalendar-BLya39Fh.js → LpCalendar-pVL91-Pl.js} +1 -1
- package/dist/chunks/{LpCard-Bf71NXZq.js → LpCard-CqKLnEhl.js} +18 -8
- package/dist/chunks/{LpCheckbox-Da5oZPHo.js → LpCheckbox-DHc2bHDe.js} +9 -6
- package/dist/chunks/{LpCodeBlock-lLbmslfs.js → LpCodeBlock-BGsQhxw8.js} +24 -32
- package/dist/chunks/LpCodeDisplay-CIKKT2iW.js +189 -0
- package/dist/chunks/LpCollapse-CWS9M_lm.js +115 -0
- package/dist/chunks/{LpColorPicker-DtEfBAcK.js → LpColorPicker-DFugXNUi.js} +10 -5
- package/dist/chunks/{LpConfirmDialog-D7s4jakB.js → LpConfirmDialog-CfGCux8m.js} +3 -3
- package/dist/chunks/{LpConfirmHost-DrYI5y1K.js → LpConfirmHost-exB7JmcO.js} +1 -1
- package/dist/chunks/{LpContextMenu-CRnjoqSt.js → LpContextMenu-COwOwRgI.js} +13 -5
- package/dist/chunks/LpCopyButton-DzHjnFs0.js +133 -0
- package/dist/chunks/LpCountdown-Dtq1ihlJ.js +231 -0
- package/dist/chunks/LpDataList-B-5nHfNw.js +120 -0
- package/dist/chunks/LpDatePicker-BUe1VUC0.js +168 -0
- package/dist/chunks/LpDateRangePicker-C_k65vGR.js +126 -0
- package/dist/chunks/{LpDisclosure-6AplYBub.js → LpDisclosure-DOarzlAG.js} +22 -11
- package/dist/chunks/{LpDrawer-Q4SOxM4s.js → LpDrawer-DGNKRsGq.js} +9 -4
- package/dist/chunks/{LpDropdownMenu-DWTt0ooM.js → LpDropdownMenu-CRgrhdAH.js} +10 -5
- package/dist/chunks/LpEditable-DTqLt2lT.js +236 -0
- package/dist/chunks/{LpEmptyState-om1UnFjV.js → LpEmptyState-KTKipws2.js} +1 -1
- package/dist/chunks/{LpFieldNotice-CHuDeLjc.js → LpFieldNotice-CurbNuXI.js} +1 -1
- package/dist/chunks/{LpFileTree-CW24YsVX.js → LpFileTree-DMISXVHC.js} +5 -3
- package/dist/chunks/{LpFileTreeNode-BXhTHaKW.js → LpFileTreeNode-CPXB_Yy3.js} +42 -55
- package/dist/chunks/{LpFormField-DPb47HEm.js → LpFormField-BWwgm1E4.js} +23 -9
- package/dist/chunks/LpFrame-BVQCQLDI.js +155 -0
- package/dist/chunks/LpGauge-Bk8TwJj0.js +204 -0
- package/dist/chunks/LpGlowBorder-B8V-QzIl.js +40 -0
- package/dist/chunks/{LpHintExample-C1eKYdeL.js → LpHintExample-9Py0wc-L.js} +11 -34
- package/dist/chunks/{LpHintLayer-7UOdWQG3.js → LpHintLayer-p4hqzdyA.js} +27 -14
- package/dist/chunks/{LpHints-BUtBp9-w.js → LpHints-CCrhRfy9.js} +3 -3
- package/dist/chunks/LpHoverCard-spGxyw7T.js +130 -0
- package/dist/chunks/{LpIcon-6tSrZ3Zy.js → LpIcon-HJP5xMz4.js} +1 -1
- package/dist/chunks/LpIdenticon-BzdC_qz9.js +63 -0
- package/dist/chunks/{LpInfraNode-Ds_8SZgN.js → LpInfraNode-CUC3t4na.js} +39 -25
- package/dist/chunks/{LpInput-CUyr6PIE.js → LpInput-CDuwODJG.js} +44 -18
- package/dist/chunks/{LpLightbox-B9J8rzsj.js → LpLightbox-XJ0LpTcZ.js} +69 -69
- package/dist/chunks/LpLoader-DjJEX8hL.js +92 -0
- package/dist/chunks/{LpLogViewer-4QFc-sVN.js → LpLogViewer-elZHbj85.js} +225 -129
- package/dist/chunks/LpMagnetic-CtXKLa2Y.js +62 -0
- package/dist/chunks/{LpMapPicker-BTbkFeSG.js → LpMapPicker-BtIl7NOM.js} +34 -31
- package/dist/chunks/LpMasonry--k0571qG.js +185 -0
- package/dist/chunks/{LpMediaGallery-CuPcscx3.js → LpMediaGallery-D5swlmZ_.js} +3 -3
- package/dist/chunks/{LpModal-hk1noDcr.js → LpModal-eBMt2Fqn.js} +8 -3
- package/dist/chunks/{LpModalNav-DQd2keZm.js → LpModalNav-D4Z0wVHR.js} +4 -4
- package/dist/chunks/{LpNotificationBell-X84PJIxD.js → LpNotificationBell-DPZmP5dM.js} +32 -48
- package/dist/chunks/LpNumberField-B9k-h2HE.js +140 -0
- package/dist/chunks/{LpNumberFlow-z4WPZcZu.js → LpNumberFlow-Cfyh033Q.js} +8 -28
- package/dist/chunks/{LpOtpInput-PbeNJ5In.js → LpOtpInput-CeBPsM7D.js} +2 -1
- package/dist/chunks/{LpPagination-Bx-VG4Ag.js → LpPagination-VNCX6pFC.js} +37 -6
- package/dist/chunks/{LpPalettePreview-CFav_7co.js → LpPalettePreview-BNB8T_MY.js} +1 -1
- package/dist/chunks/{LpPasswordInput-CbnXitAS.js → LpPasswordInput-DTV18FIh.js} +20 -20
- package/dist/chunks/{LpPhoneInput-DP69HONS.js → LpPhoneInput-C60tx1tC.js} +12 -7
- package/dist/chunks/{LpPie-yLF9-aDR.js → LpPie-CFuV7pZv.js} +158 -47
- package/dist/chunks/LpPipeline-D81yh_NX.js +329 -0
- package/dist/chunks/{LpPopover-DVnkHaOu.js → LpPopover-BEIcqb9p.js} +8 -3
- package/dist/chunks/{LpProgress-CA86wxXu.js → LpProgress-CE-m5OpD.js} +23 -63
- package/dist/chunks/LpQuota-2Sw_gqcJ.js +162 -0
- package/dist/chunks/{LpRadioGroup-8S5hf8lE.js → LpRadioGroup-CGBr5nIp.js} +1 -1
- package/dist/chunks/{LpRangeCalendar-ByQi-WUz.js → LpRangeCalendar-c5qj7YcC.js} +1 -1
- package/dist/chunks/{LpRangeSlider-C1yF9-nP.js → LpRangeSlider-Dv6lNvHY.js} +6 -2
- package/dist/chunks/LpRelativeTime-CKzVqAo9.js +73 -0
- package/dist/chunks/LpRepeater-D0VjmD6y.js +140 -0
- package/dist/chunks/LpRevealOnScroll-DcQjexLy.js +76 -0
- package/dist/chunks/LpScramble-TDO5Kdou.js +85 -0
- package/dist/chunks/LpSecretField-DMfdJ1cQ.js +307 -0
- package/dist/chunks/{LpSection-BmRXe6ZP.js → LpSection-BW4xJ-Li.js} +1 -1
- package/dist/chunks/{LpSegmented-CW3bFHM4.js → LpSegmented-BO99Sr4m.js} +11 -14
- package/dist/chunks/{LpSelect-BCyDYoih.js → LpSelect-B4f_yRi5.js} +93 -108
- package/dist/chunks/LpShaderBackdrop-Bote0iuy.js +332 -0
- package/dist/chunks/LpShinyText-DX2ajtWq.js +48 -0
- package/dist/chunks/{LpSidebar-DfIiS-lB.js → LpSidebar-D-M0Y_7U.js} +5 -3
- package/dist/chunks/{LpSidebarNav-C5FDNI0t.js → LpSidebarNav-DF-TYxOV.js} +3 -3
- package/dist/chunks/{LpSlider-Cuzmrd7p.js → LpSlider-DuoRpOrH.js} +6 -2
- package/dist/chunks/{LpSparkline-DXT4KrSi.js → LpSparkline-CExIRDb3.js} +32 -27
- package/dist/chunks/LpSpotlight-4F3NeUMY.js +41 -0
- package/dist/chunks/{LpStat-iBMF5QNk.js → LpStat-B2ix1Q7t.js} +38 -35
- package/dist/chunks/LpStatus-C7kx5PKE.js +86 -0
- package/dist/chunks/LpStepMark-DK-5BKAt.js +101 -0
- package/dist/chunks/LpStepper-DRFfdJaq.js +76 -0
- package/dist/chunks/{LpSwap-D82jiecV.js → LpSwap-DzE6M3Mg.js} +1 -1
- package/dist/chunks/LpSwitch-DrzKrZZV.js +112 -0
- package/dist/chunks/{LpTableFilter-CuIbgdW0.js → LpTableFilter-DyvHGbOk.js} +3 -2
- package/dist/chunks/{LpTableOfContents-C_4VBktT.js → LpTableOfContents-rYvo8z5T.js} +34 -21
- package/dist/chunks/{LpTagsInput-DiXrpYeE.js → LpTagsInput-DgpBFRV2.js} +68 -71
- package/dist/chunks/LpTextReveal-CtAnOUkp.js +140 -0
- package/dist/chunks/{LpTextarea-xoQd5Lnp.js → LpTextarea-DiWjeVoJ.js} +12 -11
- package/dist/chunks/{LpThemeProvider-CQ2jC9TA.js → LpThemeProvider-DcbfOxsU.js} +1 -1
- package/dist/chunks/LpThemeStudio-DguwHdGt.js +416 -0
- package/dist/chunks/LpThemeStudioSection-BwhqYDLf.js +20 -0
- package/dist/chunks/LpThemeSwitcher-D5uz0MhK.js +278 -0
- package/dist/chunks/{LpTimeField-MFgAsNc5.js → LpTimeField-BFQVf_PW.js} +13 -5
- package/dist/chunks/{LpToaster-C4zE9zB7.js → LpToaster-Wv9ksj4q.js} +49 -82
- package/dist/chunks/{LpTooltip-suFCM34F.js → LpTooltip-Cz8SQwRt.js} +8 -3
- package/dist/chunks/{LpTopologyCanvas-6ozNrQKh.js → LpTopologyCanvas-ZhKthfgz.js} +1 -1
- package/dist/chunks/{LpTour-BUp3Jt2e.js → LpTour-NfRJNEIP.js} +24 -18
- package/dist/chunks/LpTruncate-D_5Pfv5B.js +112 -0
- package/dist/chunks/LpTypewriter-BCL_a6wE.js +159 -0
- package/dist/chunks/{LpUptimeBar-B6kHw4Cj.js → LpUptimeBar-DWegV2p6.js} +9 -8
- package/dist/chunks/{LpVideoBackdrop-qWRYBOTQ.js → LpVideoBackdrop-DS1wGv-X.js} +7 -8
- package/dist/chunks/{MenuTitle-obgek0B7.js → MenuTitle-BjQZX8tQ.js} +1 -1
- package/dist/chunks/OptionContent-CmYUUeWj.js +40 -0
- package/dist/chunks/PickerField-CHRS1h7K.js +116 -0
- package/dist/chunks/RevealToggle-C-KzaQGJ.js +65 -0
- package/dist/chunks/StepConnector-37OgEOXA.js +41 -0
- package/dist/chunks/TourCard-BXuVHsfn.js +123 -0
- package/dist/chunks/backdrop-Dkf2vM81.js +59 -0
- package/dist/chunks/{icons-DVhZ_plB.js → icons-4IywzkHk.js} +1 -1
- package/dist/chunks/{useTheme-2qHG8NEU.js → useTheme-BOV_-AiY.js} +9 -65
- package/dist/component-names.d.ts +1 -1
- package/dist/components/AttentionDot.vue.d.ts +9 -0
- package/dist/components/AttentionDot.vue.js +4 -0
- package/dist/components/ContextMenuItems.vue.js +1 -1
- package/dist/components/DropzoneLinkField.vue.js +1 -1
- package/dist/components/FieldWrapper.vue.d.ts +25 -0
- package/dist/components/FieldWrapper.vue.js +4 -0
- package/dist/components/LayoutCanvas.vue.js +1 -1
- package/dist/components/LayoutNode.vue.d.ts +4 -4
- package/dist/components/LayoutNode.vue.js +1 -1
- package/dist/components/LegendSwatch.vue.d.ts +6 -0
- package/dist/components/LegendSwatch.vue.js +4 -0
- package/dist/components/LoaderMark.vue.d.ts +12 -0
- package/dist/components/LoaderMark.vue.js +4 -0
- package/dist/components/LpActionBar.vue.js +1 -1
- package/dist/components/LpActivityLine.vue.d.ts +58 -0
- package/dist/components/LpActivityLine.vue.js +4 -0
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.d.ts +2 -2
- package/dist/components/LpAlert.vue.js +1 -1
- package/dist/components/LpAppShell.vue.d.ts +8 -7
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpAutocomplete.vue.js +1 -1
- package/dist/components/LpAvatar.vue.d.ts +16 -0
- package/dist/components/LpAvatar.vue.js +1 -1
- package/dist/components/LpBadge.vue.d.ts +19 -5
- package/dist/components/LpBadge.vue.js +1 -1
- package/dist/components/LpBreadcrumbs.vue.js +1 -1
- package/dist/components/LpButton.vue.d.ts +2 -2
- package/dist/components/LpButton.vue.js +1 -1
- package/dist/components/LpCalendar.vue.js +1 -1
- package/dist/components/LpCard.vue.d.ts +24 -0
- package/dist/components/LpCard.vue.js +1 -1
- package/dist/components/LpChart.vue.d.ts +10 -0
- package/dist/components/LpChart.vue.js +122 -142
- package/dist/components/LpCheckbox.vue.d.ts +2 -2
- package/dist/components/LpCheckbox.vue.js +1 -1
- package/dist/components/LpCodeBlock.vue.js +1 -1
- package/dist/components/LpCodeDisplay.vue.d.ts +89 -0
- package/dist/components/LpCodeDisplay.vue.js +4 -0
- package/dist/components/LpCollapse.vue.d.ts +58 -0
- package/dist/components/LpCollapse.vue.js +4 -0
- package/dist/components/LpColorPicker.vue.d.ts +2 -2
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.js +34 -23
- 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.d.ts +82 -0
- package/dist/components/LpCopyButton.vue.js +4 -0
- package/dist/components/LpCountdown.vue.d.ts +100 -0
- package/dist/components/LpCountdown.vue.js +4 -0
- package/dist/components/LpDataList.vue.d.ts +66 -0
- package/dist/components/LpDataList.vue.js +4 -0
- package/dist/components/LpDatePicker.vue.d.ts +1 -1
- package/dist/components/LpDatePicker.vue.js +1 -1
- package/dist/components/LpDateRangePicker.vue.d.ts +1 -1
- package/dist/components/LpDateRangePicker.vue.js +1 -1
- package/dist/components/LpDisclosure.vue.js +1 -1
- package/dist/components/LpDock.vue.d.ts +74 -0
- package/dist/components/LpDock.vue.js +204 -0
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpDropdownMenu.vue.js +1 -1
- package/dist/components/LpDropzone.vue.d.ts +8 -1
- package/dist/components/LpDropzone.vue.js +36 -15
- package/dist/components/LpEditable.vue.d.ts +85 -0
- package/dist/components/LpEditable.vue.js +4 -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/LpFrame.vue.d.ts +22 -0
- package/dist/components/LpFrame.vue.js +4 -0
- package/dist/components/LpGauge.vue.d.ts +116 -0
- package/dist/components/LpGauge.vue.js +4 -0
- package/dist/components/LpGlowBorder.vue.d.ts +47 -0
- package/dist/components/LpGlowBorder.vue.js +4 -0
- package/dist/components/LpHintExample.vue.d.ts +2 -2
- package/dist/components/LpHintExample.vue.js +1 -1
- package/dist/components/LpHintLayer.vue.js +1 -1
- package/dist/components/LpHints.vue.js +1 -1
- package/dist/components/LpHoverCard.vue.d.ts +60 -0
- package/dist/components/LpHoverCard.vue.js +4 -0
- package/dist/components/LpIcon.vue.js +1 -1
- package/dist/components/LpIdenticon.vue.d.ts +37 -0
- package/dist/components/LpIdenticon.vue.js +4 -0
- package/dist/components/LpImageEditor.vue.js +13 -19
- package/dist/components/LpInfraNode.vue.js +1 -1
- package/dist/components/LpInput.vue.d.ts +10 -0
- package/dist/components/LpInput.vue.js +1 -1
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpLoader.vue.d.ts +106 -0
- package/dist/components/LpLoader.vue.js +4 -0
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpMagnetic.vue.d.ts +28 -0
- package/dist/components/LpMagnetic.vue.js +4 -0
- package/dist/components/LpMapPicker.vue.js +1 -1
- package/dist/components/LpMasonry.vue.d.ts +42 -0
- package/dist/components/LpMasonry.vue.js +4 -0
- 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.d.ts +15 -6
- package/dist/components/LpNotificationBell.vue.js +1 -1
- package/dist/components/LpNumberField.vue.d.ts +57 -0
- package/dist/components/LpNumberField.vue.js +1 -1
- package/dist/components/LpNumberFlow.vue.js +1 -1
- package/dist/components/LpOtpInput.vue.d.ts +12 -4
- package/dist/components/LpOtpInput.vue.js +1 -1
- package/dist/components/LpPagination.vue.js +1 -1
- package/dist/components/LpPaletteFacts.vue.js +1 -1
- package/dist/components/LpPaletteHeader.vue.js +2 -2
- package/dist/components/LpPalettePreview.vue.js +1 -1
- package/dist/components/LpPaletteStats.vue.js +1 -1
- package/dist/components/LpPasswordInput.vue.d.ts +12 -1
- package/dist/components/LpPasswordInput.vue.js +1 -1
- package/dist/components/LpPhoneInput.vue.js +1 -1
- package/dist/components/LpPie.vue.d.ts +3 -1
- package/dist/components/LpPie.vue.js +1 -1
- package/dist/components/LpPipeline.vue.d.ts +91 -0
- package/dist/components/LpPipeline.vue.js +4 -0
- package/dist/components/LpPopover.vue.d.ts +2 -2
- package/dist/components/LpPopover.vue.js +1 -1
- package/dist/components/LpProgress.vue.d.ts +11 -4
- package/dist/components/LpProgress.vue.js +1 -1
- package/dist/components/LpQuota.vue.d.ts +68 -0
- package/dist/components/LpQuota.vue.js +4 -0
- package/dist/components/LpRadioGroup.vue.js +1 -1
- package/dist/components/LpRangeCalendar.vue.js +1 -1
- package/dist/components/LpRangeSlider.vue.js +1 -1
- package/dist/components/LpRelativeTime.vue.d.ts +41 -0
- package/dist/components/LpRelativeTime.vue.js +4 -0
- package/dist/components/LpRepeater.vue.d.ts +48 -0
- package/dist/components/LpRepeater.vue.js +4 -0
- package/dist/components/LpRevealOnScroll.vue.d.ts +67 -0
- package/dist/components/LpRevealOnScroll.vue.js +4 -0
- package/dist/components/LpRollingText.vue.d.ts +1 -1
- package/dist/components/LpScramble.vue.d.ts +55 -0
- package/dist/components/LpScramble.vue.js +4 -0
- package/dist/components/LpScrollArea.vue.d.ts +1 -1
- package/dist/components/LpSecretField.vue.d.ts +108 -0
- package/dist/components/LpSecretField.vue.js +4 -0
- package/dist/components/LpSection.vue.js +1 -1
- package/dist/components/LpSegmented.vue.js +1 -1
- package/dist/components/LpSelect.vue.d.ts +2 -0
- package/dist/components/LpSelect.vue.js +1 -1
- package/dist/components/LpShaderBackdrop.vue.d.ts +39 -0
- package/dist/components/LpShaderBackdrop.vue.js +4 -0
- package/dist/components/LpShinyText.vue.d.ts +39 -0
- package/dist/components/LpShinyText.vue.js +4 -0
- 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.js +1 -1
- package/dist/components/LpSparkline.vue.d.ts +4 -3
- package/dist/components/LpSparkline.vue.js +1 -1
- package/dist/components/LpSpotlight.vue.d.ts +37 -0
- package/dist/components/LpSpotlight.vue.js +4 -0
- package/dist/components/LpStat.vue.js +1 -1
- package/dist/components/LpStatus.vue.d.ts +65 -0
- package/dist/components/LpStatus.vue.js +4 -0
- package/dist/components/LpStepMark.vue.d.ts +31 -0
- package/dist/components/LpStepMark.vue.js +4 -0
- package/dist/components/LpStepper.vue.js +1 -1
- package/dist/components/LpSwap.vue.js +1 -1
- package/dist/components/LpSwitch.vue.d.ts +2 -2
- package/dist/components/LpSwitch.vue.js +1 -1
- package/dist/components/LpTable.vue.js +104 -120
- package/dist/components/LpTableFilter.vue.js +1 -1
- package/dist/components/LpTableOfContents.vue.d.ts +6 -1
- package/dist/components/LpTableOfContents.vue.js +1 -1
- package/dist/components/LpTabs.vue.d.ts +5 -5
- package/dist/components/LpTabs.vue.js +25 -33
- package/dist/components/LpTagsInput.vue.js +1 -1
- package/dist/components/LpTextReveal.vue.d.ts +58 -0
- package/dist/components/LpTextReveal.vue.js +4 -0
- package/dist/components/LpTextarea.vue.js +1 -1
- package/dist/components/LpThemeProvider.vue.js +1 -1
- package/dist/components/LpThemeStudio.vue.d.ts +54 -0
- package/dist/components/LpThemeStudio.vue.js +4 -0
- package/dist/components/LpThemeStudioSection.vue.d.ts +16 -0
- package/dist/components/LpThemeStudioSection.vue.js +4 -0
- package/dist/components/LpThemeSwitcher.vue.d.ts +32 -1
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpTimeField.vue.d.ts +2 -0
- package/dist/components/LpTimeField.vue.js +1 -1
- package/dist/components/LpToaster.vue.js +1 -1
- package/dist/components/LpTooltip.vue.d.ts +2 -2
- 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.d.ts +34 -0
- package/dist/components/LpTruncate.vue.js +4 -0
- package/dist/components/LpTypewriter.vue.d.ts +61 -0
- package/dist/components/LpTypewriter.vue.js +4 -0
- package/dist/components/LpUptimeBar.vue.js +1 -1
- package/dist/components/LpVideoBackdrop.vue.js +1 -1
- package/dist/components/MenuTitle.vue.js +1 -1
- package/dist/components/OptionContent.vue.d.ts +7 -0
- package/dist/components/OptionContent.vue.js +4 -0
- package/dist/components/PickerField.vue.d.ts +43 -0
- package/dist/components/PickerField.vue.js +4 -0
- package/dist/components/RevealToggle.vue.d.ts +21 -0
- package/dist/components/RevealToggle.vue.js +4 -0
- package/dist/components/StepConnector.vue.d.ts +19 -0
- package/dist/components/StepConnector.vue.js +4 -0
- package/dist/components/TourCard.vue.js +1 -1
- package/dist/components/badge.d.ts +1 -0
- package/dist/components/badge.js +4 -0
- package/dist/components/chart.d.ts +5 -1
- package/dist/components/chart.js +2 -21
- package/dist/components/dateRange.d.ts +11 -0
- package/dist/components/dateRange.js +12 -0
- package/dist/components/dock.d.ts +19 -0
- package/dist/components/dock.js +16 -0
- package/dist/components/dropdown.d.ts +10 -1
- package/dist/components/dropdown.js +7 -2
- package/dist/components/fieldText.d.ts +38 -0
- package/dist/components/fieldText.js +53 -0
- package/dist/components/fileTree.d.ts +7 -3
- package/dist/components/fileTree.js +12 -18
- package/dist/components/format.d.ts +41 -0
- package/dist/components/format.js +67 -0
- package/dist/components/identicon.d.ts +38 -0
- package/dist/components/identicon.js +85 -0
- package/dist/components/masonry.d.ts +32 -0
- package/dist/components/masonry.js +40 -0
- package/dist/components/menuWhen.js +1 -1
- package/dist/components/meter.d.ts +64 -0
- package/dist/components/meter.js +53 -0
- package/dist/components/otp.d.ts +1 -1
- package/dist/components/otp.js +7 -2
- package/dist/components/pipeline.d.ts +55 -0
- package/dist/components/pipeline.js +51 -0
- package/dist/components/quota.d.ts +57 -0
- package/dist/components/quota.js +40 -0
- package/dist/components/relativeTime.d.ts +24 -0
- package/dist/components/relativeTime.js +56 -0
- package/dist/components/reveal.d.ts +18 -0
- package/dist/components/reveal.js +32 -0
- package/dist/components/scramble.d.ts +68 -0
- package/dist/components/scramble.js +58 -0
- package/dist/components/shaderBackdrop.d.ts +20 -0
- package/dist/components/shaderBackdrop.js +156 -0
- package/dist/components/slider.d.ts +5 -0
- package/dist/components/slider.js +24 -0
- package/dist/components/slotText.d.ts +2 -0
- package/dist/components/slotText.js +9 -0
- package/dist/components/status.d.ts +14 -0
- package/dist/components/status.js +22 -0
- package/dist/components/tableFacets.d.ts +8 -0
- package/dist/components/tableFacets.js +19 -0
- package/dist/components/textSplit.d.ts +17 -0
- package/dist/components/textSplit.js +24 -0
- package/dist/components/timeSpan.d.ts +78 -0
- package/dist/components/timeSpan.js +85 -0
- package/dist/components/tone.d.ts +43 -0
- package/dist/components/tone.js +82 -0
- package/dist/components/tour.js +3 -2
- package/dist/components/typewriter.d.ts +36 -0
- package/dist/components/typewriter.js +53 -0
- package/dist/components/videoBackdrop.js +5 -6
- package/dist/composables/easing.d.ts +22 -1
- package/dist/composables/easing.js +42 -1
- package/dist/composables/pseudoLocale.d.ts +15 -0
- package/dist/composables/pseudoLocale.js +96 -0
- package/dist/composables/scroll.js +5 -3
- package/dist/composables/useClipboard.d.ts +4 -1
- package/dist/composables/useClipboard.js +12 -2
- package/dist/composables/useField.d.ts +8 -0
- package/dist/composables/useField.js +5 -0
- package/dist/composables/useForm.js +19 -5
- package/dist/composables/useHintSystem.d.ts +4 -0
- package/dist/composables/useHintSystem.js +33 -1
- package/dist/composables/useHints.js +9 -3
- package/dist/composables/useInView.d.ts +11 -0
- package/dist/composables/useInView.js +39 -0
- package/dist/composables/useLoadingGate.d.ts +8 -0
- package/dist/composables/useLoadingGate.js +38 -0
- package/dist/composables/useLpConfig.d.ts +10 -0
- package/dist/composables/useLpConfig.js +11 -1
- package/dist/composables/useMediaQuery.js +3 -1
- package/dist/composables/useNow.d.ts +16 -0
- package/dist/composables/useNow.js +39 -0
- package/dist/composables/usePersisted.js +14 -7
- package/dist/composables/usePillTransition.d.ts +5 -2
- package/dist/composables/usePillTransition.js +4 -3
- package/dist/composables/usePointerFrame.d.ts +18 -0
- package/dist/composables/usePointerFrame.js +31 -0
- package/dist/composables/useReducedMotion.d.ts +2 -0
- package/dist/composables/useReducedMotion.js +7 -0
- package/dist/composables/useRepeater.d.ts +17 -0
- package/dist/composables/useRepeater.js +31 -0
- package/dist/composables/useResizeObserver.d.ts +8 -0
- package/dist/composables/useResizeObserver.js +33 -0
- package/dist/composables/useRevealedValue.d.ts +31 -0
- package/dist/composables/useRevealedValue.js +26 -0
- package/dist/composables/useScramble.d.ts +42 -0
- package/dist/composables/useScramble.js +76 -0
- package/dist/composables/useShift.js +2 -10
- package/dist/composables/useSpotlight.d.ts +18 -0
- package/dist/composables/useSpotlight.js +53 -0
- package/dist/composables/useTilt.js +26 -18
- package/dist/composables/useTween.d.ts +43 -0
- package/dist/composables/useTween.js +69 -0
- package/dist/composables/useView.d.ts +18 -0
- package/dist/composables/useView.js +61 -0
- package/dist/icons-all.js +1 -1
- package/dist/index.d.ts +98 -1
- package/dist/index.js +262 -104
- package/dist/theme/define.d.ts +12 -0
- package/dist/theme/studio.d.ts +90 -0
- package/package.json +5 -2
- package/src/component-names.ts +33 -0
- package/src/components/AttentionDot.vue +27 -0
- package/src/components/FieldWrapper.vue +38 -0
- package/src/components/LayoutNode.vue +5 -9
- package/src/components/LegendSwatch.vue +15 -0
- package/src/components/LoaderMark.vue +90 -0
- package/src/components/LpActionBar.vue +12 -20
- package/src/components/LpActivityLine.vue +186 -0
- package/src/components/LpAlert.vue +54 -24
- package/src/components/LpAppShell.vue +7 -11
- package/src/components/LpAutocomplete.vue +16 -14
- package/src/components/LpAvatar.vue +17 -2
- package/src/components/LpBadge.vue +77 -13
- package/src/components/LpBreadcrumbs.vue +101 -35
- package/src/components/LpButton.vue +15 -3
- package/src/components/LpCard.vue +30 -7
- package/src/components/LpChart.vue +152 -156
- package/src/components/LpCheckbox.vue +10 -4
- package/src/components/LpCodeBlock.vue +11 -19
- package/src/components/LpCodeDisplay.vue +228 -0
- package/src/components/LpCollapse.vue +162 -0
- package/src/components/LpColorPicker.vue +10 -1
- package/src/components/LpCommandPalette.vue +38 -24
- package/src/components/LpContextMenu.vue +10 -1
- package/src/components/LpCopyButton.vue +185 -0
- package/src/components/LpCountdown.vue +308 -0
- package/src/components/LpDataList.vue +154 -0
- package/src/components/LpDatePicker.vue +53 -104
- package/src/components/LpDateRangePicker.vue +49 -95
- package/src/components/LpDisclosure.vue +58 -36
- package/src/components/LpDock.vue +274 -0
- package/src/components/LpDrawer.vue +10 -1
- package/src/components/LpDropdownMenu.vue +10 -1
- package/src/components/LpDropzone.vue +34 -13
- package/src/components/LpEditable.vue +293 -0
- package/src/components/LpFileTree.vue +5 -1
- package/src/components/LpFileTreeNode.vue +25 -50
- package/src/components/LpFormField.vue +22 -3
- package/src/components/LpFrame.vue +200 -0
- package/src/components/LpGauge.vue +287 -0
- package/src/components/LpGlowBorder.vue +60 -0
- package/src/components/LpHintExample.vue +14 -38
- package/src/components/LpHintLayer.vue +13 -3
- package/src/components/LpHoverCard.vue +175 -0
- package/src/components/LpIdenticon.vue +69 -0
- package/src/components/LpImageEditor.vue +15 -18
- package/src/components/LpInfraNode.vue +27 -16
- package/src/components/LpInput.vue +30 -16
- package/src/components/LpLightbox.vue +44 -23
- package/src/components/LpLoader.vue +145 -0
- package/src/components/LpLogViewer.vue +311 -82
- package/src/components/LpMagnetic.vue +97 -0
- package/src/components/LpMapPicker.vue +26 -28
- package/src/components/LpMasonry.vue +247 -0
- package/src/components/LpModal.vue +10 -1
- package/src/components/LpNotificationBell.vue +33 -49
- package/src/components/LpNumberField.vue +79 -14
- package/src/components/LpNumberFlow.vue +10 -35
- package/src/components/LpOtpInput.vue +7 -2
- package/src/components/LpPagination.vue +38 -4
- package/src/components/LpPalettePreview.vue +2 -7
- package/src/components/LpPasswordInput.vue +16 -14
- package/src/components/LpPhoneInput.vue +12 -3
- package/src/components/LpPie.vue +197 -36
- package/src/components/LpPipeline.vue +366 -0
- package/src/components/LpPopover.vue +10 -1
- package/src/components/LpProgress.vue +22 -84
- package/src/components/LpQuota.vue +197 -0
- package/src/components/LpRangeSlider.vue +5 -1
- package/src/components/LpRelativeTime.vue +90 -0
- package/src/components/LpRepeater.vue +80 -0
- package/src/components/LpRevealOnScroll.vue +125 -0
- package/src/components/LpScramble.vue +107 -0
- package/src/components/LpSecretField.vue +313 -0
- package/src/components/LpSegmented.vue +3 -10
- package/src/components/LpSelect.vue +78 -64
- package/src/components/LpShaderBackdrop.vue +434 -0
- package/src/components/LpShinyText.vue +139 -0
- package/src/components/LpSidebar.vue +2 -0
- package/src/components/LpSlider.vue +5 -1
- package/src/components/LpSparkline.vue +55 -37
- package/src/components/LpSpotlight.vue +51 -0
- package/src/components/LpStat.vue +6 -2
- package/src/components/LpStatus.vue +127 -0
- package/src/components/LpStepMark.vue +138 -0
- package/src/components/LpStepper.vue +37 -43
- package/src/components/LpSwap.vue +2 -2
- package/src/components/LpSwitch.vue +15 -22
- package/src/components/LpTable.vue +47 -63
- package/src/components/LpTableFilter.vue +1 -0
- package/src/components/LpTableOfContents.vue +53 -29
- package/src/components/LpTabs.vue +10 -21
- package/src/components/LpTagsInput.vue +53 -37
- package/src/components/LpTextReveal.vue +236 -0
- package/src/components/LpTextarea.vue +11 -11
- package/src/components/LpThemeStudio.vue +216 -0
- package/src/components/LpThemeStudioSection.vue +16 -0
- package/src/components/LpThemeSwitcher.vue +156 -47
- package/src/components/LpTimeField.vue +9 -1
- package/src/components/LpToaster.vue +63 -83
- package/src/components/LpTooltip.vue +10 -1
- package/src/components/LpTour.vue +24 -13
- package/src/components/LpTruncate.vue +135 -0
- package/src/components/LpTypewriter.vue +236 -0
- package/src/components/LpUptimeBar.vue +6 -5
- package/src/components/LpVideoBackdrop.vue +15 -13
- package/src/components/OptionContent.vue +17 -0
- package/src/components/PickerField.vue +58 -0
- package/src/components/RevealToggle.vue +51 -0
- package/src/components/StepConnector.vue +54 -0
- package/src/components/TourCard.vue +29 -16
- package/src/components/badge.ts +8 -0
- package/src/components/chart.ts +8 -23
- package/src/components/dateRange.ts +25 -0
- package/src/components/dock.ts +31 -0
- package/src/components/dropdown.ts +15 -3
- package/src/components/fieldText.ts +95 -0
- package/src/components/fileTree.ts +19 -21
- package/src/components/format.ts +136 -0
- package/src/components/identicon.ts +160 -0
- package/src/components/masonry.ts +73 -0
- package/src/components/meter.ts +122 -0
- package/src/components/otp.ts +14 -3
- package/src/components/pipeline.ts +101 -0
- package/src/components/quota.ts +111 -0
- package/src/components/relativeTime.ts +118 -0
- package/src/components/reveal.ts +56 -0
- package/src/components/scramble.ts +138 -0
- package/src/components/shaderBackdrop.ts +208 -0
- package/src/components/slider.ts +30 -0
- package/src/components/slotText.ts +13 -0
- package/src/components/status.ts +46 -0
- package/src/components/tableFacets.ts +25 -0
- package/src/components/textSplit.ts +55 -0
- package/src/components/timeSpan.ts +183 -0
- package/src/components/tone.ts +144 -0
- package/src/components/tour.ts +3 -2
- package/src/components/typewriter.ts +104 -0
- package/src/components/videoBackdrop.ts +7 -6
- package/src/composables/easing.ts +66 -1
- package/src/composables/pseudoLocale.ts +105 -0
- package/src/composables/scroll.ts +6 -3
- package/src/composables/useClipboard.ts +25 -3
- package/src/composables/useField.ts +12 -0
- package/src/composables/useForm.ts +27 -6
- package/src/composables/useHintSystem.ts +40 -0
- package/src/composables/useHints.ts +14 -3
- package/src/composables/useInView.ts +61 -0
- package/src/composables/useLoadingGate.ts +63 -0
- package/src/composables/useLpConfig.ts +20 -1
- package/src/composables/useMediaQuery.ts +6 -2
- package/src/composables/useNow.ts +84 -0
- package/src/composables/usePersisted.ts +19 -8
- package/src/composables/usePillTransition.ts +21 -13
- package/src/composables/usePointerFrame.ts +72 -0
- package/src/composables/useReducedMotion.ts +22 -0
- package/src/composables/useRepeater.ts +44 -0
- package/src/composables/useResizeObserver.ts +59 -0
- package/src/composables/useRevealedValue.ts +84 -0
- package/src/composables/useScramble.ts +152 -0
- package/src/composables/useShift.ts +10 -16
- package/src/composables/useSpotlight.ts +82 -0
- package/src/composables/useTilt.ts +40 -23
- package/src/composables/useTween.ts +149 -0
- package/src/composables/useView.ts +93 -0
- package/src/index.css +1 -0
- package/src/index.ts +143 -1
- package/src/kit-icons.ts +1 -1
- package/src/theme/define.ts +65 -0
- package/src/theme/studio.ts +202 -0
- package/src/tokens/effects.css +151 -0
- package/src/tokens/tokens.css +131 -39
- package/src/tokens/utilities.css +81 -0
- package/dist/assets/LpTabs-Bk7v4bZz.css +0 -18
- package/dist/chunks/LpAlert-BdvxvcWE.js +0 -85
- package/dist/chunks/LpBadge-Fe0D1bYx.js +0 -62
- package/dist/chunks/LpBreadcrumbs-rzOKkLiI.js +0 -98
- package/dist/chunks/LpDatePicker-BMfM454Y.js +0 -243
- package/dist/chunks/LpDateRangePicker-CNb9X3eL.js +0 -195
- package/dist/chunks/LpNumberField-DO1ufKvE.js +0 -107
- package/dist/chunks/LpStepper-C2slwObu.js +0 -90
- package/dist/chunks/LpSwitch-CZy9_IoA.js +0 -132
- package/dist/chunks/LpThemeSwitcher-CDoziW0z.js +0 -166
- package/dist/chunks/TourCard-BKI7X9-r.js +0 -105
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Numbers as the kit prints them: byte sizes (a file tree's size column, a
|
|
3
|
+
* quota's "4.2 GB of 5 GB") and compact figures (a chart's axis ticks, a
|
|
4
|
+
* counter's "1.2k"). One module so the size column and the quota bar agree on
|
|
5
|
+
* units and rounding, and so the axis and the counter cross a million the same
|
|
6
|
+
* way. Pure, so the edge cases are tested without a DOM.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* Building an Intl formatter costs far more than using one, and a file tree
|
|
11
|
+
* formats a size on every row, so each distinct setup is built once.
|
|
12
|
+
*/
|
|
13
|
+
const numberFormats = new Map<string, Intl.NumberFormat>()
|
|
14
|
+
|
|
15
|
+
function numberFormat(locale: string | undefined, options: Intl.NumberFormatOptions): Intl.NumberFormat {
|
|
16
|
+
const key = `${locale ?? ""}|${JSON.stringify(options)}`
|
|
17
|
+
let format = numberFormats.get(key)
|
|
18
|
+
if (!format) {
|
|
19
|
+
format = new Intl.NumberFormat(locale, options)
|
|
20
|
+
numberFormats.set(key, format)
|
|
21
|
+
}
|
|
22
|
+
return format
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const BYTE_UNITS = ["byte", "kilobyte", "megabyte", "gigabyte", "terabyte", "petabyte"] as const
|
|
26
|
+
|
|
27
|
+
export interface FormatBytesOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Keep the decimal place a value under 10 gets even when it is zero
|
|
30
|
+
* ("1.0 MB" rather than "1 MB"), so a column of sizes keeps one shape.
|
|
31
|
+
*/
|
|
32
|
+
fixed?: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A byte count in the largest unit that keeps it readable: 1 536 → "1.5 kB".
|
|
37
|
+
* Binary steps, the way file managers and storage limits show sizes. Unit
|
|
38
|
+
* names come from Intl, so a Russian app reads "1,5 МБ" without a table of
|
|
39
|
+
* its own. One decimal under 10 (2.4 MB), none above (240 MB). Negative or
|
|
40
|
+
* non-finite input is "—": there is no size to show.
|
|
41
|
+
*/
|
|
42
|
+
export function formatBytes(bytes: number, locale?: string, options: FormatBytesOptions = {}): string {
|
|
43
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "—"
|
|
44
|
+
let scaled = bytes
|
|
45
|
+
let step = 0
|
|
46
|
+
// Compared as it will print, so 1 048 575 B is "1.0 MB", not "1024 kB".
|
|
47
|
+
while (Math.round(scaled) >= 1024 && step < BYTE_UNITS.length - 1) {
|
|
48
|
+
scaled /= 1024
|
|
49
|
+
step += 1
|
|
50
|
+
}
|
|
51
|
+
// Decided on the rounded value: 9.96 kB prints as "10 kB", not "10.0 kB".
|
|
52
|
+
const places = step > 0 && Number(scaled.toFixed(1)) < 10 ? 1 : 0
|
|
53
|
+
// The number and the unit symbol are assembled here rather than taken whole:
|
|
54
|
+
// Intl's "short" form spells the smallest unit out ("1023 byte", "1023 Byte")
|
|
55
|
+
// while every other unit is a symbol, and its "narrow" form glues English
|
|
56
|
+
// symbols to the figure ("1023B"). Narrow symbols with one no-break space
|
|
57
|
+
// read the same in every locale: "1023 B", "1.5 kB", "1,5 МБ".
|
|
58
|
+
const parts = numberFormat(locale, {
|
|
59
|
+
style: "unit",
|
|
60
|
+
unit: BYTE_UNITS[step],
|
|
61
|
+
unitDisplay: "narrow",
|
|
62
|
+
// A figure here never passes 1023 below petabytes, and "1,023 kB" reads as
|
|
63
|
+
// a decimal in half the world.
|
|
64
|
+
useGrouping: false,
|
|
65
|
+
minimumFractionDigits: options.fixed ? places : 0,
|
|
66
|
+
maximumFractionDigits: places,
|
|
67
|
+
}).formatToParts(scaled)
|
|
68
|
+
const unit = parts.filter((p) => p.type === "unit").map((p) => p.value).join("")
|
|
69
|
+
const figure = parts.filter((p) => p.type !== "unit" && p.type !== "literal").map((p) => p.value).join("")
|
|
70
|
+
return unit ? `${figure}\u00a0${unit}` : figure
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const COMPACT_UNITS: readonly (readonly [number, string])[] = [
|
|
74
|
+
[1, ""],
|
|
75
|
+
[1e3, "k"],
|
|
76
|
+
[1e6, "M"],
|
|
77
|
+
[1e9, "B"],
|
|
78
|
+
[1e12, "T"],
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
export interface CompactNumberOptions {
|
|
82
|
+
/**
|
|
83
|
+
* Writes the decimal separator the locale's way ("1,2k" in Russian). The
|
|
84
|
+
* k/M/B/T suffixes stay: Intl's own compact notation changes the shape of
|
|
85
|
+
* the figure per locale ("1,2 тыс."), which a tick label or a rolling
|
|
86
|
+
* counter cannot absorb. Unset keeps a plain "." whatever the browser says.
|
|
87
|
+
*/
|
|
88
|
+
locale?: string
|
|
89
|
+
/** Places for a figure below a thousand (12.25). Default 2. */
|
|
90
|
+
plainDecimals?: number
|
|
91
|
+
/** Places once a unit is in play (1.2k). Default 1. */
|
|
92
|
+
decimals?: number
|
|
93
|
+
/**
|
|
94
|
+
* Keep trailing zeros ("5.0M", "1.0k") so the figure's shape only changes
|
|
95
|
+
* at a rollover. Off, they are dropped ("5M"), the way a label is written.
|
|
96
|
+
*/
|
|
97
|
+
fixed?: boolean
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A figure shortened with k/M/B/T: 1 200 → "1.2k", 3 400 000 → "3.4M".
|
|
102
|
+
*
|
|
103
|
+
* The unit is picked by what will PRINT, not by the raw size: 999 950 at one
|
|
104
|
+
* place rounds to "1000k", which reads as the next unit up, so it becomes
|
|
105
|
+
* "1M"; 999.999 with two places would be "1000", so it becomes "1k".
|
|
106
|
+
*/
|
|
107
|
+
export function compactNumber(value: number, options: CompactNumberOptions = {}): string {
|
|
108
|
+
if (!Number.isFinite(value)) return String(value)
|
|
109
|
+
const { locale, plainDecimals = 2, decimals = 1, fixed = false } = options
|
|
110
|
+
const abs = Math.abs(value)
|
|
111
|
+
let i = COMPACT_UNITS.length - 1
|
|
112
|
+
while (i > 0 && abs < COMPACT_UNITS[i][0]) i--
|
|
113
|
+
|
|
114
|
+
const placesAt = (at: number) => (at === 0 ? plainDecimals : decimals)
|
|
115
|
+
const roundAt = (at: number) => Number((value / COMPACT_UNITS[at][0]).toFixed(placesAt(at)))
|
|
116
|
+
let scaled = roundAt(i)
|
|
117
|
+
if (Math.abs(scaled) >= 1000 && i < COMPACT_UNITS.length - 1) {
|
|
118
|
+
i++
|
|
119
|
+
scaled = roundAt(i)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const places = placesAt(i)
|
|
123
|
+
// -0.001 rounds to -0, which would print as "-0".
|
|
124
|
+
if (scaled === 0) scaled = 0
|
|
125
|
+
const number =
|
|
126
|
+
locale === undefined
|
|
127
|
+
? fixed
|
|
128
|
+
? scaled.toFixed(places)
|
|
129
|
+
: String(scaled)
|
|
130
|
+
: numberFormat(locale, {
|
|
131
|
+
minimumFractionDigits: fixed ? places : 0,
|
|
132
|
+
maximumFractionDigits: places,
|
|
133
|
+
useGrouping: false,
|
|
134
|
+
}).format(scaled)
|
|
135
|
+
return `${number}${COMPACT_UNITS[i][1]}`
|
|
136
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Identicons: a picture derived from a string, so something with no avatar
|
|
3
|
+
* of its own (a new account, a server, an API key, a commit) still gets a
|
|
4
|
+
* face that is the same every time and different from its neighbours.
|
|
5
|
+
*
|
|
6
|
+
* The pattern is a small pixel grid mirrored left to right, the way faces
|
|
7
|
+
* and logos are: symmetry is what makes a random grid read as a shape rather
|
|
8
|
+
* than noise, and it halves the bits each picture needs.
|
|
9
|
+
*
|
|
10
|
+
* The colour comes from the same hash, but only the HUE is free. Saturation
|
|
11
|
+
* and lightness are held to a middle band so every identicon reads on a dark
|
|
12
|
+
* surface and on a light one alike; a free lightness would hand some seeds a
|
|
13
|
+
* near-black shape that vanishes in a dark theme and others a pale one that
|
|
14
|
+
* vanishes in a light theme.
|
|
15
|
+
*
|
|
16
|
+
* Pure and deterministic: no randomness beyond the seed, no DOM. The same
|
|
17
|
+
* seed gives the same picture on the server, in the client and across
|
|
18
|
+
* releases, which is what lets a user recognise it.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export interface IdenticonOptions {
|
|
22
|
+
/** Cells per side. 5 is the familiar look; more cells, busier shapes. */
|
|
23
|
+
grid?: number
|
|
24
|
+
/** Saturation range, percent. Kept in the middle so the colour holds on any theme. */
|
|
25
|
+
saturation?: readonly [number, number]
|
|
26
|
+
/** Lightness range, percent: dark enough for a light theme, light enough for a dark one. */
|
|
27
|
+
lightness?: readonly [number, number]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface Identicon {
|
|
31
|
+
/** Rows of cells, `true` filled. Mirrored left to right. */
|
|
32
|
+
cells: boolean[][]
|
|
33
|
+
/** The hue the seed picked, 0–360. */
|
|
34
|
+
hue: number
|
|
35
|
+
/** The shape's colour as a CSS `hsl()`. */
|
|
36
|
+
color: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
* FNV-1a over the string's UTF-16 units. Not a secure hash and not meant to
|
|
41
|
+
* be: it only has to spread similar ids ("user-1", "user-2") far apart, which
|
|
42
|
+
* it does, in a few lines that give the same answer on every engine.
|
|
43
|
+
*/
|
|
44
|
+
export function hashString(seed: string): number {
|
|
45
|
+
let h = 0x811c9dc5
|
|
46
|
+
for (let i = 0; i < seed.length; i++) {
|
|
47
|
+
h ^= seed.charCodeAt(i)
|
|
48
|
+
h = Math.imul(h, 0x01000193)
|
|
49
|
+
}
|
|
50
|
+
return h >>> 0
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/*
|
|
54
|
+
* A small seeded generator (mulberry32). The hash alone has 32 bits, and a
|
|
55
|
+
* 7×7 grid wants more than that; drawing from a generator seeded with it
|
|
56
|
+
* keeps every bit dependent on the whole string.
|
|
57
|
+
*/
|
|
58
|
+
function generator(seed: number): () => number {
|
|
59
|
+
let a = seed
|
|
60
|
+
return () => {
|
|
61
|
+
a = (a + 0x6d2b79f5) | 0
|
|
62
|
+
let t = Math.imul(a ^ (a >>> 15), 1 | a)
|
|
63
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
|
|
64
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The grid and colour for a seed. */
|
|
69
|
+
export function identicon(seed: string, options: IdenticonOptions = {}): Identicon {
|
|
70
|
+
const size = Math.max(3, Math.min(12, Math.round(options.grid ?? 5)))
|
|
71
|
+
const [sMin, sMax] = options.saturation ?? [55, 72]
|
|
72
|
+
const [lMin, lMax] = options.lightness ?? [48, 60]
|
|
73
|
+
const next = generator(hashString(seed))
|
|
74
|
+
|
|
75
|
+
const hue = Math.floor(next() * 360)
|
|
76
|
+
const saturation = Math.round(sMin + next() * (sMax - sMin))
|
|
77
|
+
const lightness = Math.round(lMin + next() * (lMax - lMin))
|
|
78
|
+
|
|
79
|
+
const half = Math.ceil(size / 2)
|
|
80
|
+
const cells: boolean[][] = []
|
|
81
|
+
let filled = 0
|
|
82
|
+
for (let y = 0; y < size; y++) {
|
|
83
|
+
const row: boolean[] = new Array(size).fill(false)
|
|
84
|
+
for (let x = 0; x < half; x++) {
|
|
85
|
+
const on = next() < 0.5
|
|
86
|
+
row[x] = on
|
|
87
|
+
row[size - 1 - x] = on
|
|
88
|
+
if (on) filled++
|
|
89
|
+
}
|
|
90
|
+
cells.push(row)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
* A grid that came out nearly empty or nearly full is not a shape, just a
|
|
95
|
+
* dot or a block. Flip cells in a seed-driven order until the half holds
|
|
96
|
+
* between a third and two thirds of its cells.
|
|
97
|
+
*/
|
|
98
|
+
const total = half * size
|
|
99
|
+
const low = Math.ceil(total / 3)
|
|
100
|
+
const high = Math.floor((total * 2) / 3)
|
|
101
|
+
while (filled < low || filled > high) {
|
|
102
|
+
const y = Math.floor(next() * size)
|
|
103
|
+
const x = Math.floor(next() * half)
|
|
104
|
+
const on = cells[y][x]
|
|
105
|
+
if ((filled < low && on) || (filled > high && !on)) continue
|
|
106
|
+
cells[y][x] = !on
|
|
107
|
+
cells[y][size - 1 - x] = !on
|
|
108
|
+
filled += on ? -1 : 1
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { cells, hue, color: `hsl(${hue} ${saturation}% ${lightness}%)` }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** The filled cells as rectangles, merged along each row: fewer nodes than one per cell. */
|
|
115
|
+
export function identiconRects(cells: boolean[][]): { x: number; y: number; w: number }[] {
|
|
116
|
+
const rects: { x: number; y: number; w: number }[] = []
|
|
117
|
+
cells.forEach((row, y) => {
|
|
118
|
+
let x = 0
|
|
119
|
+
while (x < row.length) {
|
|
120
|
+
if (!row[x]) {
|
|
121
|
+
x++
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
let w = 1
|
|
125
|
+
while (x + w < row.length && row[x + w]) w++
|
|
126
|
+
rects.push({ x, y, w })
|
|
127
|
+
x += w
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
return rects
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface IdenticonSvgOptions extends IdenticonOptions {
|
|
134
|
+
/** Pixel size of the square image. */
|
|
135
|
+
size?: number
|
|
136
|
+
/** Fill behind the pattern, any CSS colour; transparent when unset. */
|
|
137
|
+
background?: string
|
|
138
|
+
/** Empty margin around the grid, as a share of the side. */
|
|
139
|
+
padding?: number
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The identicon as standalone SVG markup, for places a component cannot go:
|
|
144
|
+
* a favicon, a canvas, a notification icon (`data:image/svg+xml,…`).
|
|
145
|
+
*/
|
|
146
|
+
export function identiconSvg(seed: string, options: IdenticonSvgOptions = {}): string {
|
|
147
|
+
const { cells, color } = identicon(seed, options)
|
|
148
|
+
const n = cells.length
|
|
149
|
+
const pad = n * Math.max(0, Math.min(0.4, options.padding ?? 0.12))
|
|
150
|
+
const view = n + pad * 2
|
|
151
|
+
const px = options.size ?? 64
|
|
152
|
+
const bg = options.background ? `<rect width="${view}" height="${view}" fill="${options.background}"/>` : ""
|
|
153
|
+
const shapes = identiconRects(cells)
|
|
154
|
+
.map((r) => `<rect x="${r.x + pad}" y="${r.y + pad}" width="${r.w}" height="1"/>`)
|
|
155
|
+
.join("")
|
|
156
|
+
return (
|
|
157
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="${px}" height="${px}" viewBox="0 0 ${view} ${view}" shape-rendering="crispEdges">` +
|
|
158
|
+
`${bg}<g fill="${color}">${shapes}</g></svg>`
|
|
159
|
+
)
|
|
160
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Column maths for LpMasonry, kept free of the DOM so it can be tested.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface MasonryColumns {
|
|
6
|
+
/**
|
|
7
|
+
* Fixed count, or a table of minimum container widths to counts
|
|
8
|
+
* (`{ 0: 1, 480: 2, 900: 3 }`). Omit to derive the count from `minColumnWidth`.
|
|
9
|
+
*/
|
|
10
|
+
columns?: number | Record<number, number>
|
|
11
|
+
/** Narrowest a column may get when the count is derived. */
|
|
12
|
+
minColumnWidth: number
|
|
13
|
+
/** Upper bound for a derived count, so a wide screen does not shred cards into slivers. */
|
|
14
|
+
maxColumns?: number
|
|
15
|
+
gap: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** How many columns fit a container `width` px wide. Always at least one. */
|
|
19
|
+
export function masonryColumnCount(width: number, options: MasonryColumns): number {
|
|
20
|
+
const { columns, minColumnWidth, maxColumns, gap } = options
|
|
21
|
+
if (typeof columns === "number") return Math.max(1, Math.floor(columns))
|
|
22
|
+
if (columns) {
|
|
23
|
+
let picked = 1
|
|
24
|
+
let best = -Infinity
|
|
25
|
+
for (const [min, count] of Object.entries(columns)) {
|
|
26
|
+
const at = Number(min)
|
|
27
|
+
if (width >= at && at > best) {
|
|
28
|
+
best = at
|
|
29
|
+
picked = count
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return Math.max(1, Math.floor(picked))
|
|
33
|
+
}
|
|
34
|
+
const fit = Math.floor((width + gap) / (Math.max(1, minColumnWidth) + gap))
|
|
35
|
+
return Math.max(1, Math.min(fit, maxColumns ?? Infinity))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MasonryPlacement {
|
|
39
|
+
/** Left edge of each item, in px, in input order. */
|
|
40
|
+
x: number[]
|
|
41
|
+
/** Top edge of each item, in px, in input order. */
|
|
42
|
+
y: number[]
|
|
43
|
+
/** Column each item went to. */
|
|
44
|
+
column: number[]
|
|
45
|
+
/** Width every item is laid out at. */
|
|
46
|
+
columnWidth: number
|
|
47
|
+
/** Height of the tallest column: the container's height. */
|
|
48
|
+
height: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Places items in order, each into the column that is currently shortest
|
|
53
|
+
* (leftmost on a tie). In order, so the first items stay at the top and an
|
|
54
|
+
* item added at the end never reshuffles the ones before it.
|
|
55
|
+
*/
|
|
56
|
+
export function placeMasonry(heights: number[], count: number, width: number, gap: number): MasonryPlacement {
|
|
57
|
+
const columns = Math.max(1, count)
|
|
58
|
+
const columnWidth = Math.max(0, (width - gap * (columns - 1)) / columns)
|
|
59
|
+
const bottoms = new Array<number>(columns).fill(0)
|
|
60
|
+
const x: number[] = []
|
|
61
|
+
const y: number[] = []
|
|
62
|
+
const column: number[] = []
|
|
63
|
+
for (const height of heights) {
|
|
64
|
+
let shortest = 0
|
|
65
|
+
for (let c = 1; c < columns; c += 1) if (bottoms[c]! < bottoms[shortest]! - 0.5) shortest = c
|
|
66
|
+
x.push(shortest * (columnWidth + gap))
|
|
67
|
+
y.push(bottoms[shortest]!)
|
|
68
|
+
column.push(shortest)
|
|
69
|
+
bottoms[shortest] = bottoms[shortest]! + Math.max(0, height) + gap
|
|
70
|
+
}
|
|
71
|
+
const tallest = Math.max(0, ...bottoms)
|
|
72
|
+
return { x, y, column, columnWidth, height: heights.length ? tallest - gap : 0 }
|
|
73
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The arithmetic shared by the kit's meters: LpGauge, LpQuota, LpStatus and
|
|
3
|
+
* LpActivityLine. A reading against a range, the tone it earns, and the colour
|
|
4
|
+
* that tone paints in. Kept in one place so a gauge and a quota showing the same
|
|
5
|
+
* disk agree on when it turns amber, instead of each picking its own breakpoints.
|
|
6
|
+
* The colours themselves are tone.ts's: a meter tone is one of its aliases.
|
|
7
|
+
*/
|
|
8
|
+
import { TONE_ALIASES, TONES, type ToneLook } from "./tone"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The semantic tones a meter speaks in. They name what the reading MEANS, not
|
|
12
|
+
* which colour it is: a theme decides the colour, and `ok` stays the "fine"
|
|
13
|
+
* colour whatever that happens to be.
|
|
14
|
+
*/
|
|
15
|
+
export type MeterTone = "ok" | "warning" | "danger" | "brand" | "neutral"
|
|
16
|
+
|
|
17
|
+
/** One step of a scale: from `at` (in the meter's own units) upward, `tone`. */
|
|
18
|
+
export interface MeterThreshold {
|
|
19
|
+
at: number
|
|
20
|
+
tone: MeterTone
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const METER_TONES: readonly MeterTone[] = ["ok", "warning", "danger", "brand", "neutral"]
|
|
24
|
+
const byMeterTone = (pick: (look: ToneLook) => string) =>
|
|
25
|
+
Object.fromEntries(METER_TONES.map((t) => [t, pick(TONES[TONE_ALIASES[t]])])) as Record<MeterTone, string>
|
|
26
|
+
|
|
27
|
+
/** The solid colour of a tone, as a CSS value a style or an SVG stroke takes. */
|
|
28
|
+
export const TONE_COLOR: Record<MeterTone, string> = byMeterTone((look) => look.color)
|
|
29
|
+
|
|
30
|
+
/** The tinted background of a tone, for pills and highlighted rows. */
|
|
31
|
+
export const TONE_SOFT: Record<MeterTone, string> = byMeterTone((look) => look.soft)
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Where `value` sits between `min` and `max`, as 0–1. An empty or inverted
|
|
35
|
+
* range (no limit known yet) reads as empty rather than as NaN or Infinity,
|
|
36
|
+
* which would otherwise reach an SVG attribute and draw nothing at all.
|
|
37
|
+
*/
|
|
38
|
+
export function meterFraction(value: number, min: number, max: number): number {
|
|
39
|
+
const span = max - min
|
|
40
|
+
if (!(span > 0) || !Number.isFinite(value)) return 0
|
|
41
|
+
return Math.max(0, Math.min(1, (value - min) / span))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The capacity scale every finite resource is read against: comfortable well
|
|
46
|
+
* past half, uncomfortable in the last fifth, urgent in the last tenth. The
|
|
47
|
+
* same breakpoints LpProgress's `auto` tone uses, so the two never disagree
|
|
48
|
+
* about one disk.
|
|
49
|
+
*/
|
|
50
|
+
export function capacityThresholds(
|
|
51
|
+
min: number,
|
|
52
|
+
max: number,
|
|
53
|
+
warnAt = 0.8,
|
|
54
|
+
dangerAt = 0.9,
|
|
55
|
+
): MeterThreshold[] {
|
|
56
|
+
const span = max - min
|
|
57
|
+
return [
|
|
58
|
+
{ at: min + span * warnAt, tone: "warning" },
|
|
59
|
+
{ at: min + span * dangerAt, tone: "danger" },
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The tone a reading earns on a scale. Thresholds are steps, not bands: each
|
|
65
|
+
* one holds from its `at` until the next, and below the first the reading is
|
|
66
|
+
* `base`. Sorted here, so a caller can list them in whatever order reads best.
|
|
67
|
+
*
|
|
68
|
+
* Steps go either way, which is what lets one shape describe both "higher is
|
|
69
|
+
* worse" (load: ok → warning → danger) and "higher is better" (a score:
|
|
70
|
+
* danger from 0, warning from 50, ok from 80).
|
|
71
|
+
*/
|
|
72
|
+
export function toneAt(value: number, thresholds: readonly MeterThreshold[], base: MeterTone = "ok"): MeterTone {
|
|
73
|
+
let tone = base
|
|
74
|
+
for (const step of [...thresholds].sort((a, b) => a.at - b.at)) {
|
|
75
|
+
if (value >= step.at) tone = step.tone
|
|
76
|
+
}
|
|
77
|
+
return tone
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* ── Bar (LpProgress, LpQuota) ──────────────────────────────────────────── */
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The track a bar meter fills, shared so LpProgress's bar and LpQuota's read as
|
|
84
|
+
* one family. Each adds its own clipping: a progress bar clips its fill, a
|
|
85
|
+
* quota lets its limit mark stand out past the track.
|
|
86
|
+
*/
|
|
87
|
+
export const METER_TRACK = "h-1.5 w-full rounded-pill bg-surface-soft"
|
|
88
|
+
|
|
89
|
+
/** The row above a bar: what is measured at the start, the reading at the end. Each sets its own size and gap. */
|
|
90
|
+
export const METER_HEADER = "flex items-baseline justify-between"
|
|
91
|
+
|
|
92
|
+
/* ── Arc geometry (LpGauge) ─────────────────────────────────────────────── */
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* A point on a circle, with the angle in degrees clockwise from twelve
|
|
96
|
+
* o'clock — the way a dial is read, so -90 is nine o'clock and 90 is three.
|
|
97
|
+
*/
|
|
98
|
+
export function polar(cx: number, cy: number, r: number, deg: number): { x: number; y: number } {
|
|
99
|
+
const rad = (deg * Math.PI) / 180
|
|
100
|
+
return { x: cx + r * Math.sin(rad), y: cy - r * Math.cos(rad) }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** An SVG path along a circle from one angle to another, clockwise. */
|
|
104
|
+
export function arcPath(cx: number, cy: number, r: number, from: number, to: number): string {
|
|
105
|
+
const a = polar(cx, cy, r, from)
|
|
106
|
+
const b = polar(cx, cy, r, to)
|
|
107
|
+
const large = to - from > 180 ? 1 : 0
|
|
108
|
+
const f = (n: number) => Number(n.toFixed(3))
|
|
109
|
+
return `M ${f(a.x)} ${f(a.y)} A ${f(r)} ${f(r)} 0 ${large} 1 ${f(b.x)} ${f(b.y)}`
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The sweep of each gauge shape, as [start, end] angles. A semicircle reads
|
|
114
|
+
* left to right across the top; the full dial opens at the bottom, a 270°
|
|
115
|
+
* sweep, because a closed circle has no visible start or end and that is
|
|
116
|
+
* LpProgress's ring, a different thing (progress toward done, not a reading
|
|
117
|
+
* within a range).
|
|
118
|
+
*/
|
|
119
|
+
export const GAUGE_SWEEP: Record<"semi" | "full", readonly [number, number]> = {
|
|
120
|
+
semi: [-90, 90],
|
|
121
|
+
full: [-135, 135],
|
|
122
|
+
}
|
package/src/components/otp.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* model. reka empties a cell by deleting its slot (numeric mode) or setting it
|
|
4
4
|
* to "", so the array can have holes in the middle.
|
|
5
5
|
*/
|
|
6
|
+
import { groupCode } from "./fieldText"
|
|
7
|
+
|
|
6
8
|
export type OtpCells = (string | number | undefined)[]
|
|
7
9
|
|
|
8
10
|
/** The code typed so far: the filled cells in order. */
|
|
@@ -36,10 +38,19 @@ export function upperCells<T extends OtpCells>(cells: T): T {
|
|
|
36
38
|
* The cell indexes a group separator follows: after every `group` cells, never
|
|
37
39
|
* after the last. Eight characters are read and copied as two blocks of four,
|
|
38
40
|
* which is how such codes are printed, far more easily than as one row.
|
|
41
|
+
*
|
|
42
|
+
* The blocks are LpCodeDisplay's (`groupCode`), so a code shown by one and
|
|
43
|
+
* typed into the other breaks in the same places; exact sizes (`[4, 6, 5]`)
|
|
44
|
+
* work here too. Unlike there, no `group` means no breaks: an input does not
|
|
45
|
+
* guess a grouping the printed code may not have.
|
|
39
46
|
*/
|
|
40
|
-
export function groupBreaks(length: number, group: number | undefined): Set<number> {
|
|
47
|
+
export function groupBreaks(length: number, group: number | number[] | undefined): Set<number> {
|
|
41
48
|
const breaks = new Set<number>()
|
|
42
|
-
if (
|
|
43
|
-
|
|
49
|
+
if (Array.isArray(group) ? !group.some((n) => n > 0) : !group || group < 1) return breaks
|
|
50
|
+
let at = 0
|
|
51
|
+
for (const block of groupCode("x".repeat(Math.max(0, length)), group)) {
|
|
52
|
+
at += block.length
|
|
53
|
+
if (at < length) breaks.add(at - 1)
|
|
54
|
+
}
|
|
44
55
|
return breaks
|
|
45
56
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The state of a run of jobs, read off the jobs themselves. Pure, so the
|
|
3
|
+
* rules LpPipeline draws by (which connector is filled, which step is
|
|
4
|
+
* "current", what a run as a whole amounts to) are testable without a DOM.
|
|
5
|
+
*/
|
|
6
|
+
import { toEpoch } from "./timeSpan"
|
|
7
|
+
|
|
8
|
+
export type StepStatus = "pending" | "running" | "success" | "failed" | "skipped" | "cancelled"
|
|
9
|
+
|
|
10
|
+
export interface PipelineStep {
|
|
11
|
+
label: string
|
|
12
|
+
/** Stable id: what `expanded` and the `step` slot address. Defaults to the index. */
|
|
13
|
+
key?: string
|
|
14
|
+
status: StepStatus
|
|
15
|
+
/** A line under the label: the command, the host, the reason it was skipped. */
|
|
16
|
+
description?: string
|
|
17
|
+
/** Replaces the glyph while the step is pending (a step that is "deploy" more than "3"). */
|
|
18
|
+
icon?: string
|
|
19
|
+
/**
|
|
20
|
+
* Time the step took, in ms. When it is not known yet, `startedAt` (and
|
|
21
|
+
* `finishedAt`) let the pipeline work it out — and keep a running step's
|
|
22
|
+
* clock ticking without the app re-sending it every second.
|
|
23
|
+
*/
|
|
24
|
+
duration?: number
|
|
25
|
+
startedAt?: Date | string | number
|
|
26
|
+
finishedAt?: Date | string | number
|
|
27
|
+
/** Jobs inside this one, shown when the step is expanded (vertical layout). */
|
|
28
|
+
steps?: PipelineStep[]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Done, one way or another: nothing more will happen to the step. */
|
|
32
|
+
export function isSettled(status: StepStatus): boolean {
|
|
33
|
+
return status !== "pending" && status !== "running"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Whether the line leaving a step is drawn as travelled. A step that passed
|
|
38
|
+
* or was skipped hands over to the next; one that failed or was cancelled is
|
|
39
|
+
* where the run stopped, so the line after it stays empty — the break in the
|
|
40
|
+
* line is the place to look.
|
|
41
|
+
*/
|
|
42
|
+
export function connectorFilled(status: StepStatus): boolean {
|
|
43
|
+
return status === "success" || status === "skipped"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The step a viewer is following: the running one, else the first that has
|
|
48
|
+
* not run yet after something has, else the last failure. -1 when the run has
|
|
49
|
+
* not started or is cleanly over.
|
|
50
|
+
*/
|
|
51
|
+
export function currentStepIndex(steps: readonly Pick<PipelineStep, "status">[]): number {
|
|
52
|
+
const running = steps.findIndex((s) => s.status === "running")
|
|
53
|
+
if (running !== -1) return running
|
|
54
|
+
const failed = steps.findIndex((s) => s.status === "failed")
|
|
55
|
+
if (failed !== -1) return failed
|
|
56
|
+
const started = steps.some((s) => isSettled(s.status))
|
|
57
|
+
if (!started) return -1
|
|
58
|
+
return steps.findIndex((s) => s.status === "pending")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* What the run amounts to. A failure anywhere outranks everything (one red
|
|
63
|
+
* job fails the build), then work in progress, then a cancellation; a run of
|
|
64
|
+
* passes and skips is a pass. Nothing started is pending.
|
|
65
|
+
*/
|
|
66
|
+
export function overallStatus(steps: readonly Pick<PipelineStep, "status">[]): StepStatus {
|
|
67
|
+
if (steps.length === 0) return "pending"
|
|
68
|
+
const has = (s: StepStatus) => steps.some((step) => step.status === s)
|
|
69
|
+
if (has("failed")) return "failed"
|
|
70
|
+
if (has("running")) return "running"
|
|
71
|
+
if (has("cancelled")) return "cancelled"
|
|
72
|
+
if (steps.every((s) => s.status === "skipped")) return "skipped"
|
|
73
|
+
if (steps.every((s) => s.status === "success" || s.status === "skipped")) return "success"
|
|
74
|
+
if (steps.some((s) => isSettled(s.status))) return "running"
|
|
75
|
+
return "pending"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Ms a step has taken, as of `now`. An explicit `duration` wins; otherwise
|
|
80
|
+
* from `startedAt` to `finishedAt`, or to `now` while it is still running.
|
|
81
|
+
* null when there is nothing to say (pending, or no times given).
|
|
82
|
+
*/
|
|
83
|
+
export function stepElapsed(step: PipelineStep, now: number): number | null {
|
|
84
|
+
if (typeof step.duration === "number" && Number.isFinite(step.duration)) return Math.max(0, step.duration)
|
|
85
|
+
const start = toEpoch(step.startedAt)
|
|
86
|
+
if (Number.isNaN(start)) return null
|
|
87
|
+
const end = toEpoch(step.finishedAt)
|
|
88
|
+
if (!Number.isNaN(end)) return Math.max(0, end - start)
|
|
89
|
+
return step.status === "running" ? Math.max(0, now - start) : null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** "2/5": finished children out of all, for a step whose inside is folded away. */
|
|
93
|
+
export function childProgress(step: PipelineStep): { done: number; total: number } | null {
|
|
94
|
+
if (!step.steps?.length) return null
|
|
95
|
+
return { done: step.steps.filter((s) => isSettled(s.status)).length, total: step.steps.length }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The key a step is addressed by. */
|
|
99
|
+
export function stepKey(step: Pick<PipelineStep, "key">, index: number): string {
|
|
100
|
+
return step.key ?? String(index)
|
|
101
|
+
}
|