@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
package/dist/theme/useTheme.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { type ComputedRef, type DeepReadonly, type Ref } from "vue";
|
|
2
2
|
import { type TokenSet } from "./tokens";
|
|
3
|
+
/** A light/dark pair followed by the operating system's colour scheme. */
|
|
4
|
+
export interface SystemThemes {
|
|
5
|
+
light: TokenSet;
|
|
6
|
+
dark: TokenSet;
|
|
7
|
+
}
|
|
8
|
+
/** Whether the OS asks for dark. True where it cannot be asked (server, old engines). */
|
|
9
|
+
export declare function prefersDarkScheme(): boolean;
|
|
10
|
+
/** Every CSS variable a theme sets, in application order. */
|
|
11
|
+
export declare function themeEntries(input: TokenSet): [string, string][];
|
|
3
12
|
export declare function applyTheme(theme: TokenSet, root?: HTMLElement): void;
|
|
4
13
|
/** Point a circular theme reveal expands from (viewport px). */
|
|
5
14
|
export interface RevealOrigin {
|
|
@@ -11,31 +20,45 @@ export declare function serializeTheme(theme: TokenSet): string;
|
|
|
11
20
|
export declare function themeToCssVars(theme: TokenSet): string;
|
|
12
21
|
/** Render a theme as a complete CSS rule (default selector `:root`). */
|
|
13
22
|
export declare function themeToCssRule(theme: TokenSet, selector?: string): string;
|
|
14
|
-
|
|
23
|
+
export declare function normalizeTheme(raw: Partial<TokenSet> & {
|
|
24
|
+
name?: string;
|
|
25
|
+
}): TokenSet;
|
|
26
|
+
/** Parse + validate a theme JSON; throws on a malformed shape. */
|
|
15
27
|
export declare function parseTheme(json: string): TokenSet;
|
|
16
28
|
export declare function bootstrapTheme(fallback: TokenSet): TokenSet;
|
|
17
29
|
export declare function cacheTheme(theme: TokenSet): void;
|
|
30
|
+
/** Persist "follow the OS" with the pair it switches between. */
|
|
31
|
+
export declare function cacheSystemThemes(system: SystemThemes): void;
|
|
18
32
|
export declare const THEME_COOKIE = "lp-theme";
|
|
19
33
|
/**
|
|
20
34
|
* Resolve a theme name (as read from the `lp-theme` cookie) against a set of
|
|
21
35
|
* known themes. Returns `null` when the name is absent or unknown, so the caller
|
|
22
36
|
* can fall back to its own default.
|
|
23
37
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
38
|
+
* Accepts both a raw and an already-decoded value: frameworks differ on whether
|
|
39
|
+
* their cookie helpers decode, and a name containing `%` must not throw.
|
|
40
|
+
* A "follow the OS" cookie resolves to its dark member unless `prefersDark` is
|
|
41
|
+
* false (pass it from the `Sec-CH-Prefers-Color-Scheme` request header).
|
|
26
42
|
*/
|
|
27
|
-
export declare function themeFromCookie(cookieValue: string | null | undefined, themes: readonly TokenSet[]
|
|
43
|
+
export declare function themeFromCookie(cookieValue: string | null | undefined, themes: readonly TokenSet[], options?: {
|
|
44
|
+
prefersDark?: boolean;
|
|
45
|
+
}): TokenSet | null;
|
|
28
46
|
export interface UseTheme {
|
|
29
47
|
/** The active theme, kept in sync by the kit. Null until hydrated (SSR/first
|
|
30
|
-
* paint)
|
|
48
|
+
* paint), reactive, so a switcher binds straight to it. */
|
|
31
49
|
current: DeepReadonly<Ref<TokenSet | null>>;
|
|
32
50
|
/** The active theme's name (""="not yet hydrated"). */
|
|
33
51
|
currentName: ComputedRef<string>;
|
|
34
|
-
/**
|
|
35
|
-
|
|
52
|
+
/** True while the theme follows the OS colour scheme (see setSystem). */
|
|
53
|
+
system: ComputedRef<boolean>;
|
|
54
|
+
/** Set + persist a theme (with the circular reveal). Stops following the OS. */
|
|
36
55
|
setTheme: (theme: TokenSet, origin?: RevealOrigin) => void;
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
56
|
+
/**
|
|
57
|
+
* Follow the OS colour scheme, switching between `themes.light` and
|
|
58
|
+
* `themes.dark` as it changes. Persisted like any other choice.
|
|
59
|
+
*/
|
|
60
|
+
setSystem: (themes: SystemThemes, origin?: RevealOrigin) => void;
|
|
61
|
+
/** Seed the fallback (used when nothing is cached) and hydrate now. */
|
|
39
62
|
init: (fallback: TokenSet) => TokenSet;
|
|
40
63
|
apply: (theme: TokenSet) => void;
|
|
41
64
|
applyWithTransition: (theme: TokenSet, origin?: RevealOrigin) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leavepulse/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "LeavePulse token-driven Vue component kit.",
|
|
6
6
|
"repository": {
|
|
@@ -28,10 +28,11 @@
|
|
|
28
28
|
"./scrollbar.css": "./src/tokens/scrollbar.css",
|
|
29
29
|
"./canvas.css": "./src/canvas.css",
|
|
30
30
|
"./package.json": "./package.json",
|
|
31
|
-
"./dist
|
|
31
|
+
"./dist/*.js": {
|
|
32
32
|
"types": "./dist/*.d.ts",
|
|
33
|
-
"default": "./dist
|
|
34
|
-
}
|
|
33
|
+
"default": "./dist/*.js"
|
|
34
|
+
},
|
|
35
|
+
"./dist/*": "./dist/*"
|
|
35
36
|
},
|
|
36
37
|
"publishConfig": {
|
|
37
38
|
"registry": "https://registry.npmjs.org",
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"sideEffects": [
|
|
41
42
|
"**/*.css",
|
|
42
43
|
"**/icons.ts",
|
|
43
|
-
"**/
|
|
44
|
+
"**/chunks/LpIcon-*.js"
|
|
44
45
|
],
|
|
45
46
|
"scripts": {
|
|
46
47
|
"dev": "vite",
|
|
@@ -52,14 +53,14 @@
|
|
|
52
53
|
"test": "bun test",
|
|
53
54
|
"release": "npm version patch -m \"chore: release v%s\" && git push --follow-tags",
|
|
54
55
|
"release:minor": "npm version minor -m \"chore: release v%s\" && git push --follow-tags",
|
|
55
|
-
"release:major": "npm version major -m \"chore: release v%s\" && git push --follow-tags"
|
|
56
|
+
"release:major": "npm version major -m \"chore: release v%s\" && git push --follow-tags",
|
|
57
|
+
"check:pack": "node scripts/check-pack.mjs"
|
|
56
58
|
},
|
|
57
59
|
"peerDependencies": {
|
|
58
60
|
"@iconify-json/lucide": "^1.2.0",
|
|
59
61
|
"@iconify/vue": "^5.0.0",
|
|
60
62
|
"@internationalized/date": "^3.5.0",
|
|
61
63
|
"@nuxt/kit": "^3.0.0 || ^4.0.0",
|
|
62
|
-
"@vueuse/core": "^12.0.0 || ^13.0.0 || ^14.0.0",
|
|
63
64
|
"motion-v": "^2.2.0",
|
|
64
65
|
"reka-ui": "^2.9.0",
|
|
65
66
|
"tailwind-variants": "^3.0.0",
|
package/src/component-names.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/* AUTO-GENERATED by scripts/gen-component-names.mjs — do not edit.
|
|
2
2
|
* The full list of kit component names, kept in sync with the component files
|
|
3
|
-
* so the Nuxt module (./nuxt) auto-registers every component including new ones. */
|
|
3
|
+
* so the Nuxt module (./nuxt) auto-registers every exported component including new ones. */
|
|
4
4
|
export const COMPONENT_NAMES = [
|
|
5
5
|
"LayoutCanvas",
|
|
6
|
-
"LayoutNode",
|
|
7
6
|
"LpActionBar",
|
|
8
7
|
"LpAddressInput",
|
|
9
8
|
"LpAlert",
|
|
@@ -21,6 +20,7 @@ export const COMPONENT_NAMES = [
|
|
|
21
20
|
"LpColorPicker",
|
|
22
21
|
"LpCommandPalette",
|
|
23
22
|
"LpConfirmDialog",
|
|
23
|
+
"LpConfirmHost",
|
|
24
24
|
"LpContextMenu",
|
|
25
25
|
"LpDatePicker",
|
|
26
26
|
"LpDisclosure",
|
|
@@ -30,13 +30,11 @@ export const COMPONENT_NAMES = [
|
|
|
30
30
|
"LpDropzone",
|
|
31
31
|
"LpEmptyState",
|
|
32
32
|
"LpFileTree",
|
|
33
|
-
"LpFileTreeNode",
|
|
34
33
|
"LpFormField",
|
|
35
34
|
"LpIcon",
|
|
36
35
|
"LpImageEditor",
|
|
37
36
|
"LpInfraNode",
|
|
38
37
|
"LpInput",
|
|
39
|
-
"LpLaneNode",
|
|
40
38
|
"LpLightbox",
|
|
41
39
|
"LpLink",
|
|
42
40
|
"LpLogViewer",
|
|
@@ -65,7 +63,6 @@ export const COMPONENT_NAMES = [
|
|
|
65
63
|
"LpShift",
|
|
66
64
|
"LpShortcutsDialog",
|
|
67
65
|
"LpSidebar",
|
|
68
|
-
"LpSidebarNav",
|
|
69
66
|
"LpSkeleton",
|
|
70
67
|
"LpSlider",
|
|
71
68
|
"LpSparkline",
|
|
@@ -76,12 +73,13 @@ export const COMPONENT_NAMES = [
|
|
|
76
73
|
"LpTableOfContents",
|
|
77
74
|
"LpTabs",
|
|
78
75
|
"LpTextarea",
|
|
76
|
+
"LpThemeProvider",
|
|
79
77
|
"LpThemeSwitcher",
|
|
80
78
|
"LpTilt",
|
|
81
79
|
"LpTimeField",
|
|
82
80
|
"LpToaster",
|
|
83
81
|
"LpTooltip",
|
|
82
|
+
"LpTooltipProvider",
|
|
84
83
|
"LpTopologyCanvas",
|
|
85
|
-
"LpUptimeBar"
|
|
86
|
-
"LpWaypointEdge"
|
|
84
|
+
"LpUptimeBar"
|
|
87
85
|
] as const
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* The rows of an LpContextMenu panel, rendering itself for every submenu so
|
|
4
|
+
* `children` nests to any depth. Internal: not exported.
|
|
5
|
+
*/
|
|
6
|
+
import {
|
|
7
|
+
ContextMenuCheckboxItem,
|
|
8
|
+
ContextMenuItem,
|
|
9
|
+
ContextMenuPortal,
|
|
10
|
+
ContextMenuSeparator,
|
|
11
|
+
ContextMenuSub,
|
|
12
|
+
ContextMenuSubContent,
|
|
13
|
+
ContextMenuSubTrigger,
|
|
14
|
+
} from "reka-ui"
|
|
15
|
+
import type { ContextMenuItemDef } from "./LpContextMenu.vue"
|
|
16
|
+
import LpIcon from "./LpIcon.vue"
|
|
17
|
+
|
|
18
|
+
defineOptions({ name: "ContextMenuItems" })
|
|
19
|
+
|
|
20
|
+
defineProps<{
|
|
21
|
+
items: ContextMenuItemDef[]
|
|
22
|
+
panel: string
|
|
23
|
+
}>()
|
|
24
|
+
|
|
25
|
+
const ITEM =
|
|
26
|
+
"group/item flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-1.5 text-sm outline-none transition-[background-color,color] duration-[var(--duration-fast)] data-[disabled]:pointer-events-none data-[disabled]:opacity-45 data-[highlighted]:bg-brand-soft"
|
|
27
|
+
const ICON =
|
|
28
|
+
"transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-data-[highlighted]/item:translate-x-0.5"
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<template v-for="(item, i) in items" :key="i">
|
|
33
|
+
<ContextMenuSeparator v-if="item.separatorBefore" class="my-1 h-px bg-line" />
|
|
34
|
+
|
|
35
|
+
<ContextMenuSub v-if="item.children?.length">
|
|
36
|
+
<ContextMenuSubTrigger
|
|
37
|
+
:class="[ITEM, 'justify-between text-ink data-[highlighted]:text-brand data-[state=open]:bg-brand-soft data-[state=open]:text-brand']"
|
|
38
|
+
:disabled="item.disabled"
|
|
39
|
+
>
|
|
40
|
+
<span class="flex items-center gap-2">
|
|
41
|
+
<LpIcon v-if="item.icon" :name="item.icon" :size="15" :class="ICON" />
|
|
42
|
+
{{ item.label }}
|
|
43
|
+
</span>
|
|
44
|
+
<LpIcon name="lucide:chevron-right" :size="14" class="text-muted" />
|
|
45
|
+
</ContextMenuSubTrigger>
|
|
46
|
+
<ContextMenuPortal>
|
|
47
|
+
<ContextMenuSubContent :class="panel" :side-offset="2" :align-offset="-4">
|
|
48
|
+
<ContextMenuItems :items="item.children" :panel="panel" />
|
|
49
|
+
</ContextMenuSubContent>
|
|
50
|
+
</ContextMenuPortal>
|
|
51
|
+
</ContextMenuSub>
|
|
52
|
+
|
|
53
|
+
<!-- Tick-box item: a real checkbox item, so assistive tech hears its state.
|
|
54
|
+
`@select.prevent` keeps the menu open, since these come in lists where
|
|
55
|
+
several are set in one visit. The tick occupies a fixed slot so
|
|
56
|
+
unticked rows stay aligned with ticked ones. -->
|
|
57
|
+
<ContextMenuCheckboxItem
|
|
58
|
+
v-else-if="item.checked !== undefined"
|
|
59
|
+
:model-value="item.checked"
|
|
60
|
+
:class="[ITEM, 'justify-between text-ink data-[highlighted]:text-brand']"
|
|
61
|
+
:disabled="item.disabled"
|
|
62
|
+
@select.prevent="item.onSelect?.()"
|
|
63
|
+
>
|
|
64
|
+
<span class="flex items-center gap-2">
|
|
65
|
+
<LpIcon
|
|
66
|
+
:name="item.checked ? 'lucide:check' : 'lucide:minus'"
|
|
67
|
+
:size="15"
|
|
68
|
+
:class="[ICON, item.checked ? '' : 'opacity-25']"
|
|
69
|
+
/>
|
|
70
|
+
{{ item.label }}
|
|
71
|
+
</span>
|
|
72
|
+
<kbd v-if="item.shortcut" class="text-xs text-muted">{{ item.shortcut }}</kbd>
|
|
73
|
+
</ContextMenuCheckboxItem>
|
|
74
|
+
|
|
75
|
+
<ContextMenuItem
|
|
76
|
+
v-else
|
|
77
|
+
:class="[ITEM, 'justify-between', item.danger ? 'text-danger data-[highlighted]:bg-danger-soft data-[highlighted]:text-danger' : 'text-ink data-[highlighted]:text-brand']"
|
|
78
|
+
:disabled="item.disabled"
|
|
79
|
+
@select="item.onSelect?.()"
|
|
80
|
+
>
|
|
81
|
+
<span class="flex items-center gap-2">
|
|
82
|
+
<LpIcon v-if="item.icon" :name="item.icon" :size="15" :class="ICON" />
|
|
83
|
+
{{ item.label }}
|
|
84
|
+
</span>
|
|
85
|
+
<kbd v-if="item.shortcut" class="text-xs text-muted">{{ item.shortcut }}</kbd>
|
|
86
|
+
</ContextMenuItem>
|
|
87
|
+
</template>
|
|
88
|
+
</template>
|
|
@@ -22,7 +22,8 @@ import {
|
|
|
22
22
|
type Split,
|
|
23
23
|
} from "../layout/tree"
|
|
24
24
|
import type { BlockRegistry } from "../layout/registry"
|
|
25
|
-
import {
|
|
25
|
+
import { layoutController, type LayoutController } from "../layout/useLayout"
|
|
26
|
+
import { computed, ref } from "vue"
|
|
26
27
|
|
|
27
28
|
const props = defineProps<{
|
|
28
29
|
modelValue: Split
|
|
@@ -42,13 +43,30 @@ defineSlots<{
|
|
|
42
43
|
|
|
43
44
|
const dragId = ref<string | null>(null)
|
|
44
45
|
|
|
46
|
+
// A tree owned by useLayout() is edited through its controller, so every edit
|
|
47
|
+
// lands in undo history; a bare tree is mutated directly as before.
|
|
48
|
+
const edits = computed<LayoutController>(() => {
|
|
49
|
+
const tree = props.modelValue
|
|
50
|
+
return (
|
|
51
|
+
layoutController(tree) ?? {
|
|
52
|
+
move: (movingId, targetId, side) => moveLeaf(tree, movingId, targetId, side),
|
|
53
|
+
removeBlock: (leafId, index) => removeBlock(tree, leafId, index),
|
|
54
|
+
reorderTab: (leafId, from, to) => reorderTab(tree, leafId, from, to),
|
|
55
|
+
setActive: (leafId, index) => setActiveTab(tree, leafId, index),
|
|
56
|
+
resize: (parent, index, delta) => resizeAt(parent, index, delta),
|
|
57
|
+
beginResize: () => {},
|
|
58
|
+
endResize: () => {},
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
})
|
|
62
|
+
|
|
45
63
|
function onDrop(targetId: string, side: Side) {
|
|
46
|
-
if (dragId.value)
|
|
64
|
+
if (dragId.value) edits.value.move(dragId.value, targetId, side)
|
|
47
65
|
dragId.value = null
|
|
48
66
|
}
|
|
49
67
|
|
|
50
68
|
function onResize(parent: Split, index: number, delta: number) {
|
|
51
|
-
|
|
69
|
+
edits.value.resize(parent, index, delta)
|
|
52
70
|
}
|
|
53
71
|
</script>
|
|
54
72
|
|
|
@@ -63,10 +81,12 @@ function onResize(parent: Split, index: number, delta: number) {
|
|
|
63
81
|
@dragstart="(id) => (dragId = id)"
|
|
64
82
|
@dragend="dragId = null"
|
|
65
83
|
@drop="onDrop"
|
|
66
|
-
@set-active="(leafId, i) =>
|
|
67
|
-
@remove-block="(leafId, i) => removeBlock(
|
|
68
|
-
@reorder-tab="(leafId, from, to) => reorderTab(
|
|
84
|
+
@set-active="(leafId, i) => edits.setActive(leafId, i)"
|
|
85
|
+
@remove-block="(leafId, i) => edits.removeBlock(leafId, i)"
|
|
86
|
+
@reorder-tab="(leafId, from, to) => edits.reorderTab(leafId, from, to)"
|
|
69
87
|
@resize="onResize"
|
|
88
|
+
@resize-start="edits.beginResize()"
|
|
89
|
+
@resize-end="edits.endResize()"
|
|
70
90
|
>
|
|
71
91
|
<template #block="slotProps">
|
|
72
92
|
<slot name="block" v-bind="slotProps" />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { Motion } from "motion-v"
|
|
3
|
-
import { computed, ref } from "vue"
|
|
3
|
+
import { computed, onBeforeUnmount, ref } from "vue"
|
|
4
4
|
import type { LayoutNode, Leaf, Side, Split } from "../layout/tree"
|
|
5
5
|
import type { BlockRegistry } from "../layout/registry"
|
|
6
6
|
import { blockTitle } from "../layout/registry"
|
|
@@ -21,6 +21,8 @@ const emit = defineEmits<{
|
|
|
21
21
|
(e: "remove-block", leafId: string, index: number): void
|
|
22
22
|
(e: "reorder-tab", leafId: string, from: number, to: number): void
|
|
23
23
|
(e: "resize", parent: Split, index: number, deltaFraction: number): void
|
|
24
|
+
(e: "resize-start"): void
|
|
25
|
+
(e: "resize-end"): void
|
|
24
26
|
}>()
|
|
25
27
|
|
|
26
28
|
// Explicit slot shape so the recursive #block pass-through types cleanly.
|
|
@@ -97,10 +99,18 @@ function onDragOver(ev: DragEvent) {
|
|
|
97
99
|
else hoverSide.value = "center"
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
let stopSplitter: (() => void) | null = null
|
|
103
|
+
onBeforeUnmount(() => stopSplitter?.())
|
|
104
|
+
|
|
100
105
|
function startSplitter(ev: PointerEvent, parent: Split, index: number) {
|
|
101
106
|
ev.preventDefault()
|
|
102
|
-
const
|
|
107
|
+
const handle = ev.currentTarget as HTMLElement
|
|
108
|
+
const host = handle.parentElement
|
|
103
109
|
if (!host) return
|
|
110
|
+
// Capture keeps the moves coming when the pointer crosses an iframe or leaves
|
|
111
|
+
// the window; pointercancel (a touch the browser took over) ends it too.
|
|
112
|
+
handle.setPointerCapture?.(ev.pointerId)
|
|
113
|
+
emit("resize-start")
|
|
104
114
|
const rect = host.getBoundingClientRect()
|
|
105
115
|
const horizontal = parent.dir === "row"
|
|
106
116
|
const span = horizontal ? rect.width : rect.height
|
|
@@ -114,9 +124,14 @@ function startSplitter(ev: PointerEvent, parent: Split, index: number) {
|
|
|
114
124
|
const up = () => {
|
|
115
125
|
window.removeEventListener("pointermove", move)
|
|
116
126
|
window.removeEventListener("pointerup", up)
|
|
127
|
+
window.removeEventListener("pointercancel", up)
|
|
128
|
+
stopSplitter = null
|
|
129
|
+
emit("resize-end")
|
|
117
130
|
}
|
|
131
|
+
stopSplitter = up
|
|
118
132
|
window.addEventListener("pointermove", move)
|
|
119
133
|
window.addEventListener("pointerup", up)
|
|
134
|
+
window.addEventListener("pointercancel", up)
|
|
120
135
|
}
|
|
121
136
|
</script>
|
|
122
137
|
|
|
@@ -141,6 +156,8 @@ function startSplitter(ev: PointerEvent, parent: Split, index: number) {
|
|
|
141
156
|
@remove-block="(l, idx) => emit('remove-block', l, idx)"
|
|
142
157
|
@reorder-tab="(l, from, to) => emit('reorder-tab', l, from, to)"
|
|
143
158
|
@resize="(p, idx, d) => emit('resize', p, idx, d)"
|
|
159
|
+
@resize-start="emit('resize-start')"
|
|
160
|
+
@resize-end="emit('resize-end')"
|
|
144
161
|
>
|
|
145
162
|
<template #block="slotProps">
|
|
146
163
|
<slot name="block" v-bind="slotProps" />
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* name rather than `alertdialog` — it is a landmark you can Tab into, not a
|
|
21
21
|
* thing demanding an answer before anything else can happen.
|
|
22
22
|
*/
|
|
23
|
-
import { computed,
|
|
23
|
+
import { computed, onBeforeUnmount, ref, useSlots, watch } from "vue"
|
|
24
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
24
25
|
import { useBarStack } from "./actionBar"
|
|
25
26
|
import { CLOSE_ICON } from "./dropdown"
|
|
26
27
|
import LpButton from "./LpButton.vue"
|
|
@@ -85,6 +86,7 @@ const props = withDefaults(
|
|
|
85
86
|
maxWidth?: string
|
|
86
87
|
/** Accessible name for the landmark. Falls back to `title`. */
|
|
87
88
|
ariaLabel?: string
|
|
89
|
+
labels?: Partial<{ dismiss: string }>
|
|
88
90
|
}>(),
|
|
89
91
|
{
|
|
90
92
|
open: true,
|
|
@@ -100,6 +102,7 @@ const emit = defineEmits<{
|
|
|
100
102
|
}>()
|
|
101
103
|
|
|
102
104
|
const slots = useSlots()
|
|
105
|
+
const text = useLabels("actionBar", { dismiss: "Dismiss" }, () => props.labels)
|
|
103
106
|
|
|
104
107
|
// The last action is the primary one unless the caller has said otherwise; the
|
|
105
108
|
// rest default to `ghost` so a bar with three buttons still has one obvious
|
|
@@ -154,19 +157,39 @@ const stackOffset = computed(() => {
|
|
|
154
157
|
return offset()
|
|
155
158
|
})
|
|
156
159
|
|
|
160
|
+
/*
|
|
161
|
+
* The height is watched, not measured once: a description that wraps after a
|
|
162
|
+
* rotation, or content swapped while the bar is up, changes it, and the bars
|
|
163
|
+
* above would overlap this one.
|
|
164
|
+
*/
|
|
165
|
+
let observer: ResizeObserver | null = null
|
|
166
|
+
|
|
157
167
|
watch(
|
|
158
168
|
() => props.open,
|
|
159
|
-
|
|
169
|
+
(isOpen) => {
|
|
170
|
+
// Nothing on the server ever unregisters, so registering there would leak
|
|
171
|
+
// one slot per rendered request into every later page.
|
|
172
|
+
if (typeof window === "undefined") return
|
|
160
173
|
if (!isOpen) {
|
|
161
174
|
unregister()
|
|
162
175
|
return
|
|
163
176
|
}
|
|
164
177
|
register()
|
|
165
|
-
await nextTick()
|
|
166
|
-
if (root.value) setHeight(root.value.getBoundingClientRect().height)
|
|
167
178
|
},
|
|
168
179
|
{ immediate: true },
|
|
169
180
|
)
|
|
181
|
+
|
|
182
|
+
watch(root, (el) => {
|
|
183
|
+
observer?.disconnect()
|
|
184
|
+
observer = null
|
|
185
|
+
if (!el) return
|
|
186
|
+
setHeight(el.getBoundingClientRect().height)
|
|
187
|
+
if (typeof ResizeObserver === "undefined") return
|
|
188
|
+
observer = new ResizeObserver(() => setHeight(el.getBoundingClientRect().height))
|
|
189
|
+
observer.observe(el)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
onBeforeUnmount(() => observer?.disconnect())
|
|
170
193
|
</script>
|
|
171
194
|
|
|
172
195
|
<template>
|
|
@@ -194,9 +217,9 @@ watch(
|
|
|
194
217
|
ref="root"
|
|
195
218
|
role="region"
|
|
196
219
|
:aria-label="ariaLabel ?? title"
|
|
197
|
-
class="pointer-events-none fixed inset-x-0 bottom-0
|
|
220
|
+
class="pointer-events-none fixed inset-x-0 bottom-0 flex justify-center transition-[translate] duration-[var(--duration-medium)] ease-[var(--ease-emphasized)] motion-reduce:transition-none"
|
|
198
221
|
:class="width === 'full' ? '' : 'p-3 sm:p-4'"
|
|
199
|
-
:style="{ translate: `0 -${stackOffset}px
|
|
222
|
+
:style="{ translate: `0 -${stackOffset}px`, zIndex: 'calc(var(--z-overlay) - 1)' }"
|
|
200
223
|
>
|
|
201
224
|
<div
|
|
202
225
|
class="lp-skin-panel pointer-events-auto flex items-center gap-3 border-solid bg-surface-overlay"
|
|
@@ -261,7 +284,7 @@ watch(
|
|
|
261
284
|
<button
|
|
262
285
|
v-if="dismissible"
|
|
263
286
|
type="button"
|
|
264
|
-
aria-label="
|
|
287
|
+
:aria-label="text.dismiss"
|
|
265
288
|
class="group -mr-1 flex size-8 shrink-0 items-center justify-center rounded-control text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
|
|
266
289
|
@click="dismiss"
|
|
267
290
|
>
|
|
@@ -19,6 +19,7 @@ export default { inheritAttrs: false }
|
|
|
19
19
|
* of every form that never shows a map.
|
|
20
20
|
*/
|
|
21
21
|
import { computed, defineAsyncComponent, onMounted, ref, watch } from "vue"
|
|
22
|
+
import { useAsyncTask } from "../composables/useAsyncTask"
|
|
22
23
|
import {
|
|
23
24
|
type AddressField,
|
|
24
25
|
type AddressLabels,
|
|
@@ -113,11 +114,32 @@ const geocoder = computed(() => props.provider ?? fallbackProvider)
|
|
|
113
114
|
|
|
114
115
|
const value = computed<AddressValue>(() => props.modelValue ?? emptyAddress())
|
|
115
116
|
|
|
117
|
+
// Patches build on the last value emitted, not on the prop: the prop only
|
|
118
|
+
// catches up on the parent's next render, and two patches in one tick (the
|
|
119
|
+
// text a suggestion fills in, then the parts it resolved) would otherwise each
|
|
120
|
+
// start from the stale value and the second would undo the first.
|
|
121
|
+
let latest: AddressValue | undefined
|
|
122
|
+
watch(
|
|
123
|
+
() => props.modelValue,
|
|
124
|
+
() => (latest = undefined),
|
|
125
|
+
)
|
|
116
126
|
function patch(changes: Partial<AddressValue>) {
|
|
117
|
-
|
|
127
|
+
latest = { ...(latest ?? value.value), ...changes }
|
|
128
|
+
emit("update:modelValue", latest)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// A part typed by hand describes a different place than the point a suggestion
|
|
132
|
+
// or the map resolved, so the coordinates go with it: kept, they would pin the
|
|
133
|
+
// stored address to somewhere else.
|
|
134
|
+
function edit(changes: Partial<AddressValue>) {
|
|
135
|
+
patch({ ...changes, lat: undefined, lon: undefined })
|
|
118
136
|
}
|
|
119
137
|
|
|
120
|
-
const
|
|
138
|
+
const allCountries = ref<Country[]>([])
|
|
139
|
+
const countries = computed(() => {
|
|
140
|
+
const only = props.only?.map((code) => code.toUpperCase())
|
|
141
|
+
return only?.length ? allCountries.value.filter((c) => only.includes(c.iso2)) : allCountries.value
|
|
142
|
+
})
|
|
121
143
|
const countryOptions = computed(() =>
|
|
122
144
|
countries.value.map((country) => ({
|
|
123
145
|
value: country.iso2,
|
|
@@ -126,9 +148,7 @@ const countryOptions = computed(() =>
|
|
|
126
148
|
)
|
|
127
149
|
|
|
128
150
|
onMounted(async () => {
|
|
129
|
-
|
|
130
|
-
const only = props.only?.map((code) => code.toUpperCase())
|
|
131
|
-
countries.value = only?.length ? list.filter((c) => only.includes(c.iso2)) : list
|
|
151
|
+
allCountries.value = await loadCountries()
|
|
132
152
|
})
|
|
133
153
|
|
|
134
154
|
/*
|
|
@@ -138,37 +158,31 @@ onMounted(async () => {
|
|
|
138
158
|
*/
|
|
139
159
|
function useSuggestions(field: AddressField) {
|
|
140
160
|
const options = ref<AddressSuggestion[]>([])
|
|
141
|
-
const
|
|
142
|
-
let timer: ReturnType<typeof setTimeout> | undefined
|
|
143
|
-
let controller: AbortController | undefined
|
|
161
|
+
const task = useAsyncTask<AddressSuggestion[]>()
|
|
144
162
|
|
|
145
163
|
function query(term: string) {
|
|
146
|
-
if (timer) clearTimeout(timer)
|
|
147
|
-
controller?.abort()
|
|
148
164
|
if (term.trim().length < props.minChars) {
|
|
165
|
+
task.cancel()
|
|
149
166
|
options.value = []
|
|
150
|
-
loading.value = false
|
|
151
167
|
return
|
|
152
168
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
options.value = await geocoder.value.search(term.trim(), field, {
|
|
169
|
+
task.run(
|
|
170
|
+
(signal) =>
|
|
171
|
+
geocoder.value.search(term.trim(), field, {
|
|
158
172
|
country: value.value.country || undefined,
|
|
159
173
|
language: props.language,
|
|
160
|
-
signal
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
options.value =
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
174
|
+
signal,
|
|
175
|
+
}),
|
|
176
|
+
{
|
|
177
|
+
delay: props.debounce,
|
|
178
|
+
apply: (found) => (options.value = found),
|
|
179
|
+
// Network failure: leave the field usable as free text.
|
|
180
|
+
fail: () => (options.value = []),
|
|
181
|
+
},
|
|
182
|
+
)
|
|
169
183
|
}
|
|
170
184
|
|
|
171
|
-
return { options, loading, query }
|
|
185
|
+
return { options, loading: task.pending, query }
|
|
172
186
|
}
|
|
173
187
|
|
|
174
188
|
const lineSuggestions = useSuggestions("line")
|
|
@@ -230,19 +244,21 @@ watch(mapOpen, (open) => {
|
|
|
230
244
|
resolved.value = undefined
|
|
231
245
|
})
|
|
232
246
|
|
|
233
|
-
|
|
247
|
+
// Pins dropped in quick succession each start a lookup; only the latest one's
|
|
248
|
+
// answer may describe the pin on screen.
|
|
249
|
+
const reverse = useAsyncTask<AddressSuggestion | undefined>()
|
|
250
|
+
watch(reverse.pending, (busy) => (resolving.value = busy))
|
|
251
|
+
|
|
252
|
+
function onPick(next: LatLon) {
|
|
234
253
|
point.value = next
|
|
235
|
-
resolving.value = true
|
|
236
254
|
resolved.value = undefined
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
resolving.value = false
|
|
245
|
-
}
|
|
255
|
+
reverse.run(
|
|
256
|
+
() => geocoder.value.reverse(next.lat, next.lon, { language: props.language }),
|
|
257
|
+
{
|
|
258
|
+
apply: (found) => (resolved.value = found),
|
|
259
|
+
fail: () => (resolved.value = undefined),
|
|
260
|
+
},
|
|
261
|
+
)
|
|
246
262
|
}
|
|
247
263
|
|
|
248
264
|
/** Confirm the pin: fill in whatever came back and always keep the point. */
|
|
@@ -288,7 +304,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
288
304
|
:empty-text="text.noSuggestions"
|
|
289
305
|
:loading-text="text.searching"
|
|
290
306
|
icon="lucide:map-pin"
|
|
291
|
-
@update:model-value="(next: string) => {
|
|
307
|
+
@update:model-value="(next: string) => { edit({ line: next }); lineSuggestions.query(next) }"
|
|
292
308
|
@select="onLineSelect"
|
|
293
309
|
/>
|
|
294
310
|
<LpButton
|
|
@@ -330,7 +346,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
330
346
|
:placeholder="text.cityPlaceholder"
|
|
331
347
|
:empty-text="text.noSuggestions"
|
|
332
348
|
:loading-text="text.searching"
|
|
333
|
-
@update:model-value="(next: string) => {
|
|
349
|
+
@update:model-value="(next: string) => { edit({ city: next }); citySuggestions.query(next) }"
|
|
334
350
|
@select="onCitySelect"
|
|
335
351
|
/>
|
|
336
352
|
</LpFormField>
|
|
@@ -342,7 +358,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
342
358
|
:disabled="disabled"
|
|
343
359
|
:size="size"
|
|
344
360
|
autocomplete="postal-code"
|
|
345
|
-
@update:model-value="(next: string) =>
|
|
361
|
+
@update:model-value="(next: string) => edit({ postalCode: next })"
|
|
346
362
|
/>
|
|
347
363
|
</div>
|
|
348
364
|
|
|
@@ -355,7 +371,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
355
371
|
:disabled="disabled"
|
|
356
372
|
:size="size"
|
|
357
373
|
searchable
|
|
358
|
-
@update:model-value="(next) =>
|
|
374
|
+
@update:model-value="(next) => edit({ country: String(next ?? '') })"
|
|
359
375
|
/>
|
|
360
376
|
</LpFormField>
|
|
361
377
|
|