@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
|
@@ -17,11 +17,12 @@ export default { inheritAttrs: false }
|
|
|
17
17
|
* value either way).
|
|
18
18
|
*/
|
|
19
19
|
import { ProgressIndicator, ProgressRoot } from "reka-ui"
|
|
20
|
-
import { computed
|
|
21
|
-
import { easeOut, prefersReducedMotion } from "../composables/easing"
|
|
20
|
+
import { computed } from "vue"
|
|
22
21
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
23
|
-
import {
|
|
22
|
+
import { useRevealedValue } from "../composables/useRevealedValue"
|
|
24
23
|
import LpNumberFlow from "./LpNumberFlow.vue"
|
|
24
|
+
import { capacityThresholds, METER_HEADER, METER_TRACK, meterFraction, toneAt } from "./meter"
|
|
25
|
+
import { toneLook, type ToneName } from "./tone"
|
|
25
26
|
|
|
26
27
|
const props = withDefaults(
|
|
27
28
|
defineProps<{
|
|
@@ -35,8 +36,11 @@ const props = withDefaults(
|
|
|
35
36
|
* different at 92% than at 12%, and consumers were each re-deriving that
|
|
36
37
|
* with their own palette — which also put raw colours back into components
|
|
37
38
|
* the theme can no longer reach.
|
|
39
|
+
*
|
|
40
|
+
* Any of the kit's tone names works (tone.ts): `success`/`warning` read the
|
|
41
|
+
* same as `action`/`accent`.
|
|
38
42
|
*/
|
|
39
|
-
tone?:
|
|
43
|
+
tone?: ToneName | "auto"
|
|
40
44
|
/** `auto` tone: the fill fraction, 0–1, where the bar turns accent. */
|
|
41
45
|
warnAt?: number
|
|
42
46
|
/** `auto` tone: the fill fraction, 0–1, where it turns danger. */
|
|
@@ -105,44 +109,9 @@ const props = withDefaults(
|
|
|
105
109
|
* gap to the other end. Interpolating here and handing both the same number
|
|
106
110
|
* keeps them exactly level, because there is only one number.
|
|
107
111
|
*/
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
*
|
|
112
|
-
* The earlier shape kept an eased 0..1 and derived the position from it and a
|
|
113
|
-
* `from` captured in the watcher. That could not work: `from` was read off a
|
|
114
|
-
* computed over `props.value`, so by the time the watcher ran it already
|
|
115
|
-
* reported the DESTINATION, and every sweep after the first interpolated from
|
|
116
|
-
* the answer to the answer. The entrance still animated, which is what made
|
|
117
|
-
* the bug look like a decision — the bar filled beautifully once and jumped
|
|
118
|
-
* forever after.
|
|
119
|
-
*
|
|
120
|
-
* Animating the position directly removes the question. Wherever the bar is is
|
|
121
|
-
* where the next sweep starts, including one that interrupts another midway.
|
|
122
|
-
*/
|
|
123
|
-
const shown = ref(props.animateOnMount ? 0 : props.value)
|
|
124
|
-
let raf = 0
|
|
125
|
-
|
|
126
|
-
const runSweep = (to: number, ms: number) => {
|
|
127
|
-
if (raf) cancelAnimationFrame(raf)
|
|
128
|
-
const from = shown.value
|
|
129
|
-
if (prefersReducedMotion() || ms <= 0 || from === to) {
|
|
130
|
-
shown.value = to
|
|
131
|
-
return
|
|
132
|
-
}
|
|
133
|
-
const t0 = performance.now()
|
|
134
|
-
const step = (now: number) => {
|
|
135
|
-
const t = Math.min(1, (now - t0) / ms)
|
|
136
|
-
// The kit's shared curve — the same function LpNumberFlow eases on, so the
|
|
137
|
-
// arc and the counter agree by construction rather than by comment.
|
|
138
|
-
shown.value = from + (to - from) * easeOut(t)
|
|
139
|
-
raf = t < 1 ? requestAnimationFrame(step) : 0
|
|
140
|
-
}
|
|
141
|
-
raf = requestAnimationFrame(step)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
onBeforeUnmount(() => {
|
|
145
|
-
if (raf) cancelAnimationFrame(raf)
|
|
112
|
+
const { el: revealAnchor, shown } = useRevealedValue(() => props.value, {
|
|
113
|
+
animateOnMount: () => props.animateOnMount,
|
|
114
|
+
duration: () => props.duration,
|
|
146
115
|
})
|
|
147
116
|
|
|
148
117
|
/*
|
|
@@ -154,26 +123,10 @@ onBeforeUnmount(() => {
|
|
|
154
123
|
* reka's ProgressRoot is a component, so the ref holds an instance; take its
|
|
155
124
|
* host element to observe.
|
|
156
125
|
*/
|
|
157
|
-
const { el: revealAnchor, revealed } = useReveal({ immediate: () => !props.animateOnMount })
|
|
158
|
-
|
|
159
126
|
const bindReveal = (instance: unknown) => {
|
|
160
127
|
revealAnchor.value = (instance as { $el?: Element } | null)?.$el ?? null
|
|
161
128
|
}
|
|
162
129
|
|
|
163
|
-
watch(
|
|
164
|
-
revealed,
|
|
165
|
-
(visible) => {
|
|
166
|
-
if (visible && props.animateOnMount) runSweep(props.value, props.duration)
|
|
167
|
-
},
|
|
168
|
-
{ immediate: true },
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
/* A later change eases to the new value from wherever the bar stands. */
|
|
172
|
-
watch(
|
|
173
|
-
() => [props.value, props.max],
|
|
174
|
-
() => runSweep(props.value, props.duration),
|
|
175
|
-
)
|
|
176
|
-
|
|
177
130
|
// A zero or missing max (no quota known yet) reads as empty, not as NaN%.
|
|
178
131
|
const pct = computed(() => {
|
|
179
132
|
const ratio = props.max > 0 ? (shown.value / props.max) * 100 : 0
|
|
@@ -181,19 +134,18 @@ const pct = computed(() => {
|
|
|
181
134
|
})
|
|
182
135
|
|
|
183
136
|
/*
|
|
184
|
-
* The tone actually painted. `auto` reads the fill
|
|
185
|
-
*
|
|
137
|
+
* The tone actually painted. `auto` reads the fill against the capacity scale
|
|
138
|
+
* LpGauge and LpQuota use (meter.ts), so a bar and a gauge showing one disk
|
|
139
|
+
* turn amber at the same point; every other value passes straight through.
|
|
186
140
|
*
|
|
187
141
|
* Thresholds are compared against the TARGET, not the animated position, so a
|
|
188
142
|
* bar does not change colour partway through its own sweep — it fills in the
|
|
189
143
|
* colour its reading warrants.
|
|
190
144
|
*/
|
|
191
145
|
const paintedTone = computed(() => {
|
|
192
|
-
if (props.tone !== "auto") return props.tone
|
|
193
|
-
const fraction = props.
|
|
194
|
-
|
|
195
|
-
if (fraction >= props.warnAt) return "accent"
|
|
196
|
-
return "action"
|
|
146
|
+
if (props.tone !== "auto") return toneLook(props.tone)
|
|
147
|
+
const fraction = meterFraction(props.value, 0, props.max)
|
|
148
|
+
return toneLook(toneAt(fraction, capacityThresholds(0, 1, props.warnAt, props.dangerAt)))
|
|
197
149
|
})
|
|
198
150
|
|
|
199
151
|
const captionText = computed(() => {
|
|
@@ -201,20 +153,6 @@ const captionText = computed(() => {
|
|
|
201
153
|
return typeof props.caption === "string" ? props.caption : `${Math.round(pct.value)}%`
|
|
202
154
|
})
|
|
203
155
|
|
|
204
|
-
const bar: Record<string, string> = {
|
|
205
|
-
brand: "bg-brand",
|
|
206
|
-
action: "bg-action",
|
|
207
|
-
danger: "bg-danger",
|
|
208
|
-
accent: "bg-accent",
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const stroke: Record<string, string> = {
|
|
212
|
-
brand: "var(--color-brand)",
|
|
213
|
-
action: "var(--color-action)",
|
|
214
|
-
danger: "var(--color-danger)",
|
|
215
|
-
accent: "var(--color-accent)",
|
|
216
|
-
}
|
|
217
|
-
|
|
218
156
|
/*
|
|
219
157
|
* The arc is drawn by dashing the circumference: one dash as long as the filled
|
|
220
158
|
* fraction, one gap covering the rest. Rotated -90° so it starts at twelve
|
|
@@ -235,7 +173,7 @@ const dash = computed(() => {
|
|
|
235
173
|
return Math.min(raw, full - props.thickness * 1.5)
|
|
236
174
|
})
|
|
237
175
|
|
|
238
|
-
const BAR_TRACK =
|
|
176
|
+
const BAR_TRACK = `${METER_TRACK} overflow-hidden`
|
|
239
177
|
|
|
240
178
|
/*
|
|
241
179
|
* The consumer's class lands on whichever element is the outside of the
|
|
@@ -280,7 +218,7 @@ const staticCaption = computed(() =>
|
|
|
280
218
|
:cy="size / 2"
|
|
281
219
|
:r="radius"
|
|
282
220
|
fill="none"
|
|
283
|
-
:stroke="
|
|
221
|
+
:stroke="paintedTone.color"
|
|
284
222
|
:stroke-width="thickness"
|
|
285
223
|
stroke-linecap="round"
|
|
286
224
|
:stroke-dasharray="`${dash} ${circumference - dash}`"
|
|
@@ -307,7 +245,7 @@ const staticCaption = computed(() =>
|
|
|
307
245
|
<!-- Wrapped only when there is a header to carry. A bare bar stays the single
|
|
308
246
|
element it has always been, so an existing caller's layout is untouched. -->
|
|
309
247
|
<div v-else-if="title || captionText" :class="rootClass" v-bind="rest">
|
|
310
|
-
<div class="
|
|
248
|
+
<div :class="METER_HEADER" class="gap-2 text-xs">
|
|
311
249
|
<span class="text-muted">{{ title }}</span>
|
|
312
250
|
<!-- Tabular, so a figure ticking upward doesn't shuffle its own width. -->
|
|
313
251
|
<span class="font-mono tabular-nums text-muted-strong">{{ captionText }}</span>
|
|
@@ -320,7 +258,7 @@ const staticCaption = computed(() =>
|
|
|
320
258
|
>
|
|
321
259
|
<ProgressIndicator
|
|
322
260
|
class="h-full rounded-pill transition-colors duration-medium"
|
|
323
|
-
:class="
|
|
261
|
+
:class="paintedTone.bg"
|
|
324
262
|
:style="{ width: `${pct}%` }"
|
|
325
263
|
/>
|
|
326
264
|
</ProgressRoot>
|
|
@@ -336,7 +274,7 @@ const staticCaption = computed(() =>
|
|
|
336
274
|
>
|
|
337
275
|
<ProgressIndicator
|
|
338
276
|
class="h-full rounded-pill transition-colors duration-medium"
|
|
339
|
-
:class="
|
|
277
|
+
:class="paintedTone.bg"
|
|
340
278
|
:style="{ width: `${pct}%` }"
|
|
341
279
|
/>
|
|
342
280
|
</ProgressRoot>
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// The consumer's `class` is merged into the root's own rather than appended to
|
|
3
|
+
// it, so opt out of the automatic pass-through that would add it unmerged.
|
|
4
|
+
export default { inheritAttrs: false }
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
/*
|
|
9
|
+
* "Used of limit": storage, seats, API calls. A header with the reading, a bar,
|
|
10
|
+
* and a line saying what is left, with the tone rising as the limit nears.
|
|
11
|
+
*
|
|
12
|
+
* The bar is drawn here rather than borrowed from LpProgress. LpProgress shows
|
|
13
|
+
* one fraction clamped to full, which is right for progress and wrong for a
|
|
14
|
+
* quota on two counts: a quota is often made of parts (a stacked bar), and an
|
|
15
|
+
* exceeded quota has to SHOW the excess. Here the bar stretches to hold what is
|
|
16
|
+
* used and the limit becomes a mark along it, so 12 of 10 GB reads as a bar
|
|
17
|
+
* running past a line instead of as a full bar indistinguishable from 10 of 10.
|
|
18
|
+
* The tone scale, the track and the header row are LpProgress's (meter.ts), so
|
|
19
|
+
* the two agree on one disk and look like one family.
|
|
20
|
+
*/
|
|
21
|
+
import { computed } from "vue"
|
|
22
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
23
|
+
import { useLocale } from "../composables/useLocale"
|
|
24
|
+
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
25
|
+
import { useRevealedValue } from "../composables/useRevealedValue"
|
|
26
|
+
import LpIcon from "./LpIcon.vue"
|
|
27
|
+
import LpNumberFlow from "./LpNumberFlow.vue"
|
|
28
|
+
import LpRollingText from "./LpRollingText.vue"
|
|
29
|
+
import { METER_HEADER, METER_TRACK } from "./meter"
|
|
30
|
+
import { formatAmount, quotaSegments, quotaState, type QuotaPart, type QuotaUnit } from "./quota"
|
|
31
|
+
import { toneLook } from "./tone"
|
|
32
|
+
|
|
33
|
+
export type QuotaLabels = {
|
|
34
|
+
/** `{amount}` is what remains. */
|
|
35
|
+
left: string
|
|
36
|
+
/** `{amount}` is the excess. */
|
|
37
|
+
over: string
|
|
38
|
+
/** Shown instead of what remains when there is no limit. */
|
|
39
|
+
unlimited: string
|
|
40
|
+
/** The spoken reading, followed by what is left or over. `{used}`, `{limit}`. */
|
|
41
|
+
reading: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const props = withDefaults(
|
|
45
|
+
defineProps<{
|
|
46
|
+
used: number
|
|
47
|
+
/** Unset, zero or Infinity means unlimited: the reading stays, the bar and "left" go. */
|
|
48
|
+
limit?: number | null
|
|
49
|
+
/** What is counted ("Storage", "Seats"). Also the accessible name. */
|
|
50
|
+
label?: string
|
|
51
|
+
/** `"bytes"`, a plain suffix ("seats"), or a formatter. See `formatAmount`. */
|
|
52
|
+
unit?: QuotaUnit
|
|
53
|
+
/**
|
|
54
|
+
* What the used amount is made of. The bar stacks them and a legend names
|
|
55
|
+
* them; their sum need not equal `used` (a part can be left unlisted), but
|
|
56
|
+
* the bar shows the parts, so list them all if the bar should add up.
|
|
57
|
+
*/
|
|
58
|
+
parts?: QuotaPart[]
|
|
59
|
+
/** Share of the limit (0–1) where the quota turns warning. */
|
|
60
|
+
warnAt?: number
|
|
61
|
+
/** Share of the limit where it turns danger. Over the limit is always danger. */
|
|
62
|
+
dangerAt?: number
|
|
63
|
+
/** Hide the legend under a stacked bar, for a compact row where the tooltip of the page says enough. */
|
|
64
|
+
hideLegend?: boolean
|
|
65
|
+
/** Fill from empty when first seen. */
|
|
66
|
+
animateOnMount?: boolean
|
|
67
|
+
locale?: string
|
|
68
|
+
labels?: Partial<QuotaLabels>
|
|
69
|
+
}>(),
|
|
70
|
+
{ limit: null, warnAt: 0.8, dangerAt: 0.9, hideLegend: false, animateOnMount: true },
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const text = useLabels<QuotaLabels>(
|
|
74
|
+
"quota",
|
|
75
|
+
{
|
|
76
|
+
left: "{amount} left",
|
|
77
|
+
over: "{amount} over the limit",
|
|
78
|
+
unlimited: "No limit",
|
|
79
|
+
reading: "{used} of {limit} used",
|
|
80
|
+
},
|
|
81
|
+
() => props.labels,
|
|
82
|
+
)
|
|
83
|
+
const locale = useLocale(() => props.locale)
|
|
84
|
+
|
|
85
|
+
const state = computed(() => quotaState(props.used, props.limit, props.warnAt, props.dangerAt))
|
|
86
|
+
const look = computed(() => toneLook(state.value.tone))
|
|
87
|
+
const fmt = (v: number) => formatAmount(v, props.unit, locale.value)
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* Without parts the used amount is one part in the tone it earns, so a single
|
|
91
|
+
* bar and a stacked one share the layout, the overflow mark and the motion.
|
|
92
|
+
*/
|
|
93
|
+
const layout = computed(() =>
|
|
94
|
+
quotaSegments(
|
|
95
|
+
props.parts?.length ? props.parts : [{ label: props.label ?? "", value: props.used, color: look.value.color }],
|
|
96
|
+
props.limit,
|
|
97
|
+
),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
// The bar's segments ease in CSS and the figure rolls in LpNumberFlow, so only the held value is needed here.
|
|
101
|
+
const { el: revealAnchor, revealed, target: shownUsed } = useRevealedValue(() => props.used, {
|
|
102
|
+
animateOnMount: () => props.animateOnMount,
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const footer = computed(() => {
|
|
106
|
+
const s = state.value
|
|
107
|
+
if (!s.limited) return text.value.unlimited
|
|
108
|
+
if (s.over > 0) return text.value.over.replace("{amount}", fmt(s.over))
|
|
109
|
+
return text.value.left.replace("{amount}", fmt(s.remaining))
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
/* Spoken: the reading, then the same verdict the footer shows. */
|
|
113
|
+
const valueText = computed(() =>
|
|
114
|
+
state.value.limited
|
|
115
|
+
? `${text.value.reading.replace("{used}", fmt(props.used)).replace("{limit}", fmt(props.limit as number))}, ${footer.value}`
|
|
116
|
+
: `${fmt(props.used)}, ${text.value.unlimited}`,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
// Ink while the reading is fine: a calm quota should not be coloured at all.
|
|
120
|
+
const readingTone = computed(() => (state.value.tone === "ok" ? "text-ink" : look.value.text))
|
|
121
|
+
|
|
122
|
+
const { class: rootClass, attrs: rest } = useMergedAttrs("flex w-full flex-col gap-1.5")
|
|
123
|
+
</script>
|
|
124
|
+
|
|
125
|
+
<template>
|
|
126
|
+
<div
|
|
127
|
+
:ref="(el) => (revealAnchor = el as Element | null)"
|
|
128
|
+
:role="state.limited ? 'meter' : 'group'"
|
|
129
|
+
:aria-valuenow="state.limited ? used : undefined"
|
|
130
|
+
:aria-valuemin="state.limited ? 0 : undefined"
|
|
131
|
+
:aria-valuemax="state.limited ? (limit as number) : undefined"
|
|
132
|
+
:aria-valuetext="state.limited ? valueText : undefined"
|
|
133
|
+
:aria-label="label"
|
|
134
|
+
:class="rootClass"
|
|
135
|
+
v-bind="rest"
|
|
136
|
+
>
|
|
137
|
+
<div :class="METER_HEADER" class="gap-3 text-sm">
|
|
138
|
+
<span v-if="label" class="truncate text-ink">{{ label }}</span>
|
|
139
|
+
<span class="ml-auto shrink-0 font-mono text-xs tabular-nums text-muted">
|
|
140
|
+
<LpNumberFlow
|
|
141
|
+
:value="shownUsed"
|
|
142
|
+
:format="fmt"
|
|
143
|
+
:animate-on-mount="false"
|
|
144
|
+
class="transition-colors duration-(--duration-medium)"
|
|
145
|
+
:class="readingTone"
|
|
146
|
+
/>
|
|
147
|
+
<template v-if="state.limited">
|
|
148
|
+
<span class="px-1">/</span>
|
|
149
|
+
<LpNumberFlow :value="limit as number" :format="fmt" :animate-on-mount="false" />
|
|
150
|
+
</template>
|
|
151
|
+
</span>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<div
|
|
155
|
+
v-if="state.limited || parts?.length"
|
|
156
|
+
class="relative"
|
|
157
|
+
:class="METER_TRACK"
|
|
158
|
+
aria-hidden="true"
|
|
159
|
+
>
|
|
160
|
+
<div class="flex h-full w-full gap-px overflow-hidden rounded-pill">
|
|
161
|
+
<span
|
|
162
|
+
v-for="seg in layout.segments"
|
|
163
|
+
:key="seg.label"
|
|
164
|
+
class="h-full shrink-0 transition-[width,background-color] duration-700 ease-(--ease-emphasized) motion-reduce:transition-none"
|
|
165
|
+
:style="{ width: revealed ? `${seg.share * 100}%` : '0%', backgroundColor: seg.color }"
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
<!-- The limit, once the bar has run past it. Taller than the track so it
|
|
169
|
+
reads as a line across the bar, not as one more part. -->
|
|
170
|
+
<span
|
|
171
|
+
class="absolute -inset-y-1 w-0.5 -translate-x-1/2 rounded-pill bg-ink transition-[left,opacity] duration-700 ease-(--ease-emphasized) motion-reduce:transition-none"
|
|
172
|
+
:style="{ left: `${(layout.limitAt ?? 1) * 100}%`, opacity: layout.limitAt != null && revealed ? 1 : 0 }"
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div
|
|
177
|
+
class="flex items-center gap-1 text-xs transition-colors duration-(--duration-medium)"
|
|
178
|
+
:class="state.over > 0 ? look.text : 'text-muted'"
|
|
179
|
+
>
|
|
180
|
+
<LpIcon
|
|
181
|
+
name="lucide:triangle-alert"
|
|
182
|
+
:size="12"
|
|
183
|
+
class="shrink-0 transition-[opacity,width] duration-(--duration-medium)"
|
|
184
|
+
:class="state.over > 0 ? 'w-3 opacity-100' : 'w-0 opacity-0'"
|
|
185
|
+
/>
|
|
186
|
+
<LpRollingText :value="footer" align="start" />
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<ul v-if="parts?.length && !hideLegend" class="mt-1 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted">
|
|
190
|
+
<li v-for="seg in layout.segments" :key="seg.label" class="flex items-center gap-1.5">
|
|
191
|
+
<span class="size-2 shrink-0 rounded-full" :style="{ backgroundColor: seg.color }" />
|
|
192
|
+
<span>{{ seg.label }}</span>
|
|
193
|
+
<span class="font-mono tabular-nums text-muted-strong">{{ fmt(seg.value) }}</span>
|
|
194
|
+
</li>
|
|
195
|
+
</ul>
|
|
196
|
+
</div>
|
|
197
|
+
</template>
|
|
@@ -16,6 +16,7 @@ export default { inheritAttrs: false }
|
|
|
16
16
|
import { SliderRange, SliderRoot, SliderThumb, SliderTrack } from "reka-ui"
|
|
17
17
|
import { useFieldControl } from "../composables/useField"
|
|
18
18
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
19
|
+
import { SLIDER_GLIDE, useSliderDrag } from "./slider"
|
|
19
20
|
|
|
20
21
|
const props = withDefaults(
|
|
21
22
|
defineProps<{
|
|
@@ -56,8 +57,9 @@ function onCommit(v: number[] | undefined) {
|
|
|
56
57
|
const field = useFieldControl(() => ({ wrapped: false, disabled: props.disabled }))
|
|
57
58
|
|
|
58
59
|
const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
59
|
-
|
|
60
|
+
`relative flex h-5 w-full touch-none select-none items-center data-[disabled]:opacity-55 ${SLIDER_GLIDE}`,
|
|
60
61
|
)
|
|
62
|
+
const { dragging, onPointerDown } = useSliderDrag()
|
|
61
63
|
|
|
62
64
|
const thumbClass =
|
|
63
65
|
"group/thumb relative block size-4 rounded-pill border-2 border-brand bg-surface shadow outline-none transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] hover:scale-110 active:scale-95 focus-visible:ring-2 focus-visible:ring-ring motion-reduce:hover:scale-100 motion-reduce:active:scale-100"
|
|
@@ -75,6 +77,8 @@ const labelClass =
|
|
|
75
77
|
:disabled="field.disabled"
|
|
76
78
|
:class="rootClass"
|
|
77
79
|
v-bind="rest"
|
|
80
|
+
:data-dragging="dragging || undefined"
|
|
81
|
+
@pointerdown="onPointerDown"
|
|
78
82
|
@update:model-value="onUpdate"
|
|
79
83
|
@value-commit="onCommit"
|
|
80
84
|
>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// The consumer's `class` is merged into the root's own rather than appended to
|
|
3
|
+
// it, so opt out of the automatic pass-through that would add it unmerged.
|
|
4
|
+
export default { inheritAttrs: false }
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
/*
|
|
9
|
+
* A date said the way people say it ("3 min ago", "in 2 h"), kept true as time
|
|
10
|
+
* passes, with the exact date one hover away.
|
|
11
|
+
*
|
|
12
|
+
* Each instance sleeps until its own text would change (useNow with the
|
|
13
|
+
* formatter's `refreshIn`): a "just now" wakes in seconds, a "last month" once
|
|
14
|
+
* an hour, so a table of old rows costs almost nothing between changes. When
|
|
15
|
+
* the text does change it rolls to the new one, so "4 min ago" becoming
|
|
16
|
+
* "5 min ago" is seen as the same clock moving.
|
|
17
|
+
*/
|
|
18
|
+
import { computed } from "vue"
|
|
19
|
+
import { useLocale } from "../composables/useLocale"
|
|
20
|
+
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
21
|
+
import { useNow } from "../composables/useNow"
|
|
22
|
+
import LpRollingText from "./LpRollingText.vue"
|
|
23
|
+
import LpTooltip from "./LpTooltip.vue"
|
|
24
|
+
import { formatRelativeTime } from "./relativeTime"
|
|
25
|
+
import { toEpoch } from "./timeSpan"
|
|
26
|
+
|
|
27
|
+
const props = withDefaults(
|
|
28
|
+
defineProps<{
|
|
29
|
+
/** A Date, an ISO string or epoch milliseconds. */
|
|
30
|
+
date: Date | string | number
|
|
31
|
+
/** Intl's width: `long` "3 minutes ago", `short` "3 min. ago", `narrow` "3m ago". */
|
|
32
|
+
format?: Intl.RelativeTimeFormatStyle
|
|
33
|
+
/** Within this many ms of now it reads "now" rather than counting seconds. */
|
|
34
|
+
nowWithin?: number
|
|
35
|
+
/**
|
|
36
|
+
* Show the exact date on hover and focus. Off, it moves to the native
|
|
37
|
+
* `title` instead, for a dense log where tooltips would get in the way of
|
|
38
|
+
* reading; it is never shown both ways at once, which would stack two
|
|
39
|
+
* hints on one hover.
|
|
40
|
+
*/
|
|
41
|
+
tooltip?: boolean
|
|
42
|
+
/** How the exact date is written. Defaults to the full date with the time. */
|
|
43
|
+
absolute?: Intl.DateTimeFormatOptions
|
|
44
|
+
locale?: string
|
|
45
|
+
}>(),
|
|
46
|
+
{ format: "long", tooltip: true, nowWithin: 45_000 },
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const locale = useLocale(() => props.locale)
|
|
50
|
+
const at = computed(() => toEpoch(props.date))
|
|
51
|
+
const options = computed(() => ({ locale: locale.value, style: props.format, nowWithin: props.nowWithin }))
|
|
52
|
+
|
|
53
|
+
/*
|
|
54
|
+
* The clock wakes only when this text would change (`refreshIn`): a "just
|
|
55
|
+
* now" in seconds, a "last month" once an hour, nothing while the tab is
|
|
56
|
+
* hidden. A new date or wording reschedules it, since the wait planned for the
|
|
57
|
+
* old one may be far too long for the new.
|
|
58
|
+
*/
|
|
59
|
+
const now = useNow(true, {
|
|
60
|
+
nextIn: (t) => formatRelativeTime(at.value, t, options.value).refreshIn,
|
|
61
|
+
restartOn: () => [at.value, locale.value, props.format, props.nowWithin],
|
|
62
|
+
})
|
|
63
|
+
const reading = computed(() => formatRelativeTime(at.value, now.value, options.value))
|
|
64
|
+
|
|
65
|
+
const exact = computed(() => {
|
|
66
|
+
if (!Number.isFinite(at.value)) return ""
|
|
67
|
+
return new Intl.DateTimeFormat(locale.value, props.absolute ?? { dateStyle: "full", timeStyle: "short" }).format(at.value)
|
|
68
|
+
})
|
|
69
|
+
const iso = computed(() => (Number.isFinite(at.value) ? new Date(at.value).toISOString() : undefined))
|
|
70
|
+
|
|
71
|
+
const { class: rootClass, attrs: rest } = useMergedAttrs("inline-flex whitespace-nowrap tabular-nums")
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<template>
|
|
75
|
+
<LpTooltip v-if="tooltip && exact" :content="exact">
|
|
76
|
+
<!-- Focusable, so the exact date is reachable from the keyboard too; the
|
|
77
|
+
tooltip is a description, the relative text stays the name. -->
|
|
78
|
+
<time
|
|
79
|
+
:datetime="iso"
|
|
80
|
+
tabindex="0"
|
|
81
|
+
:class="[rootClass, 'rounded-[3px] outline-none focus-visible:ring-2 focus-visible:ring-ring']"
|
|
82
|
+
v-bind="rest"
|
|
83
|
+
>
|
|
84
|
+
<LpRollingText :value="reading.text" align="start" />
|
|
85
|
+
</time>
|
|
86
|
+
</LpTooltip>
|
|
87
|
+
<time v-else :datetime="iso" :title="exact || undefined" :class="rootClass" v-bind="rest">
|
|
88
|
+
<LpRollingText :value="reading.text" align="start" />
|
|
89
|
+
</time>
|
|
90
|
+
</template>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script setup lang="ts" generic="T extends object">
|
|
2
|
+
import { nextTick, ref } from "vue"
|
|
3
|
+
import { useFieldControl } from "../composables/useField"
|
|
4
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
5
|
+
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
6
|
+
import { useRepeater, type RepeaterItemKey } from "../composables/useRepeater"
|
|
7
|
+
import LpButton from "./LpButton.vue"
|
|
8
|
+
import LpIcon from "./LpIcon.vue"
|
|
9
|
+
|
|
10
|
+
defineOptions({ inheritAttrs: false })
|
|
11
|
+
const props = defineProps<{
|
|
12
|
+
/** Return a fresh item when Add is pressed. Data and validation remain the caller's. */
|
|
13
|
+
createItem: () => T
|
|
14
|
+
/** Stable business key for immutable reloads; omitted, object identity keeps unsaved rows stable. */
|
|
15
|
+
itemKey?: RepeaterItemKey<T>
|
|
16
|
+
label?: string
|
|
17
|
+
disabled?: boolean
|
|
18
|
+
labels?: Partial<{ add: string; remove: string; empty: string }>
|
|
19
|
+
}>()
|
|
20
|
+
const model = defineModel<T[]>({ default: () => [] })
|
|
21
|
+
defineSlots<{
|
|
22
|
+
default?(props: { item: T; index: number; disabled: boolean; update: (item: T) => void; remove: () => void }): unknown
|
|
23
|
+
empty?(): unknown
|
|
24
|
+
}>()
|
|
25
|
+
|
|
26
|
+
const field = useFieldControl(() => ({ wrapped: false, disabled: props.disabled }))
|
|
27
|
+
const text = useLabels("repeater", { add: "Add item", remove: "Remove item {index}", empty: "No items" }, () => props.labels)
|
|
28
|
+
const list = useRepeater({
|
|
29
|
+
value: () => model.value,
|
|
30
|
+
set: (items) => { model.value = items },
|
|
31
|
+
createItem: () => props.createItem(),
|
|
32
|
+
itemKey: () => props.itemKey,
|
|
33
|
+
disabled: () => field.value.disabled,
|
|
34
|
+
})
|
|
35
|
+
const { rows } = list
|
|
36
|
+
const root = ref<HTMLElement | null>(null)
|
|
37
|
+
const addButton = ref<InstanceType<typeof LpButton> | null>(null)
|
|
38
|
+
const focusable = 'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [tabindex="0"], [contenteditable="true"]'
|
|
39
|
+
function focusRow(index: number) {
|
|
40
|
+
root.value?.querySelectorAll<HTMLElement>("[data-lp-repeater-row]")[index]?.querySelector<HTMLElement>(focusable)?.focus()
|
|
41
|
+
}
|
|
42
|
+
async function add() {
|
|
43
|
+
if (field.value.disabled) return
|
|
44
|
+
list.add()
|
|
45
|
+
await nextTick()
|
|
46
|
+
focusRow(model.value.length - 1)
|
|
47
|
+
}
|
|
48
|
+
async function remove(index: number) {
|
|
49
|
+
if (field.value.disabled) return
|
|
50
|
+
const row = root.value?.querySelectorAll<HTMLElement>("[data-lp-repeater-row]")[index]
|
|
51
|
+
const restore = row?.contains(row.ownerDocument.activeElement)
|
|
52
|
+
list.remove(index)
|
|
53
|
+
if (!restore) return
|
|
54
|
+
await nextTick()
|
|
55
|
+
if (model.value.length) focusRow(Math.min(index, model.value.length - 1))
|
|
56
|
+
else (addButton.value?.$el as HTMLElement | undefined)?.focus()
|
|
57
|
+
}
|
|
58
|
+
defineExpose({ add, remove, update: list.update })
|
|
59
|
+
const { class: rootClass, attrs: rest } = useMergedAttrs("flex min-w-0 flex-col gap-2")
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<template>
|
|
63
|
+
<div ref="root" v-bind="rest" :class="rootClass">
|
|
64
|
+
<div class="flex items-center justify-between gap-2">
|
|
65
|
+
<span v-if="label" class="text-sm font-medium text-ink">{{ label }}</span>
|
|
66
|
+
<LpButton ref="addButton" size="xs" variant="ghost" :disabled="field.disabled" @click="add">
|
|
67
|
+
<LpIcon name="lucide:plus" :size="14" />{{ text.add }}
|
|
68
|
+
</LpButton>
|
|
69
|
+
</div>
|
|
70
|
+
<div v-for="({ item, key }, index) in rows" :key="key" data-lp-repeater-row class="flex min-w-0 items-start gap-2">
|
|
71
|
+
<div class="min-w-0 flex-1">
|
|
72
|
+
<slot :item="item" :index="index" :disabled="field.disabled" :update="(next: T) => list.update(index, next)" :remove="() => remove(index)" />
|
|
73
|
+
</div>
|
|
74
|
+
<LpButton variant="ghost" size="sm" square :disabled="field.disabled" :aria-label="text.remove.replace('{index}', String(index + 1))" @click="remove(index)">
|
|
75
|
+
<LpIcon name="lucide:x" :size="14" />
|
|
76
|
+
</LpButton>
|
|
77
|
+
</div>
|
|
78
|
+
<div v-if="!rows.length" class="text-xs text-muted"><slot name="empty">{{ text.empty }}</slot></div>
|
|
79
|
+
</div>
|
|
80
|
+
</template>
|