@leavepulse/ui 0.43.3 → 0.44.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 +75 -7
- package/dist/assets/{LpCommandPalette-DH7sPcKP.css → LpCommandPalette-DCP5g2ik.css} +21 -21
- package/dist/assets/{LpDropzone-BBoWvi-J.css → LpDropzone-i4BdMi1H.css} +12 -12
- package/dist/assets/{LpImageEditor-Ct7Y3aNu.css → LpImageEditor-CbMA82g8.css} +6 -6
- package/dist/assets/{LpShortcutsDialog-C6YoQ8_M.css → LpShortcutsDialog-ChUyU7oV.css} +7 -7
- package/dist/assets/{LpSkeleton-D7xU8IPV.css → LpSkeleton-H3Z5dEIk.css} +4 -1
- package/dist/assets/{LpTable-BgPLJOXa.css → LpTable-DDQNdf86.css} +9 -9
- package/dist/chunks/ContextMenuItems-CrfeNPWT.js +123 -0
- package/dist/chunks/{LayoutCanvas-Dd20boUG.js → LayoutCanvas-D-jPOAvg.js} +26 -9
- package/dist/chunks/{LayoutNode-DGpZA9LX.js → LayoutNode-D8Xy9N-o.js} +20 -8
- package/dist/chunks/{LpActionBar-BlvtGB2P.js → LpActionBar-BbwCF6l5.js} +24 -10
- package/dist/chunks/{LpAddressInput-B-Zvxsn7.js → LpAddressInput-BrOqkZqg.js} +53 -48
- package/dist/chunks/{LpAlert-BAYHDSS1.js → LpAlert-DkOIjsbq.js} +15 -8
- package/dist/chunks/{LpAppShell-BGQVIY-3.js → LpAppShell-BUKPcHco.js} +51 -24
- package/dist/chunks/{LpAutocomplete-DExlIZe_.js → LpAutocomplete-DxV3Y9h4.js} +50 -23
- package/dist/chunks/{LpAvatar-B3-xAOH3.js → LpAvatar-ClBbEYZN.js} +17 -8
- package/dist/chunks/LpBreadcrumbs-B8IJgFIc.js +95 -0
- package/dist/chunks/{LpButton-5VXXCjwF.js → LpButton-kqJBHIIb.js} +44 -14
- package/dist/chunks/{LpCard-BhI1N_vL.js → LpCard-CpRByR0q.js} +1 -1
- package/dist/chunks/{LpCheckbox-CVccKYaH.js → LpCheckbox-DpqchzH4.js} +7 -3
- package/dist/chunks/{LpCodeBlock-Bo0B4TFR.js → LpCodeBlock-DeHJPq7p.js} +48 -16
- package/dist/chunks/{LpColorPicker-BlXea7LM.js → LpColorPicker-Bhf1qxhB.js} +32 -9
- package/dist/chunks/{LpConfirmDialog-Q-bxff8z.js → LpConfirmDialog-CIUOSnWo.js} +58 -16
- package/dist/chunks/LpConfirmHost-CDm0Wtpv.js +49 -0
- package/dist/chunks/LpContextMenu-DQw5dGfG.js +68 -0
- package/dist/chunks/LpDatePicker-Drirx1AK.js +191 -0
- package/dist/chunks/{LpDrawer-BZ6k3hvo.js → LpDrawer-bScx-KMO.js} +63 -26
- package/dist/chunks/{LpFileTree-_5vFBgaK.js → LpFileTree-CSI_5SK2.js} +23 -8
- package/dist/chunks/{LpFileTreeNode-BdcIYKIE.js → LpFileTreeNode-B4MiSQX2.js} +2 -2
- package/dist/chunks/LpFormField-CIk5qosh.js +87 -0
- package/dist/chunks/{LpInput-9EXWUM9B.js → LpInput-C0e8y8_d.js} +26 -11
- package/dist/chunks/{LpLightbox-2sYVMY-S.js → LpLightbox-Mr0W_6HK.js} +147 -60
- package/dist/chunks/{LpLink-DCND-zyu.js → LpLink-Krkjhe_y.js} +15 -6
- package/dist/chunks/{LpLogViewer-CaRy5MeS.js → LpLogViewer-YBOaFnc_.js} +68 -34
- package/dist/chunks/{LpMapPicker-ul4oYno6.js → LpMapPicker-NUzS9e37.js} +38 -13
- package/dist/chunks/{LpMediaGallery-FF-NquIo.js → LpMediaGallery-DfJmMW0I.js} +6 -3
- package/dist/chunks/{LpModal-Bt0pQCI_.js → LpModal-DPwGKBPv.js} +49 -26
- package/dist/chunks/{LpModalNav-YaifHrXC.js → LpModalNav-DPg5H9f3.js} +9 -6
- package/dist/chunks/{LpNotificationBell-DyGGsMAM.js → LpNotificationBell-t5yn-siX.js} +58 -24
- package/dist/chunks/{LpNumberField-CWuX08iX.js → LpNumberField-DWnQKktS.js} +20 -7
- package/dist/chunks/{LpNumberFlow-amWYaeuA.js → LpNumberFlow-z4WPZcZu.js} +22 -12
- package/dist/chunks/{LpOtpInput-y2v-pmiN.js → LpOtpInput-CziPF3Oq.js} +37 -8
- package/dist/chunks/{LpPagination-5FxjmmB5.js → LpPagination-DFngX7Xo.js} +30 -19
- package/dist/chunks/{LpPasswordInput--sjYCkLS.js → LpPasswordInput-CWVDuBJ5.js} +10 -7
- package/dist/chunks/{LpPhoneInput-BDSKVl_z.js → LpPhoneInput-CD-ZTeSd.js} +57 -33
- package/dist/chunks/{LpPie-D6OnzwlJ.js → LpPie-BLco31Nx.js} +78 -21
- package/dist/chunks/{LpPopover-8268UmlC.js → LpPopover-p_e_shN3.js} +4 -2
- package/dist/chunks/{LpProgress-Btq5W4Ut.js → LpProgress-CBU1iI69.js} +5 -4
- package/dist/chunks/{LpRadioGroup-xPIuVDO_.js → LpRadioGroup-DmEW0l78.js} +9 -2
- package/dist/chunks/{LpSegmented-fV0Sk92p.js → LpSegmented-Dejd7cIx.js} +7 -3
- package/dist/chunks/{LpSelect-ANIVtrpM.js → LpSelect-DrRD9I7L.js} +40 -18
- package/dist/chunks/{LpSidebar-ClBUwoEz.js → LpSidebar-BgwUNWpJ.js} +30 -22
- package/dist/chunks/{LpSidebarNav-llOcs_Fn.js → LpSidebarNav-Bqxz8aWT.js} +14 -9
- package/dist/chunks/{LpSkeleton-CTI-vBgW.js → LpSkeleton-BB6XIV-w.js} +1 -1
- package/dist/chunks/{LpSlider-BwNIBkAK.js → LpSlider-Cuzmrd7p.js} +7 -2
- package/dist/chunks/{LpSparkline-C6xQ2dwv.js → LpSparkline-CB80N8SJ.js} +11 -4
- package/dist/chunks/{LpStat-CQaW8R--.js → LpStat-dXtyrdX9.js} +1 -1
- package/dist/chunks/{LpSwitch-Blizvqyt.js → LpSwitch-B1aLKulj.js} +40 -26
- package/dist/chunks/{LpTableOfContents-CjvxPMpU.js → LpTableOfContents-BUcAfGPQ.js} +36 -41
- package/dist/chunks/{LpTabs-Cefe_lys.js → LpTabs-_qMVaZJ6.js} +19 -5
- package/dist/chunks/LpTextarea-yP8hGedR.js +77 -0
- package/dist/chunks/LpThemeProvider-Wjm7Tuit.js +35 -0
- package/dist/chunks/LpThemeSwitcher-CKLc10KZ.js +169 -0
- package/dist/chunks/{LpTilt-DuyKbpkA.js → LpTilt-B7KHX1zH.js} +2 -2
- package/dist/chunks/{LpTimeField-K6nAGboX.js → LpTimeField-C5HRgS5H.js} +13 -7
- package/dist/chunks/{LpToaster-gDB1EaU3.js → LpToaster--oT6lfhL.js} +57 -28
- package/dist/chunks/{LpTooltip-hCLPAqMG.js → LpTooltip-CBdzeKNf.js} +13 -6
- package/dist/chunks/LpTooltipProvider-CftjV7Xc.js +25 -0
- package/dist/chunks/{LpTopologyCanvas-Cm1L39uL.js → LpTopologyCanvas-DocsLal3.js} +6 -1
- package/dist/chunks/{LpUptimeBar-DvAMno0S.js → LpUptimeBar-DLtxOMRy.js} +43 -14
- package/dist/chunks/{LpThemeSwitcher-Cue-myST.js → useTheme-C8JHUlPM.js} +173 -215
- package/dist/component-names.d.ts +1 -1
- package/dist/components/ContextMenuItems.vue.d.ts +8 -0
- package/dist/components/ContextMenuItems.vue.js +4 -0
- package/dist/components/LayoutCanvas.vue.js +1 -1
- package/dist/components/LayoutNode.vue.d.ts +4 -0
- package/dist/components/LayoutNode.vue.js +1 -1
- package/dist/components/LpActionBar.vue.d.ts +3 -0
- package/dist/components/LpActionBar.vue.js +1 -1
- package/dist/components/LpAddressInput.vue.d.ts +1 -1
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.d.ts +9 -0
- package/dist/components/LpAlert.vue.js +1 -1
- package/dist/components/LpAppShell.vue.d.ts +10 -0
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpAutocomplete.vue.d.ts +31 -6
- package/dist/components/LpAutocomplete.vue.js +1 -1
- package/dist/components/LpAvatar.vue.d.ts +3 -3
- package/dist/components/LpAvatar.vue.js +1 -1
- package/dist/components/LpBadge.vue.d.ts +2 -2
- package/dist/components/LpBreadcrumbs.vue.d.ts +13 -9
- package/dist/components/LpBreadcrumbs.vue.js +1 -1
- package/dist/components/LpButton.vue.d.ts +35 -98
- package/dist/components/LpButton.vue.js +1 -1
- package/dist/components/LpCard.vue.js +1 -1
- package/dist/components/LpChart.vue.d.ts +31 -2
- package/dist/components/LpChart.vue.js +317 -95
- package/dist/components/LpCheckbox.vue.js +1 -1
- package/dist/components/LpCodeBlock.vue.d.ts +2 -0
- package/dist/components/LpCodeBlock.vue.js +1 -1
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.d.ts +8 -10
- package/dist/components/LpCommandPalette.vue.js +88 -78
- package/dist/components/LpConfirmDialog.vue.d.ts +15 -3
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpConfirmHost.vue.d.ts +9 -0
- package/dist/components/LpConfirmHost.vue.js +4 -0
- package/dist/components/LpContextMenu.vue.d.ts +3 -5
- package/dist/components/LpContextMenu.vue.js +1 -1
- package/dist/components/LpDatePicker.vue.d.ts +24 -6
- package/dist/components/LpDatePicker.vue.js +1 -1
- package/dist/components/LpDrawer.vue.d.ts +10 -0
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpDropzone.vue.js +22 -6
- package/dist/components/LpFileTree.vue.d.ts +11 -2
- package/dist/components/LpFileTree.vue.js +1 -1
- package/dist/components/LpFileTreeNode.vue.js +1 -1
- package/dist/components/LpFormField.vue.d.ts +9 -0
- package/dist/components/LpFormField.vue.js +1 -1
- package/dist/components/LpImageEditor.vue.js +92 -57
- package/dist/components/LpInput.vue.d.ts +3 -3
- package/dist/components/LpInput.vue.js +1 -1
- package/dist/components/LpLightbox.vue.d.ts +17 -0
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpLink.vue.d.ts +11 -11
- package/dist/components/LpLink.vue.js +1 -1
- package/dist/components/LpLogViewer.vue.d.ts +20 -2
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpMapPicker.vue.d.ts +4 -4
- package/dist/components/LpMapPicker.vue.js +1 -1
- package/dist/components/LpMediaGallery.vue.js +1 -1
- package/dist/components/LpModal.vue.d.ts +16 -6
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/components/LpModalNav.vue.d.ts +0 -1
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.d.ts +11 -2
- package/dist/components/LpNotificationBell.vue.js +1 -1
- package/dist/components/LpNumberField.vue.d.ts +2 -0
- package/dist/components/LpNumberField.vue.js +1 -1
- package/dist/components/LpNumberFlow.vue.js +1 -1
- package/dist/components/LpOtpInput.vue.js +1 -1
- package/dist/components/LpPagination.vue.d.ts +2 -0
- package/dist/components/LpPagination.vue.js +1 -1
- package/dist/components/LpPasswordInput.vue.js +1 -1
- package/dist/components/LpPhoneInput.vue.js +1 -1
- package/dist/components/LpPie.vue.d.ts +21 -2
- package/dist/components/LpPie.vue.js +1 -1
- package/dist/components/LpPopover.vue.d.ts +2 -0
- package/dist/components/LpPopover.vue.js +1 -1
- package/dist/components/LpProgress.vue.js +1 -1
- package/dist/components/LpRadioGroup.vue.js +1 -1
- package/dist/components/LpSection.vue.d.ts +1 -1
- package/dist/components/LpSegmented.vue.d.ts +5 -0
- package/dist/components/LpSegmented.vue.js +1 -1
- package/dist/components/LpSelect.vue.d.ts +1 -0
- package/dist/components/LpSelect.vue.js +1 -1
- package/dist/components/LpShortcutsDialog.vue.d.ts +1 -1
- package/dist/components/LpShortcutsDialog.vue.js +11 -8
- package/dist/components/LpSidebar.vue.d.ts +6 -2
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.vue.js +1 -1
- package/dist/components/LpSkeleton.vue.js +1 -1
- package/dist/components/LpSlider.vue.js +1 -1
- package/dist/components/LpSparkline.vue.d.ts +1 -0
- package/dist/components/LpSparkline.vue.js +1 -1
- package/dist/components/LpStat.vue.js +1 -1
- package/dist/components/LpSwitch.vue.js +1 -1
- package/dist/components/LpTable.vue.d.ts +23 -1
- package/dist/components/LpTable.vue.js +74 -36
- package/dist/components/LpTableOfContents.vue.d.ts +4 -2
- package/dist/components/LpTableOfContents.vue.js +1 -1
- package/dist/components/LpTabs.vue.d.ts +2 -0
- package/dist/components/LpTabs.vue.js +1 -1
- package/dist/components/LpTextarea.vue.d.ts +19 -6
- package/dist/components/LpTextarea.vue.js +1 -1
- package/dist/components/LpThemeProvider.vue.d.ts +22 -0
- package/dist/components/LpThemeProvider.vue.js +4 -0
- package/dist/components/LpThemeSwitcher.vue.d.ts +13 -0
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpTilt.vue.js +1 -1
- package/dist/components/LpTimeField.vue.js +1 -1
- package/dist/components/LpToaster.vue.d.ts +6 -1
- package/dist/components/LpToaster.vue.js +1 -1
- package/dist/components/LpTooltip.vue.d.ts +1 -2
- package/dist/components/LpTooltip.vue.js +1 -1
- package/dist/components/LpTooltipProvider.vue.d.ts +22 -0
- package/dist/components/LpTooltipProvider.vue.js +4 -0
- package/dist/components/LpTopologyCanvas.vue.js +1 -1
- package/dist/components/LpUptimeBar.vue.d.ts +4 -0
- package/dist/components/LpUptimeBar.vue.js +1 -1
- package/dist/components/chart.d.ts +32 -1
- package/dist/components/chart.js +39 -4
- package/dist/components/codeHighlight.d.ts +6 -0
- package/dist/components/codeHighlight.js +92 -20
- package/dist/components/color.d.ts +1 -1
- package/dist/components/color.js +20 -9
- package/dist/components/commandSearch.d.ts +0 -1
- package/dist/components/commandSearch.js +1 -17
- package/dist/components/confirm.d.ts +25 -0
- package/dist/components/confirm.js +26 -0
- package/dist/components/countries.d.ts +6 -0
- package/dist/components/countries.js +7 -0
- package/dist/components/fileTree.js +16 -7
- package/dist/components/imageCrop.d.ts +42 -0
- package/dist/components/imageCrop.js +47 -0
- package/dist/components/link.d.ts +12 -0
- package/dist/components/link.js +19 -0
- package/dist/components/modalDepth.d.ts +18 -2
- package/dist/components/modalDepth.js +17 -5
- package/dist/components/otp.d.ts +5 -0
- package/dist/components/otp.js +10 -0
- package/dist/components/sidebar.d.ts +7 -1
- package/dist/components/tableSort.d.ts +8 -0
- package/dist/components/tableSort.js +31 -0
- package/dist/composables/useField.d.ts +34 -0
- package/dist/composables/useField.js +45 -0
- package/dist/composables/useFormDraft.d.ts +10 -3
- package/dist/composables/useFormDraft.js +9 -14
- package/dist/composables/useHotkeys.d.ts +17 -1
- package/dist/composables/useHotkeys.js +55 -18
- package/dist/composables/useInputFilter.d.ts +17 -0
- package/dist/composables/useInputFilter.js +47 -10
- package/dist/composables/useLpConfig.d.ts +24 -0
- package/dist/composables/useLpConfig.js +44 -0
- package/dist/composables/useRovingFocus.js +7 -0
- package/dist/composables/useTilt.d.ts +2 -2
- package/dist/composables/useTilt.js +5 -6
- package/dist/composables/useToast.d.ts +20 -0
- package/dist/composables/useToast.js +28 -4
- package/dist/composables/useZoomPan.d.ts +3 -3
- package/dist/composables/useZoomPan.js +4 -4
- package/dist/index.d.ts +33 -3
- package/dist/index.js +185 -139
- package/dist/layout/tree.d.ts +2 -0
- package/dist/layout/tree.js +16 -1
- package/dist/layout/useLayout.d.ts +15 -0
- package/dist/layout/useLayout.js +41 -14
- package/dist/theme/useTheme.d.ts +32 -9
- package/package.json +8 -7
- package/src/component-names.ts +5 -7
- package/src/components/ContextMenuItems.vue +88 -0
- package/src/components/LayoutCanvas.vue +26 -6
- package/src/components/LayoutNode.vue +19 -2
- package/src/components/LpActionBar.vue +30 -7
- package/src/components/LpAddressInput.vue +57 -41
- package/src/components/LpAlert.vue +16 -3
- package/src/components/LpAppShell.vue +45 -12
- package/src/components/LpAutocomplete.vue +46 -10
- package/src/components/LpAvatar.vue +19 -5
- package/src/components/LpBreadcrumbs.vue +65 -41
- package/src/components/LpButton.vue +59 -13
- package/src/components/LpChart.vue +272 -28
- package/src/components/LpCheckbox.vue +7 -3
- package/src/components/LpCodeBlock.vue +50 -14
- package/src/components/LpColorPicker.vue +39 -9
- package/src/components/LpCommandPalette.vue +36 -25
- package/src/components/LpConfirmDialog.vue +68 -17
- package/src/components/LpConfirmHost.vue +59 -0
- package/src/components/LpContextMenu.vue +14 -115
- package/src/components/LpDatePicker.vue +99 -68
- package/src/components/LpDrawer.vue +56 -19
- package/src/components/LpDropzone.vue +21 -4
- package/src/components/LpFileTree.vue +33 -7
- package/src/components/LpFormField.vue +30 -7
- package/src/components/LpImageEditor.vue +96 -57
- package/src/components/LpInput.vue +18 -7
- package/src/components/LpLightbox.vue +140 -29
- package/src/components/LpLink.vue +24 -13
- package/src/components/LpLogViewer.vue +86 -17
- package/src/components/LpMapPicker.vue +36 -13
- package/src/components/LpMediaGallery.vue +8 -2
- package/src/components/LpModal.vue +53 -27
- package/src/components/LpModalNav.vue +5 -2
- package/src/components/LpNotificationBell.vue +63 -17
- package/src/components/LpNumberField.vue +21 -6
- package/src/components/LpNumberFlow.vue +26 -10
- package/src/components/LpOtpInput.vue +43 -8
- package/src/components/LpPagination.vue +23 -7
- package/src/components/LpPasswordInput.vue +4 -1
- package/src/components/LpPhoneInput.vue +53 -27
- package/src/components/LpPie.vue +94 -15
- package/src/components/LpPopover.vue +3 -0
- package/src/components/LpProgress.vue +5 -3
- package/src/components/LpRadioGroup.vue +9 -2
- package/src/components/LpSegmented.vue +12 -2
- package/src/components/LpSelect.vue +0 -0
- package/src/components/LpShortcutsDialog.vue +8 -3
- package/src/components/LpSidebar.vue +14 -9
- package/src/components/LpSidebarNav.vue +17 -5
- package/src/components/LpSkeleton.vue +4 -1
- package/src/components/LpSlider.vue +7 -1
- package/src/components/LpSparkline.vue +11 -3
- package/src/components/LpSwitch.vue +30 -7
- package/src/components/LpTable.vue +91 -32
- package/src/components/LpTableOfContents.vue +50 -32
- package/src/components/LpTabs.vue +23 -6
- package/src/components/LpTextarea.vue +44 -6
- package/src/components/LpThemeProvider.vue +41 -0
- package/src/components/LpThemeSwitcher.vue +64 -17
- package/src/components/LpTilt.vue +2 -2
- package/src/components/LpTimeField.vue +14 -3
- package/src/components/LpToaster.vue +66 -26
- package/src/components/LpTooltip.vue +22 -5
- package/src/components/LpTooltipProvider.vue +24 -0
- package/src/components/LpTopologyCanvas.vue +12 -1
- package/src/components/LpUptimeBar.vue +46 -8
- package/src/components/chart.ts +77 -5
- package/src/components/codeHighlight.ts +111 -15
- package/src/components/color.ts +30 -9
- package/src/components/commandSearch.ts +0 -17
- package/src/components/confirm.ts +58 -0
- package/src/components/countries.ts +12 -0
- package/src/components/fileTree.ts +19 -7
- package/src/components/imageCrop.ts +107 -0
- package/src/components/link.ts +34 -0
- package/src/components/modalDepth.ts +48 -6
- package/src/components/otp.ts +16 -0
- package/src/components/sidebar.ts +7 -1
- package/src/components/tableSort.ts +37 -0
- package/src/composables/useField.ts +79 -0
- package/src/composables/useFormDraft.ts +25 -20
- package/src/composables/useHotkeys.ts +89 -18
- package/src/composables/useInputFilter.ts +77 -14
- package/src/composables/useLpConfig.ts +87 -0
- package/src/composables/useRovingFocus.ts +11 -0
- package/src/composables/useTilt.ts +5 -6
- package/src/composables/useToast.ts +56 -4
- package/src/composables/useZoomPan.ts +6 -6
- package/src/index.ts +38 -3
- package/src/layout/tree.ts +30 -0
- package/src/layout/useLayout.ts +68 -18
- package/src/nuxt.ts +59 -17
- package/src/runtime/config.plugin.ts +14 -0
- package/src/runtime/theme.plugin.ts +42 -22
- package/src/theme/useTheme.ts +206 -100
- package/dist/chunks/LpBreadcrumbs-p4jHe_V-.js +0 -62
- package/dist/chunks/LpContextMenu-CwW1QXe_.js +0 -217
- package/dist/chunks/LpDatePicker-CwOToxGq.js +0 -164
- package/dist/chunks/LpFormField-CazBc2-a.js +0 -62
- package/dist/chunks/LpTextarea-t7F6gv8E.js +0 -47
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { Motion } from "motion-v"
|
|
13
13
|
import { computed, onBeforeUnmount, onMounted, ref, useId, watch } from "vue"
|
|
14
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
14
15
|
import { usePillTransition } from "../composables/usePillTransition"
|
|
15
16
|
|
|
16
17
|
export interface TocLink {
|
|
@@ -23,14 +24,29 @@ export interface TocLink {
|
|
|
23
24
|
const props = withDefaults(
|
|
24
25
|
defineProps<{
|
|
25
26
|
links: TocLink[]
|
|
26
|
-
/** Heading shown above the list. */
|
|
27
|
+
/** Heading shown above the list (defaults to the `title` label). */
|
|
27
28
|
title?: string
|
|
28
29
|
/** px offset from the top used as the scroll-spy trigger line. */
|
|
29
30
|
offset?: number
|
|
31
|
+
labels?: Partial<{ title: string }>
|
|
30
32
|
}>(),
|
|
31
|
-
{
|
|
33
|
+
{ offset: 96 },
|
|
32
34
|
)
|
|
33
35
|
|
|
36
|
+
const text = useLabels("toc", { title: "On this page" }, () => props.labels)
|
|
37
|
+
const heading = computed(() => props.title ?? text.value.title)
|
|
38
|
+
|
|
39
|
+
// Every level of the tree as one ordered list; depth drives the indent, so
|
|
40
|
+
// links nested deeper than two levels render instead of vanishing.
|
|
41
|
+
interface TocRow {
|
|
42
|
+
link: TocLink
|
|
43
|
+
depth: number
|
|
44
|
+
}
|
|
45
|
+
function flatten(links: TocLink[], depth = 0): TocRow[] {
|
|
46
|
+
return links.flatMap((link) => [{ link, depth }, ...flatten(link.children ?? [], depth + 1)])
|
|
47
|
+
}
|
|
48
|
+
const rows = computed(() => flatten(props.links))
|
|
49
|
+
|
|
34
50
|
const activeId = ref<string>("")
|
|
35
51
|
// The id the pointer is hovering (null when not hovering). The indicator bar
|
|
36
52
|
// follows the hovered link, falling back to the scroll-spy active one — same
|
|
@@ -97,13 +113,28 @@ function unfreeze() {
|
|
|
97
113
|
safetyTimer = null
|
|
98
114
|
}
|
|
99
115
|
|
|
100
|
-
|
|
116
|
+
/*
|
|
117
|
+
* The element that actually scrolls to the heading. Inside an app shell that is
|
|
118
|
+
* a scroll region, not the window, and watching window.scrollY there saw no
|
|
119
|
+
* movement and unfroze on the first frame.
|
|
120
|
+
*/
|
|
121
|
+
function scrollerOf(el: HTMLElement): HTMLElement | null {
|
|
122
|
+
for (let node = el.parentElement; node; node = node.parentElement) {
|
|
123
|
+
const { overflowY } = getComputedStyle(node)
|
|
124
|
+
if (/(auto|scroll|overlay)/.test(overflowY) && node.scrollHeight > node.clientHeight) return node
|
|
125
|
+
}
|
|
126
|
+
return null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function suppressSpyUntilScrollEnds(target: HTMLElement) {
|
|
101
130
|
spySuppressed = true
|
|
102
131
|
if (rafId != null) cancelAnimationFrame(rafId)
|
|
103
132
|
if (safetyTimer != null) clearTimeout(safetyTimer)
|
|
104
|
-
|
|
133
|
+
const scroller = scrollerOf(target)
|
|
134
|
+
const position = () => (scroller ? scroller.scrollTop : window.scrollY)
|
|
135
|
+
let last = position()
|
|
105
136
|
const tick = () => {
|
|
106
|
-
const y =
|
|
137
|
+
const y = position()
|
|
107
138
|
if (y === last) {
|
|
108
139
|
unfreeze() // position held for a frame → scrolling has come to rest
|
|
109
140
|
return
|
|
@@ -122,9 +153,11 @@ function onClick(id: string, ev: MouseEvent) {
|
|
|
122
153
|
if (!el) return
|
|
123
154
|
ev.preventDefault()
|
|
124
155
|
activeId.value = id // mark immediately so the bar jumps to the clicked item
|
|
125
|
-
suppressSpyUntilScrollEnds()
|
|
156
|
+
suppressSpyUntilScrollEnds(el)
|
|
126
157
|
el.scrollIntoView({ behavior: "smooth", block: "start" })
|
|
127
|
-
|
|
158
|
+
// Keep the router's own state: replacing it with null broke scroll
|
|
159
|
+
// restoration and back/forward bookkeeping in vue-router apps.
|
|
160
|
+
history.replaceState(history.state, "", `#${id}`)
|
|
128
161
|
}
|
|
129
162
|
|
|
130
163
|
onMounted(setup)
|
|
@@ -136,16 +169,21 @@ onBeforeUnmount(() => {
|
|
|
136
169
|
</script>
|
|
137
170
|
|
|
138
171
|
<template>
|
|
139
|
-
<nav :aria-label="
|
|
172
|
+
<nav :aria-label="heading" class="text-sm">
|
|
140
173
|
<p class="mb-2.5 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted">
|
|
141
|
-
{{
|
|
174
|
+
{{ heading }}
|
|
142
175
|
</p>
|
|
143
176
|
<ul class="relative flex flex-col gap-0.5 border-l border-line" @mouseleave="hoveredId = null">
|
|
144
|
-
<li v-for="link in
|
|
177
|
+
<li v-for="{ link, depth } in rows" :key="link.id" class="flex flex-col">
|
|
145
178
|
<a
|
|
146
179
|
:href="`#${link.id}`"
|
|
147
|
-
class="relative -ml-px block py-1
|
|
148
|
-
:class="
|
|
180
|
+
class="relative -ml-px block py-1 leading-snug transition-colors duration-[var(--duration-fast)]"
|
|
181
|
+
:class="[
|
|
182
|
+
depth > 0 ? 'text-[13px]' : '',
|
|
183
|
+
litId === link.id ? 'text-brand' : 'text-muted hover:text-ink',
|
|
184
|
+
]"
|
|
185
|
+
:style="{ paddingLeft: `${0.875 + depth * 0.875}rem` }"
|
|
186
|
+
:aria-current="activeId === link.id ? 'location' : undefined"
|
|
149
187
|
@click="onClick(link.id, $event)"
|
|
150
188
|
@mouseenter="hoveredId = link.id"
|
|
151
189
|
>
|
|
@@ -157,26 +195,6 @@ onBeforeUnmount(() => {
|
|
|
157
195
|
/>
|
|
158
196
|
{{ link.text }}
|
|
159
197
|
</a>
|
|
160
|
-
|
|
161
|
-
<ul v-if="link.children?.length" class="flex flex-col gap-0.5">
|
|
162
|
-
<li v-for="child in link.children" :key="child.id">
|
|
163
|
-
<a
|
|
164
|
-
:href="`#${child.id}`"
|
|
165
|
-
class="relative -ml-px block py-1 pl-7 text-[13px] leading-snug transition-colors duration-[var(--duration-fast)]"
|
|
166
|
-
:class="litId === child.id ? 'text-brand' : 'text-muted hover:text-ink'"
|
|
167
|
-
@click="onClick(child.id, $event)"
|
|
168
|
-
@mouseenter="hoveredId = child.id"
|
|
169
|
-
>
|
|
170
|
-
<Motion
|
|
171
|
-
v-if="litId === child.id"
|
|
172
|
-
:layout-id="pillId"
|
|
173
|
-
:transition="pillTransition"
|
|
174
|
-
class="absolute -left-px top-0 h-full w-0.5 rounded-full bg-brand"
|
|
175
|
-
/>
|
|
176
|
-
{{ child.text }}
|
|
177
|
-
</a>
|
|
178
|
-
</li>
|
|
179
|
-
</ul>
|
|
180
198
|
</li>
|
|
181
199
|
</ul>
|
|
182
200
|
</nav>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { Motion, useReducedMotion } from "motion-v"
|
|
3
3
|
import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "reka-ui"
|
|
4
|
-
import { computed, ref, useId } from "vue"
|
|
4
|
+
import { computed, ref, useId, watch } from "vue"
|
|
5
5
|
import { usePillTransition } from "../composables/usePillTransition"
|
|
6
6
|
import LpIcon from "./LpIcon.vue"
|
|
7
7
|
|
|
@@ -12,9 +12,11 @@ export interface TabItem {
|
|
|
12
12
|
icon?: string
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
withDefaults(
|
|
15
|
+
const props = withDefaults(
|
|
16
16
|
defineProps<{
|
|
17
17
|
modelValue?: string
|
|
18
|
+
/** Initially active tab when `v-model` is not bound (default: the first). */
|
|
19
|
+
defaultValue?: string
|
|
18
20
|
items: TabItem[]
|
|
19
21
|
/**
|
|
20
22
|
* "contained" (default) wraps the triggers in a bordered, filled bar.
|
|
@@ -31,7 +33,22 @@ withDefaults(
|
|
|
31
33
|
}>(),
|
|
32
34
|
{ variant: "contained", accent: false, block: false },
|
|
33
35
|
)
|
|
34
|
-
defineEmits<{ (e: "update:modelValue", value: string): void }>()
|
|
36
|
+
const emit = defineEmits<{ (e: "update:modelValue", value: string): void }>()
|
|
37
|
+
|
|
38
|
+
// Held here rather than left to reka, so the active marker follows the active
|
|
39
|
+
// tab in uncontrolled use too: reading the prop alone left it unmarked.
|
|
40
|
+
const current = ref(props.modelValue ?? props.defaultValue ?? props.items[0]?.value)
|
|
41
|
+
watch(
|
|
42
|
+
() => props.modelValue,
|
|
43
|
+
(value) => {
|
|
44
|
+
if (value !== undefined) current.value = value
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
function select(value: string) {
|
|
49
|
+
if (props.modelValue === undefined) current.value = value
|
|
50
|
+
emit("update:modelValue", value)
|
|
51
|
+
}
|
|
35
52
|
|
|
36
53
|
// The pill sits under the hovered tab, falling back to the active one
|
|
37
54
|
// (UAProject header behaviour). motion-v shared layoutId animates the move.
|
|
@@ -80,8 +97,8 @@ function releaseFocusAfterPointer(event: PointerEvent) {
|
|
|
80
97
|
|
|
81
98
|
<template>
|
|
82
99
|
<TabsRoot
|
|
83
|
-
:model-value="
|
|
84
|
-
@update:model-value="(v) =>
|
|
100
|
+
:model-value="current"
|
|
101
|
+
@update:model-value="(v) => select(v as string)"
|
|
85
102
|
>
|
|
86
103
|
<TabsList
|
|
87
104
|
class="gap-1"
|
|
@@ -109,7 +126,7 @@ function releaseFocusAfterPointer(event: PointerEvent) {
|
|
|
109
126
|
<!-- Active marker: a bottom bar in "underline", a filled pill otherwise.
|
|
110
127
|
Both slide between tabs via the shared motion layoutId. -->
|
|
111
128
|
<Motion
|
|
112
|
-
v-if="pillUnder(item.value,
|
|
129
|
+
v-if="pillUnder(item.value, current)"
|
|
113
130
|
:layout-id="pillId"
|
|
114
131
|
:transition="pillTransition"
|
|
115
132
|
:class="variant === 'underline'
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default { inheritAttrs: false }
|
|
3
|
+
</script>
|
|
4
|
+
|
|
1
5
|
<script setup lang="ts">
|
|
2
|
-
import { computed } from "vue"
|
|
6
|
+
import { computed, useAttrs } from "vue"
|
|
7
|
+
import { useFieldControl } from "../composables/useField"
|
|
8
|
+
import { mergeClass } from "../composables/useMergedClass"
|
|
3
9
|
import LpFormField from "./LpFormField.vue"
|
|
4
10
|
|
|
5
11
|
const props = defineProps<{
|
|
@@ -21,25 +27,57 @@ const props = defineProps<{
|
|
|
21
27
|
defineEmits<{ (e: "update:modelValue", value: string): void }>()
|
|
22
28
|
|
|
23
29
|
const hasField = computed(() => !!props.label || !!props.hint || !!props.error)
|
|
30
|
+
|
|
31
|
+
// class/style lay out the field when there is one, and style the <textarea>
|
|
32
|
+
// when it stands alone; every other attr (name, maxlength, autofocus, @blur…)
|
|
33
|
+
// belongs to the <textarea> itself.
|
|
34
|
+
const attrs = useAttrs()
|
|
35
|
+
const textareaAttrs = computed(() => {
|
|
36
|
+
const { class: _c, style: _s, ...rest } = attrs
|
|
37
|
+
return rest
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const field = useFieldControl(() => ({
|
|
41
|
+
wrapped: hasField.value,
|
|
42
|
+
hasMessage: !!props.error || !!props.hint,
|
|
43
|
+
id: typeof attrs.id === "string" ? attrs.id : undefined,
|
|
44
|
+
invalid: props.invalid || !!props.error,
|
|
45
|
+
required: props.required,
|
|
46
|
+
disabled: props.disabled,
|
|
47
|
+
}))
|
|
48
|
+
|
|
49
|
+
const textareaClass = computed(() =>
|
|
50
|
+
mergeClass(
|
|
51
|
+
"w-full resize-y rounded-control border bg-surface-soft px-3 py-2 text-sm text-ink outline-none transition-colors duration-[var(--duration-fast)] placeholder:text-muted focus:border-brand focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-55",
|
|
52
|
+
field.value.invalid ? "border-danger focus:border-danger focus:ring-danger-soft" : "border-line",
|
|
53
|
+
hasField.value ? "" : (attrs.class as string),
|
|
54
|
+
),
|
|
55
|
+
)
|
|
24
56
|
</script>
|
|
25
57
|
|
|
26
58
|
<template>
|
|
27
59
|
<component
|
|
28
60
|
:is="hasField ? LpFormField : 'div'"
|
|
61
|
+
:id="hasField ? field.id : undefined"
|
|
29
62
|
:label="hasField ? label : undefined"
|
|
30
63
|
:hint="hasField ? hint : undefined"
|
|
31
64
|
:error="hasField ? error : undefined"
|
|
32
65
|
:required="hasField ? required : undefined"
|
|
33
|
-
:class="hasField ?
|
|
66
|
+
:class="hasField ? attrs.class : 'contents'"
|
|
67
|
+
:style="hasField ? attrs.style : undefined"
|
|
34
68
|
>
|
|
35
69
|
<textarea
|
|
70
|
+
v-bind="textareaAttrs"
|
|
71
|
+
:id="field.id"
|
|
36
72
|
:value="modelValue"
|
|
37
73
|
:placeholder="placeholder"
|
|
38
74
|
:rows="rows ?? 4"
|
|
39
|
-
:disabled="disabled"
|
|
40
|
-
:required="required || undefined"
|
|
41
|
-
|
|
42
|
-
:
|
|
75
|
+
:disabled="field.disabled"
|
|
76
|
+
:required="field.required || undefined"
|
|
77
|
+
:aria-invalid="field.invalid || undefined"
|
|
78
|
+
:aria-describedby="field.describedBy"
|
|
79
|
+
:class="textareaClass"
|
|
80
|
+
:style="hasField ? undefined : (attrs.style as never)"
|
|
43
81
|
@input="$emit('update:modelValue', ($event.target as HTMLTextAreaElement).value)"
|
|
44
82
|
/>
|
|
45
83
|
</component>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default { inheritAttrs: false }
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
/*
|
|
7
|
+
* Applies a theme to a subtree instead of the whole page: a dark sidebar in a
|
|
8
|
+
* light app, a live preview of a theme being edited, an embedded widget that
|
|
9
|
+
* keeps its own look. Every token is declared on this element, derived ones
|
|
10
|
+
* included, so kit components inside re-skin while the page around them keeps
|
|
11
|
+
* the global theme. Nothing is persisted; this is presentation only.
|
|
12
|
+
*/
|
|
13
|
+
import { computed, type Component } from "vue"
|
|
14
|
+
import type { TokenSet } from "../theme/tokens"
|
|
15
|
+
import { themeEntries } from "../theme/useTheme"
|
|
16
|
+
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(
|
|
19
|
+
defineProps<{
|
|
20
|
+
theme: TokenSet
|
|
21
|
+
/** Root element. A wrapper that should not affect layout can use `class="contents"`. */
|
|
22
|
+
as?: string | Component
|
|
23
|
+
}>(),
|
|
24
|
+
{ as: "div" },
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
// Paints its own surface and text so the scoped theme reads as one; a class
|
|
28
|
+
// like `bg-transparent` overrides that.
|
|
29
|
+
const { class: rootClass, attrs: rest } = useMergedAttrs("bg-surface text-ink")
|
|
30
|
+
|
|
31
|
+
const style = computed(() => ({
|
|
32
|
+
...Object.fromEntries(themeEntries(props.theme)),
|
|
33
|
+
colorScheme: props.theme.mode,
|
|
34
|
+
}))
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<template>
|
|
38
|
+
<component :is="as" :style="style" :data-theme-mode="theme.mode" :class="rootClass" v-bind="rest">
|
|
39
|
+
<slot />
|
|
40
|
+
</component>
|
|
41
|
+
</template>
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* fetches; it just drives the theme engine.
|
|
13
13
|
*/
|
|
14
14
|
import { computed, onMounted, ref } from "vue"
|
|
15
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
15
16
|
import { presets } from "../theme/presets"
|
|
16
17
|
import type { TokenSet } from "../theme/tokens"
|
|
17
18
|
import { useTheme } from "../theme/useTheme"
|
|
@@ -51,6 +52,13 @@ const props = withDefaults(
|
|
|
51
52
|
transition?: boolean
|
|
52
53
|
size?: "sm" | "md" | "lg"
|
|
53
54
|
"aria-label"?: string
|
|
55
|
+
/**
|
|
56
|
+
* Offer a "System" entry in the menu that follows the OS colour scheme,
|
|
57
|
+
* switching between these two themes (names from `themes`, or TokenSets).
|
|
58
|
+
*/
|
|
59
|
+
system?: { light: string | TokenSet; dark: string | TokenSet }
|
|
60
|
+
/** Text overrides. `trigger` may contain `{name}`. */
|
|
61
|
+
labels?: Partial<{ trigger: string; system: string }>
|
|
54
62
|
}>(),
|
|
55
63
|
{
|
|
56
64
|
variant: "swatch",
|
|
@@ -66,6 +74,12 @@ const emit = defineEmits<{
|
|
|
66
74
|
|
|
67
75
|
const theme = useTheme()
|
|
68
76
|
|
|
77
|
+
const text = useLabels(
|
|
78
|
+
"themeSwitcher",
|
|
79
|
+
{ trigger: "Theme: {name}. Click to switch, right-click to choose.", system: "System" },
|
|
80
|
+
() => props.labels,
|
|
81
|
+
)
|
|
82
|
+
|
|
69
83
|
const list = computed<TokenSet[]>(() =>
|
|
70
84
|
props.themes && props.themes.length ? props.themes : Object.values(presets),
|
|
71
85
|
)
|
|
@@ -75,9 +89,6 @@ const list = computed<TokenSet[]>(() =>
|
|
|
75
89
|
const activeName = computed(
|
|
76
90
|
() => props.modelValue ?? (theme.currentName.value || list.value[0]?.name || ""),
|
|
77
91
|
)
|
|
78
|
-
const active = computed<TokenSet | undefined>(
|
|
79
|
-
() => list.value.find((t) => t.name === activeName.value) ?? list.value[0],
|
|
80
|
-
)
|
|
81
92
|
|
|
82
93
|
// Apply the consumer's default on mount when nothing is cached yet. init()
|
|
83
94
|
// hydrates from the cache first (a saved choice wins), so this only takes effect
|
|
@@ -140,18 +151,49 @@ function cycle(ev: MouseEvent) {
|
|
|
140
151
|
// Right-click menu: every theme, the active one checked. A circular-reveal from
|
|
141
152
|
// the (saved) click point isn't available here, so the menu picks reveal from
|
|
142
153
|
// the trigger centre via applyWithTransition's default origin.
|
|
143
|
-
|
|
144
|
-
list.value.
|
|
154
|
+
function resolve(t: string | TokenSet): TokenSet | undefined {
|
|
155
|
+
return typeof t === "string" ? list.value.find((x) => x.name === t) : t
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const systemPair = computed(() => {
|
|
159
|
+
if (!props.system) return null
|
|
160
|
+
const light = resolve(props.system.light)
|
|
161
|
+
const dark = resolve(props.system.dark)
|
|
162
|
+
return light && dark ? { light, dark } : null
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
function followSystem() {
|
|
166
|
+
const pair = systemPair.value
|
|
167
|
+
if (!pair) return
|
|
168
|
+
theme.setSystem(pair)
|
|
169
|
+
const next = theme.current.value as TokenSet | null
|
|
170
|
+
if (next) {
|
|
171
|
+
emit("update:modelValue", next.name)
|
|
172
|
+
emit("change", next)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const menu = computed<ContextMenuItemDef[]>(() => {
|
|
177
|
+
const following = theme.system.value
|
|
178
|
+
const items: ContextMenuItemDef[] = list.value.map((t) => ({
|
|
145
179
|
label: t.name,
|
|
146
180
|
icon:
|
|
147
|
-
t.name === activeName.value
|
|
181
|
+
!following && t.name === activeName.value
|
|
148
182
|
? "lucide:check"
|
|
149
183
|
: t.mode === "dark"
|
|
150
184
|
? "lucide:moon"
|
|
151
185
|
: "lucide:sun",
|
|
152
186
|
onSelect: () => applyTheme(t),
|
|
153
|
-
}))
|
|
154
|
-
)
|
|
187
|
+
}))
|
|
188
|
+
if (systemPair.value) {
|
|
189
|
+
items.unshift({
|
|
190
|
+
label: text.value.system,
|
|
191
|
+
icon: following ? "lucide:check" : "lucide:monitor",
|
|
192
|
+
onSelect: followSystem,
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
return items
|
|
196
|
+
})
|
|
155
197
|
|
|
156
198
|
/*
|
|
157
199
|
* The theme the SERVER rendered is not necessarily the one the client restores:
|
|
@@ -176,7 +218,7 @@ const labelName = computed(() =>
|
|
|
176
218
|
)
|
|
177
219
|
|
|
178
220
|
const triggerLabel = computed(
|
|
179
|
-
() => props["aria-label"] ??
|
|
221
|
+
() => props["aria-label"] ?? text.value.trigger.replace("{name}", labelName.value),
|
|
180
222
|
)
|
|
181
223
|
</script>
|
|
182
224
|
|
|
@@ -197,23 +239,28 @@ const triggerLabel = computed(
|
|
|
197
239
|
"
|
|
198
240
|
@click="cycle"
|
|
199
241
|
>
|
|
200
|
-
<!-- swatch: brand over surface,
|
|
242
|
+
<!-- swatch: brand over surface, read from the applied variables rather
|
|
243
|
+
than the theme object, so the server's render (painted from the
|
|
244
|
+
cookie) and the client's agree without a hydration patch -->
|
|
201
245
|
<span
|
|
202
246
|
v-if="variant !== 'icon'"
|
|
203
247
|
class="grid shrink-0 place-items-center rounded-control border border-line/60"
|
|
204
248
|
:class="SIZES[size]"
|
|
205
|
-
|
|
249
|
+
style="background: var(--color-surface)"
|
|
206
250
|
>
|
|
207
|
-
<span
|
|
208
|
-
class="size-3 rounded-full"
|
|
209
|
-
:style="{ background: active?.colors.brand }"
|
|
210
|
-
/>
|
|
251
|
+
<span class="size-3 rounded-full" style="background: var(--color-brand)" />
|
|
211
252
|
</span>
|
|
212
|
-
<!-- icon: sun/moon by mode -->
|
|
253
|
+
<!-- icon: sun/moon by mode, chosen by CSS from the applied theme's mode -->
|
|
213
254
|
<span v-else class="grid shrink-0 place-items-center" :class="SIZES[size]">
|
|
214
255
|
<LpIcon
|
|
215
|
-
|
|
256
|
+
name="lucide:moon"
|
|
257
|
+
:size="ICON_SIZE[size]"
|
|
258
|
+
class="in-data-[theme-mode=light]:hidden"
|
|
259
|
+
/>
|
|
260
|
+
<LpIcon
|
|
261
|
+
name="lucide:sun"
|
|
216
262
|
:size="ICON_SIZE[size]"
|
|
263
|
+
class="hidden in-data-[theme-mode=light]:block"
|
|
217
264
|
/>
|
|
218
265
|
</span>
|
|
219
266
|
|
|
@@ -31,12 +31,12 @@ const props = withDefaults(
|
|
|
31
31
|
{ max: 10, lift: 8, scale: 1.03, glare: false, perspective: 900 },
|
|
32
32
|
)
|
|
33
33
|
|
|
34
|
-
const tilt = useTilt({
|
|
34
|
+
const tilt = useTilt(() => ({
|
|
35
35
|
max: props.max,
|
|
36
36
|
lift: props.lift,
|
|
37
37
|
scale: props.scale,
|
|
38
38
|
glare: props.glare,
|
|
39
|
-
})
|
|
39
|
+
}))
|
|
40
40
|
|
|
41
41
|
const stageStyle = computed(() => ({ perspective: `${props.perspective}px` }))
|
|
42
42
|
</script>
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { Time } from "@internationalized/date"
|
|
14
14
|
import { TimeFieldInput, TimeFieldRoot } from "reka-ui"
|
|
15
15
|
import { computed } from "vue"
|
|
16
|
+
import { useFieldControl } from "../composables/useField"
|
|
16
17
|
import LpIcon from "./LpIcon.vue"
|
|
17
18
|
|
|
18
19
|
const props = withDefaults(
|
|
@@ -50,6 +51,12 @@ function onUpdate(next: unknown) {
|
|
|
50
51
|
emit("update:modelValue", `${pad(next.hour)}:${pad(next.minute)}`)
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
const field = useFieldControl(() => ({
|
|
55
|
+
wrapped: false,
|
|
56
|
+
invalid: props.invalid,
|
|
57
|
+
disabled: props.disabled,
|
|
58
|
+
}))
|
|
59
|
+
|
|
53
60
|
const shellSize = {
|
|
54
61
|
sm: "h-(--size-control-sm) text-xs",
|
|
55
62
|
md: "h-(--size-control-md) text-sm",
|
|
@@ -64,16 +71,20 @@ const shellSize = {
|
|
|
64
71
|
:hour-cycle="hourCycle"
|
|
65
72
|
:locale="locale"
|
|
66
73
|
granularity="minute"
|
|
67
|
-
:disabled="disabled"
|
|
74
|
+
:disabled="field.disabled"
|
|
75
|
+
:aria-invalid="field.invalid || undefined"
|
|
76
|
+
:aria-describedby="field.describedBy"
|
|
68
77
|
class="flex w-full items-center gap-0.5 rounded-control border bg-surface-soft px-3 text-ink transition-colors duration-[var(--duration-fast)] focus-within:ring-2 focus-within:ring-ring data-[disabled]:cursor-not-allowed data-[disabled]:opacity-55"
|
|
69
78
|
:class="[
|
|
70
79
|
shellSize[size],
|
|
71
|
-
invalid ? 'border-danger focus-within:ring-danger-soft' : 'border-line focus-within:border-brand',
|
|
80
|
+
field.invalid ? 'border-danger focus-within:ring-danger-soft' : 'border-line focus-within:border-brand',
|
|
72
81
|
]"
|
|
73
82
|
@update:model-value="onUpdate"
|
|
74
83
|
>
|
|
75
84
|
<LpIcon v-if="icon" name="lucide:clock" :size="15" class="mr-1.5 shrink-0 text-muted" />
|
|
76
|
-
|
|
85
|
+
<!-- Indexed keys: in 12-hour mode the segments hold two "literal" parts
|
|
86
|
+
(the ":" and the space before AM/PM), which a key by part repeats. -->
|
|
87
|
+
<template v-for="(item, i) in segments" :key="i">
|
|
77
88
|
<TimeFieldInput v-if="item.part === 'literal'" :part="item.part" class="text-muted">
|
|
78
89
|
{{ item.value }}
|
|
79
90
|
</TimeFieldInput>
|