@leavepulse/ui 0.69.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.js +107 -138
- 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/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 +143 -154
- 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 +9 -32
- 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/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,46 @@
|
|
|
1
|
+
import type { MeterTone } from "./meter"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The states a service, a bot or a person can be in, and what each one looks
|
|
5
|
+
* like. Named by meaning so a table of servers and a header pill agree on what
|
|
6
|
+
* "degraded" is without each picking a colour.
|
|
7
|
+
*/
|
|
8
|
+
export type StatusKind =
|
|
9
|
+
| "online"
|
|
10
|
+
| "offline"
|
|
11
|
+
| "degraded"
|
|
12
|
+
| "down"
|
|
13
|
+
| "maintenance"
|
|
14
|
+
| "starting"
|
|
15
|
+
| "unknown"
|
|
16
|
+
|
|
17
|
+
export interface StatusLook {
|
|
18
|
+
tone: MeterTone
|
|
19
|
+
/**
|
|
20
|
+
* Whether the light breathes. Reserved for states that are LIVE (online:
|
|
21
|
+
* something is answering right now) or IN MOTION (starting: it will not stay
|
|
22
|
+
* like this) or demand attention (down). A pulse on every row would make the
|
|
23
|
+
* pulse mean nothing, so steady states stay still.
|
|
24
|
+
*/
|
|
25
|
+
pulse: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const STATUS_LOOK: Record<StatusKind, StatusLook> = {
|
|
29
|
+
online: { tone: "ok", pulse: true },
|
|
30
|
+
offline: { tone: "neutral", pulse: false },
|
|
31
|
+
degraded: { tone: "warning", pulse: false },
|
|
32
|
+
down: { tone: "danger", pulse: true },
|
|
33
|
+
maintenance: { tone: "brand", pulse: false },
|
|
34
|
+
starting: { tone: "brand", pulse: true },
|
|
35
|
+
unknown: { tone: "neutral", pulse: false },
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const STATUS_LABELS: Record<StatusKind, string> = {
|
|
39
|
+
online: "Online",
|
|
40
|
+
offline: "Offline",
|
|
41
|
+
degraded: "Degraded",
|
|
42
|
+
down: "Down",
|
|
43
|
+
maintenance: "Maintenance",
|
|
44
|
+
starting: "Starting",
|
|
45
|
+
unknown: "Unknown",
|
|
46
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { computed } from "vue"
|
|
2
|
+
import { countValues } from "./tableFilter"
|
|
3
|
+
|
|
4
|
+
export function useTableFacets<T extends Record<string, unknown>>(options: {
|
|
5
|
+
rows: () => T[]
|
|
6
|
+
columns: () => string[]
|
|
7
|
+
hidden: () => Record<string, ReadonlySet<unknown>>
|
|
8
|
+
}) {
|
|
9
|
+
// Each column stays lazy: opening one filter must not compute every other filter.
|
|
10
|
+
const columns = computed(() => new Map(options.columns().map((key) => [key, {
|
|
11
|
+
distinct: computed(() => [...new Set(options.rows().map((row) => row[key]))]),
|
|
12
|
+
counts: computed(() => {
|
|
13
|
+
const others = Object.entries(options.hidden()).filter(([column, values]) => column !== key && values.size)
|
|
14
|
+
const rows = others.length
|
|
15
|
+
? options.rows().filter((row) => others.every(([column, values]) => !values.has(row[column])))
|
|
16
|
+
: options.rows()
|
|
17
|
+
return countValues(rows, key)
|
|
18
|
+
}),
|
|
19
|
+
}])))
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
distinct: (key: string): unknown[] => columns.value.get(key)?.distinct.value ?? [],
|
|
23
|
+
counts: (key: string): Map<unknown, number> | undefined => columns.value.get(key)?.counts.value,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { graphemes } from "./scramble"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Splitting text into the pieces an entrance animates (LpTextReveal).
|
|
5
|
+
*
|
|
6
|
+
* Words are the unit of identity: each gets a key from its text and how many
|
|
7
|
+
* times that text appeared before it, so when the sentence changes the words
|
|
8
|
+
* that are still there keep their key (and their DOM node, and do not replay),
|
|
9
|
+
* and only the new ones animate in. Keying by position instead would replay
|
|
10
|
+
* every word after an insertion.
|
|
11
|
+
*
|
|
12
|
+
* Whitespace is kept as its own piece and is never animated: it stays a real
|
|
13
|
+
* space in the flow, which is what lets the line wrap between words. Letters
|
|
14
|
+
* of one word stay inside that word's box, so letter mode never breaks a word
|
|
15
|
+
* across lines.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export interface TextWord {
|
|
19
|
+
kind: "word"
|
|
20
|
+
/** Stable across edits for the same word in the same order. */
|
|
21
|
+
key: string
|
|
22
|
+
text: string
|
|
23
|
+
/** Letters (graphemes), or the whole word as one piece in word mode. */
|
|
24
|
+
parts: string[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface TextSpace {
|
|
28
|
+
kind: "space"
|
|
29
|
+
key: string
|
|
30
|
+
text: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type TextPiece = TextWord | TextSpace
|
|
34
|
+
|
|
35
|
+
export function splitText(text: string, by: "word" | "letter"): TextPiece[] {
|
|
36
|
+
const pieces: TextPiece[] = []
|
|
37
|
+
const seen = new Map<string, number>()
|
|
38
|
+
let spaces = 0
|
|
39
|
+
for (const token of text.split(/(\s+)/)) {
|
|
40
|
+
if (!token) continue
|
|
41
|
+
if (/^\s+$/.test(token)) {
|
|
42
|
+
pieces.push({ kind: "space", key: `s${spaces++}`, text: token })
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
const n = seen.get(token) ?? 0
|
|
46
|
+
seen.set(token, n + 1)
|
|
47
|
+
pieces.push({ kind: "word", key: `${token}#${n}`, text: token, parts: by === "letter" ? graphemes(token) : [token] })
|
|
48
|
+
}
|
|
49
|
+
return pieces
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Keys of the words in `pieces`. */
|
|
53
|
+
export function wordKeys(pieces: TextPiece[]): Set<string> {
|
|
54
|
+
return new Set(pieces.filter((p) => p.kind === "word").map((p) => p.key))
|
|
55
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Spans of time as people read them: "0:42", "2d 3h", "1m 12s".
|
|
3
|
+
*
|
|
4
|
+
* Pure functions, shared by LpCountdown (time left) and LpPipeline (time
|
|
5
|
+
* taken), so a job that ran for 72 seconds and a code that can be resent in 72
|
|
6
|
+
* seconds are written the same way. Nothing here reads the clock: callers pass
|
|
7
|
+
* milliseconds, which is what makes the formatting testable and lets the
|
|
8
|
+
* components decide how often to look at the time.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface TimeSplit {
|
|
12
|
+
days: number
|
|
13
|
+
hours: number
|
|
14
|
+
minutes: number
|
|
15
|
+
seconds: number
|
|
16
|
+
/** Whole milliseconds left over below a second. */
|
|
17
|
+
ms: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type TimeUnit = "d" | "h" | "m" | "s"
|
|
21
|
+
|
|
22
|
+
/** Unit suffixes, so an app can write "2 д 3 ч" without reformatting. */
|
|
23
|
+
export type TimeUnitLabels = Record<TimeUnit, string>
|
|
24
|
+
|
|
25
|
+
export const DEFAULT_UNIT_LABELS: TimeUnitLabels = { d: "d", h: "h", m: "m", s: "s" }
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* How a span is written.
|
|
29
|
+
*
|
|
30
|
+
* `clock` is for a short wait the eye follows second by second ("0:42",
|
|
31
|
+
* "1:05:09"); `units` is for a long one read at a glance ("2d 3h"); `auto` is
|
|
32
|
+
* clock below an hour and units above it, which is what both examples above
|
|
33
|
+
* want without the caller choosing.
|
|
34
|
+
*/
|
|
35
|
+
export type TimeFormat = "auto" | "clock" | "units"
|
|
36
|
+
|
|
37
|
+
const SECOND = 1000
|
|
38
|
+
const MINUTE = 60 * SECOND
|
|
39
|
+
const HOUR = 60 * MINUTE
|
|
40
|
+
const DAY = 24 * HOUR
|
|
41
|
+
|
|
42
|
+
/** Splits a span into calendar-free parts. Negative spans count as zero. */
|
|
43
|
+
export function splitTime(ms: number): TimeSplit {
|
|
44
|
+
const total = Number.isFinite(ms) ? Math.max(0, Math.floor(ms)) : 0
|
|
45
|
+
return {
|
|
46
|
+
days: Math.floor(total / DAY),
|
|
47
|
+
hours: Math.floor((total % DAY) / HOUR),
|
|
48
|
+
minutes: Math.floor((total % HOUR) / MINUTE),
|
|
49
|
+
seconds: Math.floor((total % MINUTE) / SECOND),
|
|
50
|
+
ms: total % SECOND,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** One printed piece of a span: a number and what follows it. */
|
|
55
|
+
export interface TimePart {
|
|
56
|
+
unit: TimeUnit
|
|
57
|
+
value: number
|
|
58
|
+
/** Digits to pad to: 2 for a clock's minutes and seconds after the first. */
|
|
59
|
+
pad: number
|
|
60
|
+
/** Text printed after the number: a unit suffix, or ":" between clock fields. */
|
|
61
|
+
after: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A countdown rounds UP: with 41.2 s left it says 0:42, so the display only
|
|
66
|
+
* reaches 0:00 at the moment the wait is over. Rounding down would show 0:00
|
|
67
|
+
* for most of the last second while the thing is still not ready.
|
|
68
|
+
*/
|
|
69
|
+
export function ceilToSecond(ms: number): number {
|
|
70
|
+
if (!Number.isFinite(ms) || ms <= 0) return 0
|
|
71
|
+
return Math.ceil(ms / SECOND) * SECOND
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Which of `clock` and `units` an `auto` span resolves to. */
|
|
75
|
+
export function resolveFormat(ms: number, format: TimeFormat): "clock" | "units" {
|
|
76
|
+
if (format !== "auto") return format
|
|
77
|
+
return ms >= HOUR ? "units" : "clock"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The parts of a span in the given format.
|
|
82
|
+
*
|
|
83
|
+
* Units keep the two largest non-zero places ("2d 3h", not "2d 3h 4m 5s"): a
|
|
84
|
+
* long wait is read for its size, and the extra places only flicker. A span
|
|
85
|
+
* under a minute keeps its seconds, and zero is "0s" rather than nothing.
|
|
86
|
+
* The clock always has minutes and seconds and grows hours when it needs
|
|
87
|
+
* them; days fold into hours, since a clock reading of days is not a clock.
|
|
88
|
+
*/
|
|
89
|
+
export function timeParts(
|
|
90
|
+
ms: number,
|
|
91
|
+
format: TimeFormat = "auto",
|
|
92
|
+
labels: TimeUnitLabels = DEFAULT_UNIT_LABELS,
|
|
93
|
+
places = 2,
|
|
94
|
+
): TimePart[] {
|
|
95
|
+
const t = splitTime(ms)
|
|
96
|
+
if (resolveFormat(ms, format) === "clock") {
|
|
97
|
+
const hours = t.days * 24 + t.hours
|
|
98
|
+
const parts: TimePart[] = []
|
|
99
|
+
if (hours > 0) parts.push({ unit: "h", value: hours, pad: 1, after: ":" })
|
|
100
|
+
parts.push({ unit: "m", value: t.minutes, pad: hours > 0 ? 2 : 1, after: ":" })
|
|
101
|
+
parts.push({ unit: "s", value: t.seconds, pad: 2, after: "" })
|
|
102
|
+
return parts
|
|
103
|
+
}
|
|
104
|
+
const all: Array<[TimeUnit, number]> = [["d", t.days], ["h", t.hours], ["m", t.minutes], ["s", t.seconds]]
|
|
105
|
+
const first = all.findIndex(([, v]) => v > 0)
|
|
106
|
+
if (first === -1) return [{ unit: "s", value: 0, pad: 1, after: labels.s }]
|
|
107
|
+
return all
|
|
108
|
+
.slice(first, first + Math.max(1, places))
|
|
109
|
+
// A trailing zero place says nothing ("2d 0h"); a middle one would, but
|
|
110
|
+
// with two places there is no middle.
|
|
111
|
+
.filter(([, v], i) => i === 0 || v > 0)
|
|
112
|
+
.map(([unit, value]) => ({ unit, value, pad: 1, after: labels[unit] }))
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Pads a part's number the way the clock prints it. */
|
|
116
|
+
export function padPart(part: Pick<TimePart, "value" | "pad">): string {
|
|
117
|
+
return String(part.value).padStart(part.pad, "0")
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** The span as one string — for titles, aria text and anything not animated. */
|
|
121
|
+
export function formatTime(
|
|
122
|
+
ms: number,
|
|
123
|
+
format: TimeFormat = "auto",
|
|
124
|
+
labels: TimeUnitLabels = DEFAULT_UNIT_LABELS,
|
|
125
|
+
places = 2,
|
|
126
|
+
): string {
|
|
127
|
+
const parts = timeParts(ms, format, labels, places)
|
|
128
|
+
if (resolveFormat(ms, format) === "clock") return parts.map((p) => padPart(p) + p.after).join("")
|
|
129
|
+
return parts.map((p) => `${p.value}${p.after}`).join(" ")
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Time a job took, as a CI log writes it: "850ms" under a second (a step that
|
|
134
|
+
* fast is still worth telling apart from one that took nothing), then "42s",
|
|
135
|
+
* "1m 12s", "1h 3m".
|
|
136
|
+
*/
|
|
137
|
+
export function formatElapsed(ms: number, labels: TimeUnitLabels = DEFAULT_UNIT_LABELS): string {
|
|
138
|
+
if (!Number.isFinite(ms) || ms < 0) return ""
|
|
139
|
+
if (ms < SECOND) return `${Math.round(ms)}ms`
|
|
140
|
+
return formatTime(ms, "units", labels)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Milliseconds until the display next changes, so a ticking timer wakes on the
|
|
145
|
+
* second boundary instead of every 1000 ms from whenever it started. Started
|
|
146
|
+
* mid-second, a plain interval shows each value late by that fraction — and
|
|
147
|
+
* keeps drifting as timers fire late. Never zero: a tick that lands exactly on
|
|
148
|
+
* the boundary waits a whole second rather than spinning.
|
|
149
|
+
*/
|
|
150
|
+
export function msToNextSecond(remaining: number): number {
|
|
151
|
+
if (!Number.isFinite(remaining) || remaining <= 0) return SECOND
|
|
152
|
+
const rest = remaining % SECOND
|
|
153
|
+
return rest === 0 ? SECOND : rest
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Remaining time worth saying aloud. A timer re-read every second is noise to
|
|
158
|
+
* a screen reader; one that speaks when it crosses a round mark ("5 minutes
|
|
159
|
+
* left", "10 seconds left") is information. Returns the mark crossed going
|
|
160
|
+
* from `before` to `after`, or null.
|
|
161
|
+
*/
|
|
162
|
+
export const DEFAULT_MILESTONES = [3600_000, 1800_000, 600_000, 300_000, 60_000, 30_000, 10_000]
|
|
163
|
+
|
|
164
|
+
export function crossedMilestone(
|
|
165
|
+
before: number,
|
|
166
|
+
after: number,
|
|
167
|
+
milestones: readonly number[] = DEFAULT_MILESTONES,
|
|
168
|
+
): number | null {
|
|
169
|
+
if (!(after < before)) return null
|
|
170
|
+
let hit: number | null = null
|
|
171
|
+
for (const mark of milestones) {
|
|
172
|
+
if (before > mark && after <= mark) hit = hit === null ? mark : Math.min(hit, mark)
|
|
173
|
+
}
|
|
174
|
+
return hit
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** A date-ish input as epoch milliseconds, or NaN when it is not one. */
|
|
178
|
+
export function toEpoch(value: Date | string | number | null | undefined): number {
|
|
179
|
+
if (value === null || value === undefined || value === "") return Number.NaN
|
|
180
|
+
if (value instanceof Date) return value.getTime()
|
|
181
|
+
if (typeof value === "number") return value
|
|
182
|
+
return new Date(value).getTime()
|
|
183
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The kit's semantic tones, once.
|
|
3
|
+
*
|
|
4
|
+
* Badges, alerts, toasts, loaders, meters, log levels and notification rows all
|
|
5
|
+
* colour themselves by what something MEANS: fine, worrying, broken, brand,
|
|
6
|
+
* nothing in particular. Each used to keep its own little map from a tone name
|
|
7
|
+
* to classes, and the maps drifted: a warning badge was tinted `bg-accent/15`
|
|
8
|
+
* while a warning row used `--color-accent-soft`, and a toaster called it
|
|
9
|
+
* `error` where an alert called it `danger`. Here every component looks the
|
|
10
|
+
* tone up, so the same meaning paints the same colour everywhere, and a theme
|
|
11
|
+
* that recolours `accent` recolours every warning at once.
|
|
12
|
+
*
|
|
13
|
+
* Class strings are spelled out whole on purpose. Tailwind finds classes by
|
|
14
|
+
* scanning source text, so a class assembled at runtime (`bg-${name}`) would
|
|
15
|
+
* never be emitted and the tone would silently paint nothing.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** The canonical tones. Everything else is an alias of one of these. */
|
|
19
|
+
export type Tone = "neutral" | "brand" | "success" | "warning" | "danger"
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Every name a component's `tone` prop accepts. The aliases are the names the
|
|
23
|
+
* kit's components grew up with (a meter says `ok`, a button palette says
|
|
24
|
+
* `action`, a toast says `error`), kept so no caller has to rename anything.
|
|
25
|
+
*/
|
|
26
|
+
export type ToneName = Tone | "info" | "ok" | "action" | "accent" | "error" | "muted"
|
|
27
|
+
|
|
28
|
+
/** Which canonical tone each name stands for. */
|
|
29
|
+
export const TONE_ALIASES: Readonly<Record<ToneName, Tone>> = {
|
|
30
|
+
neutral: "neutral",
|
|
31
|
+
muted: "neutral",
|
|
32
|
+
brand: "brand",
|
|
33
|
+
info: "brand",
|
|
34
|
+
success: "success",
|
|
35
|
+
ok: "success",
|
|
36
|
+
action: "success",
|
|
37
|
+
warning: "warning",
|
|
38
|
+
accent: "warning",
|
|
39
|
+
danger: "danger",
|
|
40
|
+
error: "danger",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** What a component needs to paint one tone. */
|
|
44
|
+
export interface ToneLook {
|
|
45
|
+
/** The solid colour, as a CSS value for a style or an SVG stroke. */
|
|
46
|
+
color: string
|
|
47
|
+
/** The tinted background colour, as a CSS value. */
|
|
48
|
+
soft: string
|
|
49
|
+
/** The solid colour as text: an icon, a label, a spinner. */
|
|
50
|
+
text: string
|
|
51
|
+
/** The solid colour as a fill: a bar, a dot, a rail. */
|
|
52
|
+
bg: string
|
|
53
|
+
/** The tinted background alone. */
|
|
54
|
+
softBg: string
|
|
55
|
+
/**
|
|
56
|
+
* A chip: the tinted background with text that reads on it. Not always
|
|
57
|
+
* `softBg + text`: neutral text on its soft surface steps up to
|
|
58
|
+
* muted-strong, because plain muted on surface-soft is too faint to read.
|
|
59
|
+
*/
|
|
60
|
+
//
|
|
61
|
+
// The text is the tone mixed 60/40 with ink, not the tone itself: a light
|
|
62
|
+
// theme's green or amber on its own soft tint read at 2.8 to 3.4:1, under
|
|
63
|
+
// the 4.5 small text needs. Mixed toward ink it darkens on light themes and
|
|
64
|
+
// lightens on dark ones, and stays recognisably the tone (5.6 to 6.4:1 on
|
|
65
|
+
// light, higher on dark).
|
|
66
|
+
chip: string
|
|
67
|
+
/** The solid colour as a border. */
|
|
68
|
+
border: string
|
|
69
|
+
/** A quiet panel in the tone: a faint tint with a faint edge (an alert, a callout). */
|
|
70
|
+
panel: string
|
|
71
|
+
/** Selected text highlighted in the tone. */
|
|
72
|
+
selection: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const TONES: Readonly<Record<Tone, ToneLook>> = {
|
|
76
|
+
neutral: {
|
|
77
|
+
color: "var(--color-muted)",
|
|
78
|
+
soft: "var(--color-surface-soft)",
|
|
79
|
+
text: "text-muted",
|
|
80
|
+
bg: "bg-muted",
|
|
81
|
+
softBg: "bg-surface-soft",
|
|
82
|
+
chip: "bg-surface-soft text-muted-strong",
|
|
83
|
+
border: "border-line-strong",
|
|
84
|
+
panel: "border-line bg-surface-soft",
|
|
85
|
+
selection: "selection:bg-muted/40",
|
|
86
|
+
},
|
|
87
|
+
brand: {
|
|
88
|
+
color: "var(--color-brand)",
|
|
89
|
+
soft: "var(--color-brand-soft)",
|
|
90
|
+
text: "text-brand",
|
|
91
|
+
bg: "bg-brand",
|
|
92
|
+
softBg: "bg-brand-soft",
|
|
93
|
+
chip: "bg-brand-soft text-[color-mix(in_oklab,var(--color-brand)_60%,var(--color-ink))]",
|
|
94
|
+
border: "border-brand",
|
|
95
|
+
panel: "border-brand/30 bg-brand/10",
|
|
96
|
+
selection: "selection:bg-brand/40",
|
|
97
|
+
},
|
|
98
|
+
success: {
|
|
99
|
+
color: "var(--color-action)",
|
|
100
|
+
soft: "var(--color-action-soft)",
|
|
101
|
+
text: "text-action",
|
|
102
|
+
bg: "bg-action",
|
|
103
|
+
softBg: "bg-action-soft",
|
|
104
|
+
chip: "bg-action-soft text-[color-mix(in_oklab,var(--color-action)_60%,var(--color-ink))]",
|
|
105
|
+
border: "border-action",
|
|
106
|
+
panel: "border-action/30 bg-action/10",
|
|
107
|
+
selection: "selection:bg-action/40",
|
|
108
|
+
},
|
|
109
|
+
warning: {
|
|
110
|
+
color: "var(--color-accent)",
|
|
111
|
+
soft: "var(--color-accent-soft)",
|
|
112
|
+
text: "text-accent",
|
|
113
|
+
bg: "bg-accent",
|
|
114
|
+
softBg: "bg-accent-soft",
|
|
115
|
+
chip: "bg-accent-soft text-[color-mix(in_oklab,var(--color-accent)_60%,var(--color-ink))]",
|
|
116
|
+
border: "border-accent",
|
|
117
|
+
panel: "border-accent/30 bg-accent/10",
|
|
118
|
+
selection: "selection:bg-accent/40",
|
|
119
|
+
},
|
|
120
|
+
danger: {
|
|
121
|
+
color: "var(--color-danger)",
|
|
122
|
+
soft: "var(--color-danger-soft)",
|
|
123
|
+
text: "text-danger",
|
|
124
|
+
bg: "bg-danger",
|
|
125
|
+
softBg: "bg-danger-soft",
|
|
126
|
+
chip: "bg-danger-soft text-[color-mix(in_oklab,var(--color-danger)_60%,var(--color-ink))]",
|
|
127
|
+
border: "border-danger",
|
|
128
|
+
panel: "border-danger/30 bg-danger/10",
|
|
129
|
+
selection: "selection:bg-danger/40",
|
|
130
|
+
},
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The canonical tone a name stands for. An unknown name (a typo, a tone from a
|
|
135
|
+
* newer kit read by an older one) falls back rather than painting nothing.
|
|
136
|
+
*/
|
|
137
|
+
export function toneOf(name: string | null | undefined, fallback: Tone = "neutral"): Tone {
|
|
138
|
+
return (name && TONE_ALIASES[name as ToneName]) || fallback
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** How a tone is painted, by any of its names. */
|
|
142
|
+
export function toneLook(name: string | null | undefined, fallback: Tone = "neutral"): ToneLook {
|
|
143
|
+
return TONES[toneOf(name, fallback)]
|
|
144
|
+
}
|
package/src/components/tour.ts
CHANGED
|
@@ -82,7 +82,8 @@ export function openContextMenu(el: Element): () => void {
|
|
|
82
82
|
)
|
|
83
83
|
return () => {
|
|
84
84
|
const outside = { bubbles: true, cancelable: true, clientX: 0, clientY: 0 }
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
const body = el.ownerDocument.body
|
|
86
|
+
body.dispatchEvent(new PointerEvent("pointerdown", outside))
|
|
87
|
+
body.dispatchEvent(new PointerEvent("pointerup", outside))
|
|
87
88
|
}
|
|
88
89
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { Random } from "./scramble"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The typewriter's timeline as a pure state machine (LpTypewriter).
|
|
5
|
+
*
|
|
6
|
+
* One call advances one keystroke and says how long to wait before the next,
|
|
7
|
+
* so the component only runs a timer, and a test can walk the whole cycle with
|
|
8
|
+
* a fixed random source. Strings are passed as grapheme arrays: an emoji is one
|
|
9
|
+
* keystroke, not two halves.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export interface TypewriterState {
|
|
13
|
+
/** The string being shown. */
|
|
14
|
+
index: number
|
|
15
|
+
/** How many of its characters are shown. */
|
|
16
|
+
length: number
|
|
17
|
+
phase: "typing" | "holding" | "erasing" | "done"
|
|
18
|
+
/** The string to type once erasing has gone far enough. */
|
|
19
|
+
to: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TypewriterOptions {
|
|
23
|
+
/** Ms per typed character, before variance. */
|
|
24
|
+
speed: number
|
|
25
|
+
/** Ms per erased character. Erasing is steady: nobody holds backspace unevenly. */
|
|
26
|
+
eraseSpeed: number
|
|
27
|
+
/** Ms a finished string stays before it is erased. */
|
|
28
|
+
hold: number
|
|
29
|
+
/** Start over after the last string instead of stopping on it. */
|
|
30
|
+
loop: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface TypewriterStep {
|
|
34
|
+
state: TypewriterState
|
|
35
|
+
/** Ms until the next call. Infinity: nothing more to do. */
|
|
36
|
+
wait: number
|
|
37
|
+
/** Set when this step finished typing a string: its index. */
|
|
38
|
+
typed?: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const typewriterStart = (): TypewriterState => ({ index: 0, length: 0, phase: "typing", to: 0 })
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A keystroke's delay: the base speed with some unevenness, and a pause after
|
|
45
|
+
* punctuation, which is where a person typing actually stops.
|
|
46
|
+
*/
|
|
47
|
+
export function keystrokeDelay(char: string, speed: number, random: Random): number {
|
|
48
|
+
const jitter = speed * (0.55 + 0.9 * random())
|
|
49
|
+
if (/[.!?…]/.test(char)) return jitter + speed * 5
|
|
50
|
+
if (/[,;:]/.test(char)) return jitter + speed * 2.5
|
|
51
|
+
return jitter
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Characters two strings share at the start: erasing stops there and typing goes on from it. */
|
|
55
|
+
export function sharedPrefix(a: string[], b: string[]): number {
|
|
56
|
+
let n = 0
|
|
57
|
+
while (n < a.length && n < b.length && a[n] === b[n]) n++
|
|
58
|
+
return n
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function typewriterNext(
|
|
62
|
+
state: TypewriterState,
|
|
63
|
+
strings: string[][],
|
|
64
|
+
options: TypewriterOptions,
|
|
65
|
+
random: Random = Math.random,
|
|
66
|
+
): TypewriterStep {
|
|
67
|
+
const count = strings.length
|
|
68
|
+
if (!count) return { state: { ...state, phase: "done" }, wait: Infinity }
|
|
69
|
+
const current = strings[state.index] ?? []
|
|
70
|
+
|
|
71
|
+
switch (state.phase) {
|
|
72
|
+
case "typing": {
|
|
73
|
+
if (state.length >= current.length) return finish(state, count, options)
|
|
74
|
+
const length = state.length + 1
|
|
75
|
+
const next = { ...state, length }
|
|
76
|
+
if (length < current.length) return { state: next, wait: keystrokeDelay(current[length - 1]!, options.speed, random) }
|
|
77
|
+
return finish(next, count, options)
|
|
78
|
+
}
|
|
79
|
+
case "holding": {
|
|
80
|
+
const to = (state.index + 1) % count
|
|
81
|
+
return erase({ ...state, phase: "erasing", to }, strings, options)
|
|
82
|
+
}
|
|
83
|
+
case "erasing":
|
|
84
|
+
return erase(state, strings, options)
|
|
85
|
+
case "done":
|
|
86
|
+
return { state, wait: Infinity }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function finish(state: TypewriterState, count: number, options: TypewriterOptions): TypewriterStep {
|
|
91
|
+
const last = state.index === count - 1
|
|
92
|
+
if (last && !options.loop) return { state: { ...state, phase: "done" }, wait: Infinity, typed: state.index }
|
|
93
|
+
return { state: { ...state, phase: "holding" }, wait: options.hold, typed: state.index }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function erase(state: TypewriterState, strings: string[][], options: TypewriterOptions): TypewriterStep {
|
|
97
|
+
const keep = sharedPrefix(strings[state.index] ?? [], strings[state.to] ?? [])
|
|
98
|
+
if (state.length > keep) {
|
|
99
|
+
const length = state.length - 1
|
|
100
|
+
// The last backspace is followed by a beat before typing starts again.
|
|
101
|
+
return { state: { ...state, length }, wait: length > keep ? options.eraseSpeed : options.speed * 4 }
|
|
102
|
+
}
|
|
103
|
+
return { state: { index: state.to, length: state.length, phase: "typing", to: state.to }, wait: 0 }
|
|
104
|
+
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* so they can be tested without a media element.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { splitTime } from "./timeSpan"
|
|
7
|
+
|
|
6
8
|
/** The stretch of a clip that loops, and how long two passes overlap at the seam. */
|
|
7
9
|
export interface LoopPlan {
|
|
8
10
|
/** Where every pass starts, in seconds of media time. */
|
|
@@ -63,11 +65,10 @@ export function isNativeLoop(plan: LoopPlan, duration: number): boolean {
|
|
|
63
65
|
|
|
64
66
|
/** "1:05", "0:07.5": a position on a clip as a range slider labels it. */
|
|
65
67
|
export function formatClipTime(seconds: number, precise = false): string {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const tenth = Math.floor(
|
|
71
|
-
const secs = String(whole).padStart(2, "0")
|
|
68
|
+
const t = splitTime(Number.isFinite(seconds) ? seconds * 1000 : 0)
|
|
69
|
+
// Hours fold into minutes: a slider over a clip reads "75:00", not "1:15:00".
|
|
70
|
+
const minutes = (t.days * 24 + t.hours) * 60 + t.minutes
|
|
71
|
+
const secs = String(t.seconds).padStart(2, "0")
|
|
72
|
+
const tenth = Math.floor(t.ms / 100)
|
|
72
73
|
return precise && tenth ? `${minutes}:${secs}.${tenth}` : `${minutes}:${secs}`
|
|
73
74
|
}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*
|
|
28
28
|
* A plain function, not a reactive composable: it is read at the moment an
|
|
29
29
|
* animation starts, and a component that also needs to RE-RENDER when the
|
|
30
|
-
* setting changes mid-session should use
|
|
30
|
+
* setting changes mid-session should use `usePrefersReducedMotion`.
|
|
31
31
|
*/
|
|
32
32
|
export const prefersReducedMotion = (): boolean =>
|
|
33
33
|
typeof window !== "undefined" &&
|
|
@@ -98,3 +98,68 @@ export const easeTravel = (t: number): number => {
|
|
|
98
98
|
const head = (EDGE * EDGE) / (2 * EDGE) + EDGE * 0.1
|
|
99
99
|
return head + ((t - EDGE) / (1 - 2 * EDGE)) * (1 - 2 * head)
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* A cubic-bezier as a function of time, solved per call.
|
|
104
|
+
*
|
|
105
|
+
* The curves above are hand-shaped stand-ins for tokens a component reaches for
|
|
106
|
+
* by feel. A tween that has to match a CSS transition running beside it (a
|
|
107
|
+
* slice's arc easing while its colour and lift move on `--ease-emphasized`)
|
|
108
|
+
* needs the token's actual curve, and a theme may retune it — so this reads the
|
|
109
|
+
* four numbers and solves for them rather than approximating one fixed shape.
|
|
110
|
+
*
|
|
111
|
+
* Newton's method on x(s) = t, falling back to bisection where the slope is
|
|
112
|
+
* too flat to step on; a handful of iterations is far below a pixel.
|
|
113
|
+
*/
|
|
114
|
+
export const cubicBezier = (x1: number, y1: number, x2: number, y2: number) => {
|
|
115
|
+
const cx = 3 * x1
|
|
116
|
+
const bx = 3 * (x2 - x1) - cx
|
|
117
|
+
const ax = 1 - cx - bx
|
|
118
|
+
const cy = 3 * y1
|
|
119
|
+
const by = 3 * (y2 - y1) - cy
|
|
120
|
+
const ay = 1 - cy - by
|
|
121
|
+
const x = (s: number) => ((ax * s + bx) * s + cx) * s
|
|
122
|
+
const y = (s: number) => ((ay * s + by) * s + cy) * s
|
|
123
|
+
const dx = (s: number) => (3 * ax * s + 2 * bx) * s + cx
|
|
124
|
+
|
|
125
|
+
return (t: number): number => {
|
|
126
|
+
if (t <= 0) return 0
|
|
127
|
+
if (t >= 1) return 1
|
|
128
|
+
let s = t
|
|
129
|
+
for (let i = 0; i < 6; i++) {
|
|
130
|
+
const err = x(s) - t
|
|
131
|
+
if (Math.abs(err) < 1e-5) return y(s)
|
|
132
|
+
const slope = dx(s)
|
|
133
|
+
if (Math.abs(slope) < 1e-6) break
|
|
134
|
+
s -= err / slope
|
|
135
|
+
}
|
|
136
|
+
let lo = 0
|
|
137
|
+
let hi = 1
|
|
138
|
+
s = t
|
|
139
|
+
for (let i = 0; i < 20; i++) {
|
|
140
|
+
const v = x(s)
|
|
141
|
+
if (Math.abs(v - t) < 1e-5) break
|
|
142
|
+
if (v < t) lo = s
|
|
143
|
+
else hi = s
|
|
144
|
+
s = (lo + hi) / 2
|
|
145
|
+
}
|
|
146
|
+
return y(s)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* An easing token as a function, for a value tweened in script.
|
|
152
|
+
*
|
|
153
|
+
* Anything the token does not spell as a `cubic-bezier()` (a keyword, a
|
|
154
|
+
* `linear()` list) falls back to `easeOut`, which has the same "arrives and
|
|
155
|
+
* settles" intent as every curve the kit defines.
|
|
156
|
+
*/
|
|
157
|
+
export const tokenEase = (
|
|
158
|
+
name: "ease-emphasized" | "ease-travel" | "ease-settle",
|
|
159
|
+
): ((t: number) => number) => {
|
|
160
|
+
const match = /cubic-bezier\(([^)]+)\)/.exec(easingToken(name))
|
|
161
|
+
const n = match?.[1].split(",").map((p) => Number.parseFloat(p))
|
|
162
|
+
return n && n.length === 4 && n.every(Number.isFinite)
|
|
163
|
+
? cubicBezier(n[0], n[1], n[2], n[3])
|
|
164
|
+
: easeOut
|
|
165
|
+
}
|