@leavepulse/ui 0.69.0 → 0.70.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -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/LpRepeater-C1Cw27hj.css +21 -0
- package/dist/assets/{LpRollingText-BGnE6q9j.css → LpRollingText-dyOXedGO.css} +7 -0
- 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-6kxEivBI.js} +11 -5
- 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-Chv_Xf8q.js} +7 -7
- package/dist/chunks/LpAlert-C5igj802.js +119 -0
- package/dist/chunks/{LpAppShell-DPh1756q.js → LpAppShell-BiWByLmt.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-BBq1Er9T.js +126 -0
- package/dist/chunks/LpBreadcrumbs-JKEi_yFF.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-CbJMs-IK.js +189 -0
- package/dist/chunks/LpCollapse-CWS9M_lm.js +115 -0
- package/dist/chunks/{LpColorPicker-DtEfBAcK.js → LpColorPicker-B7HSIFmn.js} +11 -6
- 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-Cj96ENDC.js} +16 -7
- package/dist/chunks/LpCopyButton-DzHjnFs0.js +133 -0
- package/dist/chunks/LpCountdown-BQnmB4gq.js +231 -0
- package/dist/chunks/LpDataList-CHNYQw7h.js +121 -0
- package/dist/chunks/LpDatePicker-ac1_6o5P.js +169 -0
- package/dist/chunks/LpDateRangePicker-B15gYrZJ.js +127 -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-R4aYVtVV.js} +14 -6
- package/dist/chunks/LpEditable-Ws3DNQHn.js +238 -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-DDNg34tJ.js} +5 -3
- package/dist/chunks/{LpFileTreeNode-BXhTHaKW.js → LpFileTreeNode-DmPuYNZ_.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-C14Z9503.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-HvwUA5Ec.js} +27 -14
- package/dist/chunks/{LpHints-BUtBp9-w.js → LpHints-DxbPKOzQ.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-D64hywJI.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-DKK4ySZl.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-rmI1DjKN.js} +3 -3
- package/dist/chunks/{LpModal-hk1noDcr.js → LpModal-eBMt2Fqn.js} +8 -3
- package/dist/chunks/{LpModalNav-DQd2keZm.js → LpModalNav-DiyFCHiZ.js} +4 -4
- package/dist/chunks/{LpNotificationBell-X84PJIxD.js → LpNotificationBell-D-WhE5m2.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-GL2KOFK8.js} +26 -22
- package/dist/chunks/{LpPhoneInput-DP69HONS.js → LpPhoneInput-C60tx1tC.js} +12 -7
- package/dist/chunks/{LpPie-yLF9-aDR.js → LpPie-BgXZ3xmm.js} +159 -48
- package/dist/chunks/LpPipeline-B31h41Mu.js +329 -0
- package/dist/chunks/{LpPopover-DVnkHaOu.js → LpPopover-DmZ3wUtP.js} +13 -7
- package/dist/chunks/{LpProgress-CA86wxXu.js → LpProgress-CE-m5OpD.js} +23 -63
- package/dist/chunks/LpQuota-DaL8TgNe.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-yin9uQP7.js +73 -0
- package/dist/chunks/LpRepeater-D0S7jjwr.js +180 -0
- package/dist/chunks/LpRevealOnScroll-DcQjexLy.js +76 -0
- package/dist/chunks/{LpRollingText-DB7upf-5.js → LpRollingText-DiL717LK.js} +4 -3
- package/dist/chunks/LpScramble-TDO5Kdou.js +85 -0
- package/dist/chunks/LpSecretField-CfWyHv9m.js +316 -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-C_8cJtj6.js} +91 -111
- package/dist/chunks/LpShaderBackdrop-Bote0iuy.js +332 -0
- package/dist/chunks/LpShinyText-DX2ajtWq.js +48 -0
- package/dist/chunks/{LpSidebar-DfIiS-lB.js → LpSidebar-CZRXtvem.js} +5 -3
- package/dist/chunks/{LpSidebarNav-C5FDNI0t.js → LpSidebarNav-DBg1U1oW.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-DFKlPJgy.js} +38 -35
- package/dist/chunks/LpStatus-BsGLrpor.js +86 -0
- package/dist/chunks/LpStepMark-TrzOioa9.js +101 -0
- package/dist/chunks/LpStepper-BbnS5jUr.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-DZg80W-B.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-DxVZMtzN.js +416 -0
- package/dist/chunks/LpThemeStudioSection-BwhqYDLf.js +20 -0
- package/dist/chunks/LpThemeSwitcher-CadVBTBn.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-C17A1XCb.js} +1 -1
- package/dist/chunks/{LpTour-BUp3Jt2e.js → LpTour-CY2bFXge.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-Tew1Ykx4.js} +10 -9
- 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-DoHv0WMH.js +125 -0
- package/dist/chunks/RevealToggle-C-KzaQGJ.js +65 -0
- package/dist/chunks/StepConnector-37OgEOXA.js +41 -0
- package/dist/chunks/TourCard-BJ8i7dk0.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 +131 -160
- 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 +3 -1
- package/dist/components/LpDatePicker.vue.js +1 -1
- package/dist/components/LpDateRangePicker.vue.d.ts +3 -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 +2 -2
- package/dist/components/LpPaletteHeader.vue.js +2 -2
- package/dist/components/LpPalettePreview.vue.js +1 -1
- package/dist/components/LpPaletteStats.vue.js +2 -2
- package/dist/components/LpPasswordInput.vue.d.ts +19 -2
- 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 +3 -1
- package/dist/components/LpRollingText.vue.js +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 +45 -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 +13 -0
- package/dist/composables/useField.js +26 -1
- package/dist/composables/useForm.js +32 -9
- 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 +64 -0
- package/dist/icons-all.js +1 -1
- package/dist/index.d.ts +98 -1
- package/dist/index.js +263 -105
- package/dist/theme/define.d.ts +12 -0
- package/dist/theme/studio.d.ts +90 -0
- package/package.json +8 -2
- package/src/component-names.ts +33 -0
- package/src/components/AttentionDot.vue +27 -0
- package/src/components/ContextMenuItems.vue +5 -2
- 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 +162 -171
- 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 +13 -3
- 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 +55 -104
- package/src/components/LpDateRangePicker.vue +51 -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 +11 -2
- package/src/components/LpDropzone.vue +34 -13
- package/src/components/LpEditable.vue +295 -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 +25 -18
- 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 +14 -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 +131 -0
- package/src/components/LpRevealOnScroll.vue +125 -0
- package/src/components/LpRollingText.vue +12 -1
- package/src/components/LpScramble.vue +107 -0
- package/src/components/LpSecretField.vue +320 -0
- package/src/components/LpSegmented.vue +3 -10
- package/src/components/LpSelect.vue +74 -65
- 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 +59 -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 +38 -1
- package/src/composables/useForm.ts +41 -11
- 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 +96 -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,185 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Attributes (class, aria-*, data-*) reach the button itself, merged by LpButton.
|
|
3
|
+
export default { inheritAttrs: false }
|
|
4
|
+
|
|
5
|
+
export interface CopyButtonLabels extends Record<string, string> {
|
|
6
|
+
copy: string
|
|
7
|
+
copied: string
|
|
8
|
+
failed: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* What to copy: the text, or a function that produces it when clicked. A
|
|
13
|
+
* `Blob` is copied as itself (an image as PNG, see useClipboard).
|
|
14
|
+
*/
|
|
15
|
+
export type CopyValue =
|
|
16
|
+
| string
|
|
17
|
+
| number
|
|
18
|
+
| Blob
|
|
19
|
+
| (() => string | number | Blob | Promise<string | number | Blob>)
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
/*
|
|
24
|
+
* The one copy button: an icon that turns into a tick for a moment, a tip
|
|
25
|
+
* that says "Copy" and then "Copied", and nothing thrown when the clipboard
|
|
26
|
+
* refuses.
|
|
27
|
+
*
|
|
28
|
+
* Several kit components drew their own (the code block's header, the
|
|
29
|
+
* toaster's message copy, the lightbox's image copy), each with its own icon
|
|
30
|
+
* swap that snapped from one glyph to the other. This one cross-fades the two
|
|
31
|
+
* glyphs in place — the copy icon shrinks and turns away as the tick grows in
|
|
32
|
+
* — so the confirmation reads as the button answering, not as a redraw.
|
|
33
|
+
*
|
|
34
|
+
* `value` may be a function, sync or async, for text that is expensive or
|
|
35
|
+
* secret enough to fetch only when someone actually asks for it (a token
|
|
36
|
+
* revealed by the server on demand). While it resolves the button shows its
|
|
37
|
+
* spinner. Note the browser's clipboard wants the write to stay close to the
|
|
38
|
+
* click; a getter that takes seconds may be refused, which surfaces as the
|
|
39
|
+
* `failed` state and the `error` event rather than as an exception.
|
|
40
|
+
*/
|
|
41
|
+
import { computed, ref, useAttrs } from "vue"
|
|
42
|
+
import { useClipboard } from "../composables/useClipboard"
|
|
43
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
44
|
+
import LpButton from "./LpButton.vue"
|
|
45
|
+
import LpIcon from "./LpIcon.vue"
|
|
46
|
+
import LpTooltip from "./LpTooltip.vue"
|
|
47
|
+
|
|
48
|
+
const props = withDefaults(
|
|
49
|
+
defineProps<{
|
|
50
|
+
/** The text to copy, or a (possibly async) function that returns it on click. */
|
|
51
|
+
value: CopyValue
|
|
52
|
+
/** LpButton's sizes, so it sits in a toolbar of buttons at the same height. */
|
|
53
|
+
size?: "xs" | "sm" | "md" | "lg"
|
|
54
|
+
/**
|
|
55
|
+
* Glyph size in px, for a toolbar whose other icons are drawn at their
|
|
56
|
+
* own size (the lightbox's 18px row). Defaults to what suits `size`.
|
|
57
|
+
*/
|
|
58
|
+
iconSize?: number
|
|
59
|
+
/** LpButton's look. `ghost` by default: a copy button is a helper beside something, not a call to action. */
|
|
60
|
+
variant?: "solid" | "action" | "outline" | "soft" | "ghost" | "muted"
|
|
61
|
+
/**
|
|
62
|
+
* Visible text beside the icon. Without it the button is a square icon
|
|
63
|
+
* button and the tip names it.
|
|
64
|
+
*/
|
|
65
|
+
label?: string
|
|
66
|
+
/** A toast on success too, for a copy whose button is about to leave the screen (a menu, a closing dialog). */
|
|
67
|
+
toast?: string | false
|
|
68
|
+
/**
|
|
69
|
+
* Show the tick now, for a copy made some other way: a Ctrl+C shortcut on
|
|
70
|
+
* the row, a "Copy" item in its menu. The button then confirms it too, so
|
|
71
|
+
* there is one place that says the clipboard holds this value.
|
|
72
|
+
*/
|
|
73
|
+
copied?: boolean
|
|
74
|
+
disabled?: boolean
|
|
75
|
+
/** Where the tip opens. */
|
|
76
|
+
side?: "top" | "right" | "bottom" | "left"
|
|
77
|
+
labels?: Partial<CopyButtonLabels>
|
|
78
|
+
}>(),
|
|
79
|
+
{ size: "sm", variant: "ghost", toast: false, side: "top" },
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
const emit = defineEmits<{
|
|
83
|
+
/**
|
|
84
|
+
* The value reached the clipboard: its text, or for a `Blob` an empty
|
|
85
|
+
* string and the blob itself second, so handlers written for text keep
|
|
86
|
+
* their `(text: string)` shape.
|
|
87
|
+
*/
|
|
88
|
+
(e: "copied", text: string, blob?: Blob): void
|
|
89
|
+
/** The getter threw or the clipboard refused. */
|
|
90
|
+
(e: "error", error: unknown): void
|
|
91
|
+
}>()
|
|
92
|
+
|
|
93
|
+
const text = useLabels<CopyButtonLabels>(
|
|
94
|
+
"copyButton",
|
|
95
|
+
{ copy: "Copy", copied: "Copied", failed: "Couldn't copy" },
|
|
96
|
+
() => props.labels,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
const { copy, copied: justCopied } = useClipboard()
|
|
100
|
+
const busy = ref(false)
|
|
101
|
+
const failed = ref(false)
|
|
102
|
+
let failTimer: ReturnType<typeof setTimeout> | undefined
|
|
103
|
+
|
|
104
|
+
const state = computed<"idle" | "copied" | "failed">(() =>
|
|
105
|
+
failed.value ? "failed" : justCopied.value || props.copied ? "copied" : "idle",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
async function run() {
|
|
109
|
+
if (busy.value || props.disabled) return
|
|
110
|
+
let value: string | Blob
|
|
111
|
+
try {
|
|
112
|
+
let raw = props.value
|
|
113
|
+
if (typeof raw === "function") {
|
|
114
|
+
busy.value = true
|
|
115
|
+
raw = await raw()
|
|
116
|
+
}
|
|
117
|
+
value = raw instanceof Blob ? raw : String(raw)
|
|
118
|
+
} catch (error) {
|
|
119
|
+
busy.value = false
|
|
120
|
+
fail(error)
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
busy.value = false
|
|
124
|
+
if (await copy(value, { toast: props.toast })) {
|
|
125
|
+
failed.value = false
|
|
126
|
+
if (value instanceof Blob) emit("copied", "", value)
|
|
127
|
+
else emit("copied", value)
|
|
128
|
+
} else fail(new Error("Clipboard unavailable"))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function fail(error: unknown) {
|
|
132
|
+
failed.value = true
|
|
133
|
+
clearTimeout(failTimer)
|
|
134
|
+
failTimer = setTimeout(() => (failed.value = false), 1800)
|
|
135
|
+
emit("error", error)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const tip = computed(() =>
|
|
139
|
+
state.value === "copied" ? text.value.copied : state.value === "failed" ? text.value.failed : text.value.copy,
|
|
140
|
+
)
|
|
141
|
+
const iconSize = computed(() => props.iconSize ?? { xs: 13, sm: 14, md: 16, lg: 16 }[props.size])
|
|
142
|
+
const attrs = useAttrs()
|
|
143
|
+
</script>
|
|
144
|
+
|
|
145
|
+
<template>
|
|
146
|
+
<LpTooltip :content="tip" :label="!label" :side="side">
|
|
147
|
+
<LpButton
|
|
148
|
+
v-bind="attrs"
|
|
149
|
+
:variant="variant"
|
|
150
|
+
:size="size"
|
|
151
|
+
:square="!label"
|
|
152
|
+
:disabled="disabled"
|
|
153
|
+
:loading="busy"
|
|
154
|
+
:data-state="state"
|
|
155
|
+
@click="run"
|
|
156
|
+
>
|
|
157
|
+
<span class="relative inline-grid shrink-0 place-items-center" :style="{ width: `${iconSize}px`, height: `${iconSize}px` }">
|
|
158
|
+
<!-- Both glyphs are always there and trade places, so the change is a
|
|
159
|
+
morph in one spot rather than one icon replacing another. -->
|
|
160
|
+
<LpIcon
|
|
161
|
+
name="lucide:copy"
|
|
162
|
+
:size="iconSize"
|
|
163
|
+
class="lp-glyph-swap col-start-1 row-start-1"
|
|
164
|
+
:data-on="state === 'idle' || undefined"
|
|
165
|
+
/>
|
|
166
|
+
<LpIcon
|
|
167
|
+
name="lucide:check"
|
|
168
|
+
:size="iconSize"
|
|
169
|
+
class="lp-glyph-swap col-start-1 row-start-1 text-action"
|
|
170
|
+
:data-on="state === 'copied' || undefined"
|
|
171
|
+
/>
|
|
172
|
+
<LpIcon
|
|
173
|
+
name="lucide:x"
|
|
174
|
+
:size="iconSize"
|
|
175
|
+
class="lp-glyph-swap col-start-1 row-start-1 text-danger"
|
|
176
|
+
:data-on="state === 'failed' || undefined"
|
|
177
|
+
/>
|
|
178
|
+
</span>
|
|
179
|
+
<span v-if="label">{{ label }}</span>
|
|
180
|
+
</LpButton>
|
|
181
|
+
</LpTooltip>
|
|
182
|
+
<!-- The tip only speaks on hover/focus; the result is announced either way. -->
|
|
183
|
+
<span class="sr-only" role="status" aria-live="polite">{{ state === "idle" ? "" : tip }}</span>
|
|
184
|
+
</template>
|
|
185
|
+
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// The consumer's `class` is merged into the root's own (below) rather than
|
|
3
|
+
// appended to it, so opt out of the automatic pass-through that would add it
|
|
4
|
+
// a second time, unmerged.
|
|
5
|
+
export default { inheritAttrs: false }
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
/*
|
|
10
|
+
* Time left until something: "resend the code in 0:42", "maintenance in
|
|
11
|
+
* 2d 3h", "the lobby closes in 4:59".
|
|
12
|
+
*
|
|
13
|
+
* Resend in <LpCountdown :seconds="45" @done="canResend = true" />
|
|
14
|
+
* <LpCountdown :to="window.startsAt" format="units" />
|
|
15
|
+
* <LpCountdown :seconds="300" progress="ring" :urgent-at="30" />
|
|
16
|
+
*
|
|
17
|
+
* What it takes care of so the app does not:
|
|
18
|
+
*
|
|
19
|
+
* - the format adapts: a clock under an hour, the two largest units above
|
|
20
|
+
* it (timeSpan.ts), and it rounds up, so 0:00 means "now", not "nearly";
|
|
21
|
+
* - no drift: the remaining time is always deadline − now, read from the
|
|
22
|
+
* clock on each tick (useNow), and ticks land on the second boundary; a
|
|
23
|
+
* tab left in the background stops ticking and is right again the
|
|
24
|
+
* moment it is looked at;
|
|
25
|
+
* - digits roll individually (LpRollingText), so 0:42 → 0:41 moves one
|
|
26
|
+
* digit and a minute rolling over moves three;
|
|
27
|
+
* - role="timer", which is silent by design, plus a polite announcement at
|
|
28
|
+
* round marks (10 minutes, 1 minute, 10 seconds…) and at the end — a
|
|
29
|
+
* screen reader hears the countdown matter, not every second of it.
|
|
30
|
+
*/
|
|
31
|
+
import { computed, ref, watch } from "vue"
|
|
32
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
33
|
+
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
34
|
+
import { useNow } from "../composables/useNow"
|
|
35
|
+
import LpProgress from "./LpProgress.vue"
|
|
36
|
+
import LpRollingText from "./LpRollingText.vue"
|
|
37
|
+
import LpShift from "./LpShift.vue"
|
|
38
|
+
import {
|
|
39
|
+
ceilToSecond,
|
|
40
|
+
crossedMilestone,
|
|
41
|
+
DEFAULT_MILESTONES,
|
|
42
|
+
DEFAULT_UNIT_LABELS,
|
|
43
|
+
formatTime,
|
|
44
|
+
msToNextSecond,
|
|
45
|
+
padPart,
|
|
46
|
+
resolveFormat,
|
|
47
|
+
timeParts,
|
|
48
|
+
toEpoch,
|
|
49
|
+
type TimeFormat,
|
|
50
|
+
type TimeUnitLabels,
|
|
51
|
+
} from "./timeSpan"
|
|
52
|
+
|
|
53
|
+
export type CountdownLabels = {
|
|
54
|
+
/** Spoken at a milestone; `{time}` is the time left in words. */
|
|
55
|
+
left: string
|
|
56
|
+
/** Spoken when the time is up, and the timer's name from then on. */
|
|
57
|
+
done: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const props = withDefaults(
|
|
61
|
+
defineProps<{
|
|
62
|
+
/** The moment to count down to. Takes precedence over `seconds`. */
|
|
63
|
+
to?: Date | string | number
|
|
64
|
+
/**
|
|
65
|
+
* Count down this many seconds from when the countdown mounts — or from
|
|
66
|
+
* when this value changes, or `restart()` is called. For "try again in 45
|
|
67
|
+
* s", where the app has a wait rather than a moment.
|
|
68
|
+
*/
|
|
69
|
+
seconds?: number
|
|
70
|
+
/**
|
|
71
|
+
* `to` only: when the wait began, so the ring or bar can show how much of
|
|
72
|
+
* it is left. Without it a `to` countdown draws no progress.
|
|
73
|
+
*/
|
|
74
|
+
from?: Date | string | number
|
|
75
|
+
/** `auto` is a clock under an hour and units above it. */
|
|
76
|
+
format?: TimeFormat
|
|
77
|
+
/** Units format: how many places to keep ("2d 3h" is 2). */
|
|
78
|
+
places?: number
|
|
79
|
+
/**
|
|
80
|
+
* Freeze the count. A `seconds` countdown resumes where it stopped; a `to`
|
|
81
|
+
* countdown is tied to a real moment, so it resumes at the true time left.
|
|
82
|
+
*/
|
|
83
|
+
paused?: boolean
|
|
84
|
+
/** Draw the time left as a ring around the digits, or a bar under them. */
|
|
85
|
+
progress?: "ring" | "bar"
|
|
86
|
+
/** Ring only: outer diameter in px. */
|
|
87
|
+
ringSize?: number
|
|
88
|
+
/** Seconds left at which it turns danger: the end is close enough to act on. */
|
|
89
|
+
urgentAt?: number
|
|
90
|
+
/**
|
|
91
|
+
* Announce the time left at these marks (ms remaining) and at the end.
|
|
92
|
+
* `false` keeps it silent apart from what the timer role itself offers.
|
|
93
|
+
*/
|
|
94
|
+
announce?: boolean | number[]
|
|
95
|
+
labels?: Partial<CountdownLabels>
|
|
96
|
+
}>(),
|
|
97
|
+
{ format: "auto", places: 2, ringSize: 56, announce: true },
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
const emit = defineEmits<{
|
|
101
|
+
/** The time is up. Fires once per countdown (again after a restart). */
|
|
102
|
+
done: []
|
|
103
|
+
}>()
|
|
104
|
+
|
|
105
|
+
defineSlots<{
|
|
106
|
+
/** Replaces the digits once the time is up: a "Resend" button, "Live now". */
|
|
107
|
+
done?: () => unknown
|
|
108
|
+
}>()
|
|
109
|
+
|
|
110
|
+
const text = useLabels<CountdownLabels>("countdown", { left: "{time} left", done: "Time is up" }, () => props.labels)
|
|
111
|
+
const units = useLabels<TimeUnitLabels>("time", DEFAULT_UNIT_LABELS)
|
|
112
|
+
|
|
113
|
+
/* What the polite live region last said. */
|
|
114
|
+
const announcement = ref("")
|
|
115
|
+
|
|
116
|
+
/* ── The deadline, and the start it is measured from. */
|
|
117
|
+
const startedAt = ref(Date.now())
|
|
118
|
+
|
|
119
|
+
const deadline = computed(() => {
|
|
120
|
+
const to = toEpoch(props.to)
|
|
121
|
+
if (!Number.isNaN(to)) return to
|
|
122
|
+
if (typeof props.seconds === "number" && Number.isFinite(props.seconds)) return startedAt.value + props.seconds * 1000
|
|
123
|
+
return Number.NaN
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
const total = computed(() => {
|
|
127
|
+
if (!Number.isNaN(toEpoch(props.to))) {
|
|
128
|
+
const from = toEpoch(props.from)
|
|
129
|
+
return Number.isNaN(from) ? Number.NaN : deadline.value - from
|
|
130
|
+
}
|
|
131
|
+
return typeof props.seconds === "number" ? props.seconds * 1000 : Number.NaN
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* Pausing freezes the remaining time; resuming a `seconds` countdown moves
|
|
136
|
+
* its start forward by the pause, so the wait continues where it stood.
|
|
137
|
+
*/
|
|
138
|
+
const frozen = ref<number | null>(null)
|
|
139
|
+
let pausedAt = 0
|
|
140
|
+
|
|
141
|
+
watch(
|
|
142
|
+
() => props.paused,
|
|
143
|
+
(paused) => {
|
|
144
|
+
if (paused) {
|
|
145
|
+
pausedAt = Date.now()
|
|
146
|
+
frozen.value = Math.max(0, deadline.value - pausedAt)
|
|
147
|
+
} else if (frozen.value !== null) {
|
|
148
|
+
if (Number.isNaN(toEpoch(props.to))) startedAt.value += Date.now() - pausedAt
|
|
149
|
+
frozen.value = null
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
{ immediate: true },
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
const running = ref(true)
|
|
156
|
+
const now = useNow(() => running.value && !props.paused && Number.isFinite(deadline.value), {
|
|
157
|
+
nextIn: (at) => msToNextSecond(deadline.value - at),
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
const remaining = computed(() => {
|
|
161
|
+
if (frozen.value !== null) return frozen.value
|
|
162
|
+
const left = deadline.value - now.value
|
|
163
|
+
return Number.isFinite(left) ? Math.max(0, left) : 0
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
const shown = computed(() => ceilToSecond(remaining.value))
|
|
167
|
+
const isDone = computed(() => !Number.isNaN(deadline.value) && remaining.value <= 0)
|
|
168
|
+
|
|
169
|
+
watch(isDone, (done) => { running.value = !done }, { immediate: true })
|
|
170
|
+
|
|
171
|
+
/* ── The end, once per countdown. */
|
|
172
|
+
let fired = false
|
|
173
|
+
watch(
|
|
174
|
+
isDone,
|
|
175
|
+
(done) => {
|
|
176
|
+
if (!done) { fired = false; return }
|
|
177
|
+
if (fired) return
|
|
178
|
+
fired = true
|
|
179
|
+
emit("done")
|
|
180
|
+
if (props.announce !== false) announcement.value = text.value.done
|
|
181
|
+
},
|
|
182
|
+
{ immediate: true },
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
/** Starts a `seconds` countdown again from the full wait. */
|
|
186
|
+
function restart() {
|
|
187
|
+
// Cleared so the next "time is up" is a change the live region announces.
|
|
188
|
+
announcement.value = ""
|
|
189
|
+
startedAt.value = Date.now()
|
|
190
|
+
now.value = startedAt.value
|
|
191
|
+
if (props.paused) {
|
|
192
|
+
pausedAt = startedAt.value
|
|
193
|
+
frozen.value = Math.max(0, deadline.value - pausedAt)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
watch(() => props.seconds, () => { if (Number.isNaN(toEpoch(props.to))) restart() })
|
|
198
|
+
|
|
199
|
+
defineExpose({ restart, remaining })
|
|
200
|
+
|
|
201
|
+
/* ── What is printed and what is said. */
|
|
202
|
+
const parts = computed(() => timeParts(shown.value, props.format, units.value, props.places))
|
|
203
|
+
const clock = computed(() => resolveFormat(shown.value, props.format) === "clock")
|
|
204
|
+
|
|
205
|
+
/*
|
|
206
|
+
* Each part's digits, keyed from the right: a part that gains a digit (9:59 →
|
|
207
|
+
* 10:00) grows it on the left without re-keying the ones that were there.
|
|
208
|
+
*/
|
|
209
|
+
const cells = computed(() =>
|
|
210
|
+
parts.value.map((part) => {
|
|
211
|
+
const digits = padPart(part).split("")
|
|
212
|
+
return { part, digits: digits.map((d, i) => ({ d, key: `${part.unit}${digits.length - i}` })) }
|
|
213
|
+
}),
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
const spokenTime = computed(() => formatTime(shown.value, "units", units.value, 2))
|
|
217
|
+
|
|
218
|
+
const milestones = computed(() => (Array.isArray(props.announce) ? props.announce : DEFAULT_MILESTONES))
|
|
219
|
+
|
|
220
|
+
watch(remaining, (after, before) => {
|
|
221
|
+
if (props.announce === false || before === undefined || props.paused) return
|
|
222
|
+
const mark = crossedMilestone(before, after, milestones.value)
|
|
223
|
+
if (mark !== null && after > 0) {
|
|
224
|
+
announcement.value = text.value.left.replace("{time}", formatTime(ceilToSecond(after), "units", units.value, 2))
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
const urgent = computed(() =>
|
|
229
|
+
props.urgentAt !== undefined && !isDone.value && remaining.value <= props.urgentAt * 1000,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
const fraction = computed(() => {
|
|
233
|
+
if (!(total.value > 0)) return null
|
|
234
|
+
return Math.max(0, Math.min(1, remaining.value / total.value))
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
238
|
+
props.progress === "bar"
|
|
239
|
+
? "flex w-full min-w-0 flex-col gap-1.5"
|
|
240
|
+
: "relative inline-flex items-center justify-center align-baseline",
|
|
241
|
+
)
|
|
242
|
+
</script>
|
|
243
|
+
|
|
244
|
+
<template>
|
|
245
|
+
<span
|
|
246
|
+
:class="rootClass"
|
|
247
|
+
:data-urgent="urgent || undefined"
|
|
248
|
+
:data-done="isDone || undefined"
|
|
249
|
+
v-bind="rest"
|
|
250
|
+
>
|
|
251
|
+
<LpProgress
|
|
252
|
+
v-if="progress === 'ring' && fraction !== null"
|
|
253
|
+
variant="ring"
|
|
254
|
+
:value="fraction * 100"
|
|
255
|
+
:size="ringSize"
|
|
256
|
+
:thickness="Math.max(3, Math.round(ringSize / 14))"
|
|
257
|
+
:tone="urgent ? 'danger' : 'brand'"
|
|
258
|
+
:animate-on-mount="false"
|
|
259
|
+
:duration="1000"
|
|
260
|
+
aria-hidden="true"
|
|
261
|
+
/>
|
|
262
|
+
<span
|
|
263
|
+
role="timer"
|
|
264
|
+
:aria-label="isDone ? text.done : spokenTime"
|
|
265
|
+
class="inline-flex items-baseline font-mono tabular-nums transition-colors duration-(--duration-medium) motion-reduce:transition-none"
|
|
266
|
+
:class="[
|
|
267
|
+
urgent ? 'text-danger' : '',
|
|
268
|
+
progress === 'ring' && fraction !== null ? 'absolute text-xs font-medium' : '',
|
|
269
|
+
]"
|
|
270
|
+
>
|
|
271
|
+
<LpShift axis="width">
|
|
272
|
+
<Transition
|
|
273
|
+
mode="out-in"
|
|
274
|
+
enter-active-class="transition-opacity duration-(--duration-fast) ease-(--ease-emphasized) motion-reduce:transition-none"
|
|
275
|
+
enter-from-class="opacity-0"
|
|
276
|
+
leave-active-class="transition-opacity duration-(--duration-fast) ease-out motion-reduce:transition-none"
|
|
277
|
+
leave-to-class="opacity-0"
|
|
278
|
+
>
|
|
279
|
+
<span v-if="isDone && $slots.done" key="done" class="inline-flex font-sans">
|
|
280
|
+
<slot name="done" />
|
|
281
|
+
</span>
|
|
282
|
+
<span v-else key="time" class="inline-flex items-baseline whitespace-nowrap" aria-hidden="true">
|
|
283
|
+
<template v-for="(cell, ci) in cells" :key="cell.part.unit">
|
|
284
|
+
<LpRollingText
|
|
285
|
+
v-for="digit in cell.digits"
|
|
286
|
+
:key="digit.key"
|
|
287
|
+
:value="digit.d"
|
|
288
|
+
direction="down"
|
|
289
|
+
:duration="260"
|
|
290
|
+
/>
|
|
291
|
+
<span v-if="clock" class="px-px">{{ cell.part.after }}</span>
|
|
292
|
+
<span v-else class="font-sans text-[0.85em] text-muted" :class="ci < cells.length - 1 && 'mr-[0.35em]'">{{ cell.part.after }}</span>
|
|
293
|
+
</template>
|
|
294
|
+
</span>
|
|
295
|
+
</Transition>
|
|
296
|
+
</LpShift>
|
|
297
|
+
</span>
|
|
298
|
+
<LpProgress
|
|
299
|
+
v-if="progress === 'bar' && fraction !== null"
|
|
300
|
+
:value="fraction * 100"
|
|
301
|
+
:tone="urgent ? 'danger' : 'brand'"
|
|
302
|
+
:animate-on-mount="false"
|
|
303
|
+
:duration="1000"
|
|
304
|
+
aria-hidden="true"
|
|
305
|
+
/>
|
|
306
|
+
<span v-if="announce !== false" class="sr-only" aria-live="polite" aria-atomic="true">{{ announcement }}</span>
|
|
307
|
+
</span>
|
|
308
|
+
</template>
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default { inheritAttrs: false }
|
|
3
|
+
|
|
4
|
+
export interface DataListItem {
|
|
5
|
+
/** Stable key, used for slots (`#value-<key>`, `#label-<key>`) and list identity. Defaults to the label. */
|
|
6
|
+
key?: string
|
|
7
|
+
label: string
|
|
8
|
+
/** The value. Empty (`null`, `undefined`, `""`) shows the placeholder. */
|
|
9
|
+
value?: string | number | null
|
|
10
|
+
/** Offer a copy button for this value. */
|
|
11
|
+
copyable?: boolean
|
|
12
|
+
/** Monospace, for ids, addresses, hashes, versions. */
|
|
13
|
+
mono?: boolean
|
|
14
|
+
/** A note on what the label means, behind an info icon. */
|
|
15
|
+
hint?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DataListLabels extends Record<string, string> {
|
|
19
|
+
empty: string
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<script setup lang="ts">
|
|
24
|
+
/*
|
|
25
|
+
* Facts about one thing, as label and value pairs: a server's address,
|
|
26
|
+
* region, uptime and version; an account's id, plan and creation date.
|
|
27
|
+
*
|
|
28
|
+
* <LpDataList :items="[
|
|
29
|
+
* { label: 'Address', value: '10.0.4.12:25565', mono: true, copyable: true },
|
|
30
|
+
* { label: 'Region', value: 'Frankfurt' },
|
|
31
|
+
* { label: 'Players', value: online },
|
|
32
|
+
* ]" />
|
|
33
|
+
*
|
|
34
|
+
* Rendered as a real description list (<dl>/<dt>/<dd>), so assistive tech
|
|
35
|
+
* reads each value with its label.
|
|
36
|
+
*
|
|
37
|
+
* Side by side when there is room, label above value when there is not —
|
|
38
|
+
* decided by the list's OWN width (a container query), not the window's: the
|
|
39
|
+
* same list sits in a wide page and in a narrow side panel, and only its own
|
|
40
|
+
* width says which reads better. Labels share one column, so values line up.
|
|
41
|
+
*
|
|
42
|
+
* Values are watched things — a player count, a status — so a short value
|
|
43
|
+
* that changes rolls to the new one (LpRollingText) instead of flicking.
|
|
44
|
+
*/
|
|
45
|
+
import { computed, useSlots } from "vue"
|
|
46
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
47
|
+
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
48
|
+
import LpCopyButton from "./LpCopyButton.vue"
|
|
49
|
+
import LpIcon from "./LpIcon.vue"
|
|
50
|
+
import LpRollingText from "./LpRollingText.vue"
|
|
51
|
+
import LpTooltip from "./LpTooltip.vue"
|
|
52
|
+
|
|
53
|
+
const props = withDefaults(
|
|
54
|
+
defineProps<{
|
|
55
|
+
items: DataListItem[]
|
|
56
|
+
/**
|
|
57
|
+
* `auto` (default) is side by side, stacking when the list itself is
|
|
58
|
+
* narrower than ~20rem. `horizontal` and `stacked` fix one.
|
|
59
|
+
*/
|
|
60
|
+
layout?: "auto" | "horizontal" | "stacked"
|
|
61
|
+
/** Hairlines between rows, for a long list read as a table. */
|
|
62
|
+
divided?: boolean
|
|
63
|
+
size?: "sm" | "md"
|
|
64
|
+
labels?: Partial<DataListLabels>
|
|
65
|
+
}>(),
|
|
66
|
+
{ layout: "auto", size: "md" },
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
const text = useLabels<DataListLabels>("dataList", { empty: "—" }, () => props.labels)
|
|
70
|
+
const slots = useSlots()
|
|
71
|
+
|
|
72
|
+
const keyOf = (item: DataListItem) => item.key ?? item.label
|
|
73
|
+
const isEmpty = (value: DataListItem["value"]) => value === null || value === undefined || value === ""
|
|
74
|
+
// Long text wraps; rolling a paragraph would clip it to one line.
|
|
75
|
+
const rolls = (value: DataListItem["value"]) => !isEmpty(value) && String(value).length <= 48
|
|
76
|
+
|
|
77
|
+
/*
|
|
78
|
+
* Literal classes per layout: the consuming app's Tailwind scans this file as
|
|
79
|
+
* text, so container variants built at runtime would never be emitted.
|
|
80
|
+
* - the list is a two-column grid, each row a subgrid, so every label
|
|
81
|
+
* shares the first column and every value starts on one line;
|
|
82
|
+
* - stacked collapses it to one column.
|
|
83
|
+
*/
|
|
84
|
+
const GRID = {
|
|
85
|
+
auto: "grid-cols-1 @xs:grid-cols-[fit-content(40%)_minmax(0,1fr)]",
|
|
86
|
+
horizontal: "grid-cols-[fit-content(40%)_minmax(0,1fr)]",
|
|
87
|
+
stacked: "grid-cols-1",
|
|
88
|
+
} as const
|
|
89
|
+
const ROW = {
|
|
90
|
+
auto: "gap-0.5 @xs:col-span-2 @xs:grid-cols-subgrid @xs:gap-x-6",
|
|
91
|
+
horizontal: "col-span-2 grid-cols-subgrid gap-x-6",
|
|
92
|
+
stacked: "gap-0.5",
|
|
93
|
+
} as const
|
|
94
|
+
|
|
95
|
+
const { class: rootClass, attrs: rest } = useMergedAttrs(() => "@container w-full")
|
|
96
|
+
const pad = computed(() => (props.size === "sm" ? "py-1.5 text-xs" : "py-2.5 text-sm"))
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<template>
|
|
100
|
+
<div :class="rootClass" v-bind="rest">
|
|
101
|
+
<TransitionGroup
|
|
102
|
+
tag="dl"
|
|
103
|
+
class="grid"
|
|
104
|
+
:class="GRID[layout]"
|
|
105
|
+
move-class="transition-transform duration-(--duration-medium) ease-(--ease-emphasized) motion-reduce:transition-none"
|
|
106
|
+
enter-active-class="transition-opacity duration-(--duration-medium) motion-reduce:transition-none"
|
|
107
|
+
enter-from-class="opacity-0"
|
|
108
|
+
leave-active-class="hidden"
|
|
109
|
+
>
|
|
110
|
+
<div
|
|
111
|
+
v-for="(item, i) in items"
|
|
112
|
+
:key="keyOf(item)"
|
|
113
|
+
class="group/row grid items-baseline"
|
|
114
|
+
:class="[ROW[layout], pad, divided && i > 0 ? 'border-t border-line' : '']"
|
|
115
|
+
>
|
|
116
|
+
<dt class="flex min-w-0 items-center gap-1 text-muted">
|
|
117
|
+
<slot :name="`label-${keyOf(item)}`" :item="item">
|
|
118
|
+
<slot name="label" :item="item">{{ item.label }}</slot>
|
|
119
|
+
</slot>
|
|
120
|
+
<LpTooltip v-if="item.hint" :content="item.hint">
|
|
121
|
+
<button
|
|
122
|
+
type="button"
|
|
123
|
+
class="grid size-4 shrink-0 place-items-center rounded-full text-muted/80 outline-none hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
|
|
124
|
+
:aria-label="item.hint"
|
|
125
|
+
>
|
|
126
|
+
<LpIcon name="lucide:info" :size="12" />
|
|
127
|
+
</button>
|
|
128
|
+
</LpTooltip>
|
|
129
|
+
</dt>
|
|
130
|
+
<dd class="flex min-w-0 items-center gap-1.5 text-ink" :class="item.mono ? 'font-mono' : ''">
|
|
131
|
+
<span class="min-w-0 flex-1 [overflow-wrap:anywhere]">
|
|
132
|
+
<slot
|
|
133
|
+
v-if="slots[`value-${keyOf(item)}`] || slots.value"
|
|
134
|
+
:name="slots[`value-${keyOf(item)}`] ? `value-${keyOf(item)}` : 'value'"
|
|
135
|
+
:item="item"
|
|
136
|
+
:value="item.value"
|
|
137
|
+
/>
|
|
138
|
+
<span v-else-if="isEmpty(item.value)" class="text-muted">{{ text.empty }}</span>
|
|
139
|
+
<LpRollingText v-else-if="rolls(item.value)" :value="item.value!" align="start" wrap />
|
|
140
|
+
<template v-else>{{ item.value }}</template>
|
|
141
|
+
</span>
|
|
142
|
+
<!-- Quiet until the row is hovered or focused; always there on touch,
|
|
143
|
+
where nothing hovers. -->
|
|
144
|
+
<LpCopyButton
|
|
145
|
+
v-if="item.copyable && !isEmpty(item.value)"
|
|
146
|
+
:value="String(item.value)"
|
|
147
|
+
size="xs"
|
|
148
|
+
class="-my-1 size-6 shrink-0 opacity-0 transition-opacity duration-(--duration-fast) group-hover/row:opacity-100 focus-visible:opacity-100 data-[state=copied]:opacity-100 [@media(hover:none)]:opacity-100"
|
|
149
|
+
/>
|
|
150
|
+
</dd>
|
|
151
|
+
</div>
|
|
152
|
+
</TransitionGroup>
|
|
153
|
+
</div>
|
|
154
|
+
</template>
|