@leavepulse/ui 0.43.4 → 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-B-0Est4Z.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 -3
- package/dist/components/LpCommandPalette.vue.js +86 -72
- 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/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 +32 -2
- package/dist/index.js +184 -137
- 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 +34 -13
- 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/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 +37 -2
- 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
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { provide, useId, computed, inject } from "vue";
|
|
2
|
+
const LP_FIELD = /* @__PURE__ */ Symbol.for("lp-field");
|
|
3
|
+
function provideField(context) {
|
|
4
|
+
provide(LP_FIELD, context);
|
|
5
|
+
}
|
|
6
|
+
function useField() {
|
|
7
|
+
return inject(LP_FIELD, null);
|
|
8
|
+
}
|
|
9
|
+
function fieldMessageId(id) {
|
|
10
|
+
return `${id}-message`;
|
|
11
|
+
}
|
|
12
|
+
function useFieldControl(own) {
|
|
13
|
+
const outer = useField();
|
|
14
|
+
const generated = useId();
|
|
15
|
+
return computed(() => {
|
|
16
|
+
const state = own();
|
|
17
|
+
if (state.wrapped) {
|
|
18
|
+
const id = state.id ?? generated;
|
|
19
|
+
return {
|
|
20
|
+
id,
|
|
21
|
+
labelId: void 0,
|
|
22
|
+
describedBy: state.hasMessage ? fieldMessageId(id) : void 0,
|
|
23
|
+
invalid: !!state.invalid,
|
|
24
|
+
required: !!state.required,
|
|
25
|
+
disabled: !!state.disabled
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const field = outer?.value;
|
|
29
|
+
return {
|
|
30
|
+
id: state.id ?? field?.id,
|
|
31
|
+
labelId: field?.labelId,
|
|
32
|
+
describedBy: field?.describedBy,
|
|
33
|
+
invalid: !!state.invalid || !!field?.invalid,
|
|
34
|
+
required: !!state.required || !!field?.required,
|
|
35
|
+
disabled: !!state.disabled || !!field?.disabled
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
LP_FIELD,
|
|
41
|
+
fieldMessageId,
|
|
42
|
+
provideField,
|
|
43
|
+
useField,
|
|
44
|
+
useFieldControl
|
|
45
|
+
};
|
|
@@ -24,9 +24,16 @@ export interface UseFormDraftOptions<T> {
|
|
|
24
24
|
disabled?: MaybeRefOrGetter<boolean>;
|
|
25
25
|
}
|
|
26
26
|
export interface UseFormDraft<T> {
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* A draft was found for this key, and has not been restored or discarded.
|
|
29
|
+
* Only a draft found on reading counts: the writes this composable makes as
|
|
30
|
+
* the user types never raise it. While it is up, automatic writes are held
|
|
31
|
+
* back, so the draft on offer cannot be overwritten by the form's own state
|
|
32
|
+
* (an async load of the record, or typing past the banner) before the user
|
|
33
|
+
* has answered.
|
|
34
|
+
*/
|
|
28
35
|
hasDraft: Ref<boolean>;
|
|
29
|
-
/** When
|
|
36
|
+
/** When the found draft was written, ISO-8601, or `null` when there is none. */
|
|
30
37
|
savedAt: Ref<string | null>;
|
|
31
38
|
/**
|
|
32
39
|
* The stored value, or `null`. Reading does not apply it — the caller decides
|
|
@@ -35,7 +42,7 @@ export interface UseFormDraft<T> {
|
|
|
35
42
|
read: () => T | null;
|
|
36
43
|
/** Forget the draft. Call after applying it, and after a successful save. */
|
|
37
44
|
discard: () => void;
|
|
38
|
-
/** Write now, ignoring `dirty
|
|
45
|
+
/** Write now, ignoring `dirty` and a pending draft. For "save and come back to this later". */
|
|
39
46
|
save: () => void;
|
|
40
47
|
}
|
|
41
48
|
/**
|
|
@@ -16,6 +16,11 @@ function useFormDraft(options) {
|
|
|
16
16
|
function currentKey() {
|
|
17
17
|
return String(toValue(options.key) || "").trim();
|
|
18
18
|
}
|
|
19
|
+
function fresh(parsed) {
|
|
20
|
+
if (!parsed || parsed.version !== ENVELOPE_VERSION) return false;
|
|
21
|
+
if (maxAge <= 0) return true;
|
|
22
|
+
return !(Date.now() - Date.parse(parsed.savedAt) > maxAge);
|
|
23
|
+
}
|
|
19
24
|
function read() {
|
|
20
25
|
const store = storage();
|
|
21
26
|
const key = currentKey();
|
|
@@ -29,17 +34,10 @@ function useFormDraft(options) {
|
|
|
29
34
|
if (!raw) return null;
|
|
30
35
|
try {
|
|
31
36
|
const parsed = JSON.parse(raw);
|
|
32
|
-
if (!parsed
|
|
37
|
+
if (!fresh(parsed)) {
|
|
33
38
|
discard();
|
|
34
39
|
return null;
|
|
35
40
|
}
|
|
36
|
-
if (maxAge > 0) {
|
|
37
|
-
const age = Date.now() - Date.parse(parsed.savedAt);
|
|
38
|
-
if (age > maxAge) {
|
|
39
|
-
discard();
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
41
|
return parsed.value;
|
|
44
42
|
} catch {
|
|
45
43
|
discard();
|
|
@@ -50,21 +48,18 @@ function useFormDraft(options) {
|
|
|
50
48
|
const store = storage();
|
|
51
49
|
const key = currentKey();
|
|
52
50
|
if (!store || !key) return;
|
|
53
|
-
const stamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
54
51
|
try {
|
|
55
52
|
store.setItem(
|
|
56
53
|
key,
|
|
57
54
|
JSON.stringify({
|
|
58
55
|
version: ENVELOPE_VERSION,
|
|
59
|
-
savedAt:
|
|
56
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
60
57
|
value
|
|
61
58
|
})
|
|
62
59
|
);
|
|
63
60
|
} catch {
|
|
64
61
|
return;
|
|
65
62
|
}
|
|
66
|
-
hasDraft.value = true;
|
|
67
|
-
savedAt.value = stamp;
|
|
68
63
|
}
|
|
69
64
|
function discard() {
|
|
70
65
|
const store = storage();
|
|
@@ -97,7 +92,7 @@ function useFormDraft(options) {
|
|
|
97
92
|
if (!raw) return;
|
|
98
93
|
try {
|
|
99
94
|
const parsed = JSON.parse(raw);
|
|
100
|
-
if (parsed
|
|
95
|
+
if (!fresh(parsed)) return;
|
|
101
96
|
hasDraft.value = true;
|
|
102
97
|
savedAt.value = parsed.savedAt;
|
|
103
98
|
} catch {
|
|
@@ -108,7 +103,7 @@ function useFormDraft(options) {
|
|
|
108
103
|
watch(
|
|
109
104
|
() => [toValue(options.value), toValue(options.dirty), toValue(options.disabled)],
|
|
110
105
|
([value, dirty, disabled]) => {
|
|
111
|
-
if (disabled) return;
|
|
106
|
+
if (disabled || hasDraft.value) return;
|
|
112
107
|
if (options.dirty !== void 0 && !dirty) return;
|
|
113
108
|
write(value);
|
|
114
109
|
},
|
|
@@ -16,6 +16,16 @@ export interface HotkeyBinding {
|
|
|
16
16
|
* something there — Escape, and the palette the user reaches from anywhere.
|
|
17
17
|
*/
|
|
18
18
|
allowInInput?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Runs even while an overlay owns the keyboard. By default a binding declared
|
|
21
|
+
* outside the topmost modal stays quiet while that modal is open, and an
|
|
22
|
+
* Escape binding declared outside the topmost dismissable layer (dialog,
|
|
23
|
+
* popover, menu) leaves Escape to that layer, so a page-level shortcut can
|
|
24
|
+
* neither act behind a dialog nor stop the dialog from closing.
|
|
25
|
+
*/
|
|
26
|
+
overLayers?: boolean;
|
|
27
|
+
/** Set `false` to ignore auto-repeat while the key is held. */
|
|
28
|
+
repeat?: boolean;
|
|
19
29
|
/**
|
|
20
30
|
* How the key reference lists this binding while it is mounted. Bindings
|
|
21
31
|
* without it still work, they just stay out of `activeShortcuts`.
|
|
@@ -57,11 +67,17 @@ export interface ShortcutGroup {
|
|
|
57
67
|
* used to pick a label.
|
|
58
68
|
*/
|
|
59
69
|
export declare const isAppleKeyboard: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* `isAppleKeyboard` for render paths: false until mounted, so the server and
|
|
72
|
+
* the hydrating client print the same label and the client switches after.
|
|
73
|
+
*/
|
|
74
|
+
export declare function useAppleKeyboard(): import("vue").Ref<boolean, boolean>;
|
|
60
75
|
/**
|
|
61
76
|
* The symbol to print for a modifier on this platform. Bindings treat Ctrl and
|
|
62
77
|
* Meta as one modifier, so only the label may differ — never the binding.
|
|
63
78
|
*/
|
|
64
|
-
export declare function appleModifierLabel(key: string): string;
|
|
79
|
+
export declare function appleModifierLabel(key: string, apple?: boolean): string;
|
|
80
|
+
export declare function matchesHotkey(event: KeyboardEvent, binding: HotkeyBinding): boolean;
|
|
65
81
|
/**
|
|
66
82
|
* Bind a list of hotkeys for as long as the calling component is mounted.
|
|
67
83
|
*
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import { shallowReactive, computed, toValue, onMounted, onBeforeUnmount } from "vue";
|
|
1
|
+
import { shallowReactive, computed, toValue, getCurrentInstance, onMounted, onBeforeUnmount, ref } from "vue";
|
|
2
|
+
import { topModalElement } from "../components/modalDepth.js";
|
|
2
3
|
const isAppleKeyboard = typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/.test(navigator.platform || "");
|
|
3
|
-
function
|
|
4
|
-
|
|
4
|
+
function useAppleKeyboard() {
|
|
5
|
+
const apple = ref(false);
|
|
6
|
+
onMounted(() => apple.value = isAppleKeyboard);
|
|
7
|
+
return apple;
|
|
8
|
+
}
|
|
9
|
+
function appleModifierLabel(key, apple = isAppleKeyboard) {
|
|
10
|
+
if (!apple) return key;
|
|
5
11
|
if (key === "Ctrl") return "⌘";
|
|
6
12
|
if (key === "Alt") return "⌥";
|
|
7
13
|
return key;
|
|
@@ -24,33 +30,57 @@ function isTextEntry(target) {
|
|
|
24
30
|
}
|
|
25
31
|
return false;
|
|
26
32
|
}
|
|
27
|
-
|
|
33
|
+
const MODIFIER_KEYS = /* @__PURE__ */ new Set(["control", "meta", "shift", "alt"]);
|
|
34
|
+
function matchesHotkey(event, binding) {
|
|
35
|
+
const key = binding.key.toLowerCase();
|
|
28
36
|
const ctrlHeld = event.ctrlKey || event.metaKey;
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
const wantsCtrl = Boolean(binding.ctrl) || key === "control" || key === "meta";
|
|
38
|
+
if (ctrlHeld !== wantsCtrl) return false;
|
|
39
|
+
const symbol = key.length === 1 && key.toLowerCase() === key.toUpperCase();
|
|
40
|
+
const shiftFree = key === "shift" || binding.shift === void 0 && symbol;
|
|
41
|
+
if (!shiftFree && event.shiftKey !== Boolean(binding.shift)) return false;
|
|
42
|
+
if (key !== "alt" && event.altKey !== Boolean(binding.alt)) return false;
|
|
43
|
+
if (MODIFIER_KEYS.has(key)) return event.key.toLowerCase() === key;
|
|
44
|
+
return event.key.toLowerCase() === key || binding.code !== void 0 && event.code === binding.code;
|
|
33
45
|
}
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
const STATE_KEY = /* @__PURE__ */ Symbol.for("@leavepulse/ui:hotkeys");
|
|
47
|
+
const g = globalThis;
|
|
48
|
+
const state = g[STATE_KEY] ??= {
|
|
49
|
+
mounted: shallowReactive(/* @__PURE__ */ new Set()),
|
|
50
|
+
order: 0,
|
|
51
|
+
listening: false
|
|
52
|
+
};
|
|
53
|
+
const mounted = state.mounted;
|
|
37
54
|
function inScope(target, scope) {
|
|
38
55
|
const el = toValue(scope);
|
|
39
56
|
if (!el) return true;
|
|
40
57
|
const node = target instanceof Node ? target : null;
|
|
41
58
|
return !!node && (el === node || el.contains(node));
|
|
42
59
|
}
|
|
60
|
+
function topLayer() {
|
|
61
|
+
const layers = document.querySelectorAll("[data-dismissable-layer]");
|
|
62
|
+
return layers[layers.length - 1] ?? null;
|
|
63
|
+
}
|
|
64
|
+
function insideOf(entry, layer) {
|
|
65
|
+
const scope = toValue(entry.scope);
|
|
66
|
+
const node = scope ?? entry.host();
|
|
67
|
+
return !!node && layer.contains(node);
|
|
68
|
+
}
|
|
43
69
|
function onKeydown(event) {
|
|
44
|
-
if (event.isComposing) return;
|
|
70
|
+
if (event.isComposing || event.defaultPrevented) return;
|
|
45
71
|
const inTextEntry = isTextEntry(event.target);
|
|
72
|
+
const blocker = event.key === "Escape" ? topLayer() : topModalElement();
|
|
46
73
|
const entries = [...mounted].sort(
|
|
47
74
|
(a, b) => Number(toValue(b.priority) || 0) - Number(toValue(a.priority) || 0) || b.order - a.order
|
|
48
75
|
);
|
|
49
76
|
for (const entry of entries) {
|
|
50
77
|
if (toValue(entry.enabled) === false || !inScope(event.target, entry.scope)) continue;
|
|
78
|
+
const covered = !!blocker && !insideOf(entry, blocker);
|
|
51
79
|
for (const binding of entry.read()) {
|
|
52
80
|
if (inTextEntry && !binding.allowInInput) continue;
|
|
53
|
-
if (
|
|
81
|
+
if (event.repeat && binding.repeat === false) continue;
|
|
82
|
+
if (covered && !binding.overLayers) continue;
|
|
83
|
+
if (!matchesHotkey(event, binding)) continue;
|
|
54
84
|
if (binding.handler(event) === false) continue;
|
|
55
85
|
event.preventDefault();
|
|
56
86
|
return;
|
|
@@ -58,25 +88,30 @@ function onKeydown(event) {
|
|
|
58
88
|
}
|
|
59
89
|
}
|
|
60
90
|
function useHotkeys(bindings, options = {}) {
|
|
91
|
+
const instance = getCurrentInstance();
|
|
61
92
|
const entry = {
|
|
62
93
|
read: () => toValue(bindings),
|
|
63
94
|
scope: options.scope,
|
|
64
95
|
enabled: options.enabled,
|
|
65
96
|
priority: options.priority,
|
|
66
|
-
order: ++
|
|
97
|
+
order: ++state.order,
|
|
98
|
+
host: () => {
|
|
99
|
+
const el = instance?.vnode.el;
|
|
100
|
+
return el instanceof Node ? el : null;
|
|
101
|
+
}
|
|
67
102
|
};
|
|
68
103
|
onMounted(() => {
|
|
69
104
|
mounted.add(entry);
|
|
70
|
-
if (!listening) {
|
|
105
|
+
if (!state.listening) {
|
|
71
106
|
window.addEventListener("keydown", onKeydown);
|
|
72
|
-
listening = true;
|
|
107
|
+
state.listening = true;
|
|
73
108
|
}
|
|
74
109
|
});
|
|
75
110
|
onBeforeUnmount(() => {
|
|
76
111
|
mounted.delete(entry);
|
|
77
|
-
if (!mounted.size && listening) {
|
|
112
|
+
if (!mounted.size && state.listening) {
|
|
78
113
|
window.removeEventListener("keydown", onKeydown);
|
|
79
|
-
listening = false;
|
|
114
|
+
state.listening = false;
|
|
80
115
|
}
|
|
81
116
|
});
|
|
82
117
|
}
|
|
@@ -128,6 +163,8 @@ export {
|
|
|
128
163
|
activeShortcuts,
|
|
129
164
|
appleModifierLabel,
|
|
130
165
|
isAppleKeyboard,
|
|
166
|
+
matchesHotkey,
|
|
131
167
|
mergeShortcutGroups,
|
|
168
|
+
useAppleKeyboard,
|
|
132
169
|
useHotkeys
|
|
133
170
|
};
|
|
@@ -5,9 +5,26 @@ export interface UseInputFilterOptions {
|
|
|
5
5
|
/** RegExp/string the full value must match to be valid. Validation only. */
|
|
6
6
|
pattern?: MaybeRefOrGetter<RegExp | string | undefined>;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Anchor a pattern to the whole string, like HTML's `pattern` attribute. The
|
|
10
|
+
* source is wrapped, never edited: anchors the caller wrote stay valid inside
|
|
11
|
+
* the group, while trimming a trailing `$` broke an escaped `\$` into an
|
|
12
|
+
* unterminated group.
|
|
13
|
+
*/
|
|
14
|
+
export declare function anchorPattern(p: RegExp | string): RegExp;
|
|
15
|
+
/**
|
|
16
|
+
* `text` with every disallowed character removed, and where `caret` lands once
|
|
17
|
+
* the characters before it that were removed are gone.
|
|
18
|
+
*/
|
|
19
|
+
export declare function stripDisallowed(text: string, charRe: RegExp, caret?: number): {
|
|
20
|
+
text: string;
|
|
21
|
+
caret: number;
|
|
22
|
+
};
|
|
8
23
|
export declare function useInputFilter(options?: UseInputFilterOptions): {
|
|
9
24
|
isInvalid: (value: string | undefined) => boolean;
|
|
10
25
|
onBeforeInput: (event: InputEvent) => void;
|
|
26
|
+
onInput: (event: Event) => void;
|
|
27
|
+
onCompositionEnd: (event: CompositionEvent) => void;
|
|
11
28
|
onPaste: (event: ClipboardEvent) => void;
|
|
12
29
|
patternRe: import("vue").ComputedRef<RegExp | undefined>;
|
|
13
30
|
restrict: import("vue").ComputedRef<RegExp | undefined>;
|
|
@@ -1,23 +1,38 @@
|
|
|
1
1
|
import { computed, toValue } from "vue";
|
|
2
|
-
function
|
|
3
|
-
if (p instanceof RegExp) {
|
|
4
|
-
const src = p.source.replace(/^\^/, "").replace(/\$$/, "");
|
|
5
|
-
return new RegExp(`^(?:${src})$`, p.flags.replace(/[gy]/g, ""));
|
|
6
|
-
}
|
|
2
|
+
function anchorPattern(p) {
|
|
3
|
+
if (p instanceof RegExp) return new RegExp(`^(?:${p.source})$`, p.flags.replace(/[gy]/g, ""));
|
|
7
4
|
return new RegExp(`^(?:${p})$`);
|
|
8
5
|
}
|
|
9
|
-
function
|
|
6
|
+
function charTester(charRe) {
|
|
10
7
|
const re = new RegExp(charRe.source, charRe.flags.replace(/[gy]/g, ""));
|
|
8
|
+
return (ch) => re.test(ch);
|
|
9
|
+
}
|
|
10
|
+
function allAllowed(text, charRe) {
|
|
11
|
+
const ok = charTester(charRe);
|
|
11
12
|
for (const ch of text) {
|
|
12
|
-
if (!
|
|
13
|
+
if (!ok(ch)) return false;
|
|
13
14
|
}
|
|
14
15
|
return true;
|
|
15
16
|
}
|
|
17
|
+
function stripDisallowed(text, charRe, caret = text.length) {
|
|
18
|
+
const ok = charTester(charRe);
|
|
19
|
+
let kept = "";
|
|
20
|
+
let at = 0;
|
|
21
|
+
let offset = 0;
|
|
22
|
+
for (const ch of text) {
|
|
23
|
+
if (ok(ch)) {
|
|
24
|
+
kept += ch;
|
|
25
|
+
if (offset < caret) at += ch.length;
|
|
26
|
+
}
|
|
27
|
+
offset += ch.length;
|
|
28
|
+
}
|
|
29
|
+
return { text: kept, caret: at };
|
|
30
|
+
}
|
|
16
31
|
function useInputFilter(options = {}) {
|
|
17
32
|
const restrict = computed(() => toValue(options.restrict));
|
|
18
33
|
const patternRe = computed(() => {
|
|
19
34
|
const p = toValue(options.pattern);
|
|
20
|
-
return p === void 0 ? void 0 :
|
|
35
|
+
return p === void 0 ? void 0 : anchorPattern(p);
|
|
21
36
|
});
|
|
22
37
|
function isInvalid(value) {
|
|
23
38
|
const re = patternRe.value;
|
|
@@ -27,18 +42,40 @@ function useInputFilter(options = {}) {
|
|
|
27
42
|
function onBeforeInput(event) {
|
|
28
43
|
const re = restrict.value;
|
|
29
44
|
if (!re || event.isComposing) return;
|
|
30
|
-
const data = event.data;
|
|
45
|
+
const data = event.data ?? event.dataTransfer?.getData("text") ?? null;
|
|
31
46
|
if (data == null) return;
|
|
32
47
|
if (!allAllowed(data, re)) event.preventDefault();
|
|
33
48
|
}
|
|
49
|
+
function sanitize(target) {
|
|
50
|
+
const re = restrict.value;
|
|
51
|
+
if (!re) return false;
|
|
52
|
+
if (!(target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement)) return false;
|
|
53
|
+
const { text, caret } = stripDisallowed(target.value, re, target.selectionStart ?? target.value.length);
|
|
54
|
+
if (text === target.value) return false;
|
|
55
|
+
target.value = text;
|
|
56
|
+
try {
|
|
57
|
+
target.setSelectionRange(caret, caret);
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
function onInput(event) {
|
|
63
|
+
if (event.isComposing) return;
|
|
64
|
+
sanitize(event.target);
|
|
65
|
+
}
|
|
66
|
+
function onCompositionEnd(event) {
|
|
67
|
+
if (sanitize(event.target)) event.target?.dispatchEvent(new Event("input", { bubbles: true }));
|
|
68
|
+
}
|
|
34
69
|
function onPaste(event) {
|
|
35
70
|
const re = restrict.value;
|
|
36
71
|
if (!re) return;
|
|
37
72
|
const text = event.clipboardData?.getData("text") ?? "";
|
|
38
73
|
if (text && !allAllowed(text, re)) event.preventDefault();
|
|
39
74
|
}
|
|
40
|
-
return { isInvalid, onBeforeInput, onPaste, patternRe, restrict };
|
|
75
|
+
return { isInvalid, onBeforeInput, onInput, onCompositionEnd, onPaste, patternRe, restrict };
|
|
41
76
|
}
|
|
42
77
|
export {
|
|
78
|
+
anchorPattern,
|
|
79
|
+
stripDisallowed,
|
|
43
80
|
useInputFilter
|
|
44
81
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type App, type Component, type ComputedRef, type InjectionKey, type MaybeRefOrGetter } from "vue";
|
|
2
|
+
export type LabelScope = Record<string, string>;
|
|
3
|
+
export interface LpConfig {
|
|
4
|
+
/** Component rendered for internal links (RouterLink, NuxtLink). Plain `<a>` when unset. */
|
|
5
|
+
linkComponent?: Component | string;
|
|
6
|
+
/** Text overrides by scope: `{ lightbox: { previous: "Back" } }`. */
|
|
7
|
+
labels?: Partial<Record<string, Partial<LabelScope>>>;
|
|
8
|
+
}
|
|
9
|
+
export declare const LP_CONFIG: InjectionKey<MaybeRefOrGetter<LpConfig>>;
|
|
10
|
+
/** Provide kit configuration to every component below the caller. */
|
|
11
|
+
export declare function provideLpConfig(config: MaybeRefOrGetter<LpConfig>): void;
|
|
12
|
+
/** Install the configuration app-wide: `app.use(lpConfig({ ... }))`. */
|
|
13
|
+
export declare function lpConfig(config: MaybeRefOrGetter<LpConfig>): {
|
|
14
|
+
install: (app: App) => void;
|
|
15
|
+
};
|
|
16
|
+
export declare function mergeConfig(outer: LpConfig, inner: LpConfig): LpConfig;
|
|
17
|
+
export declare function useLpConfig(): ComputedRef<LpConfig>;
|
|
18
|
+
/**
|
|
19
|
+
* The labels a component renders: its own `labels` prop over the configured
|
|
20
|
+
* scope over its defaults.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useLabels<T extends LabelScope>(scope: string, defaults: T, own?: () => Partial<T> | undefined): ComputedRef<T>;
|
|
23
|
+
/** The link component to render for an internal destination. */
|
|
24
|
+
export declare function useLinkComponent(): ComputedRef<Component | string>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { computed, inject, provide, toValue } from "vue";
|
|
2
|
+
const LP_CONFIG = /* @__PURE__ */ Symbol.for("lp-config");
|
|
3
|
+
function provideLpConfig(config) {
|
|
4
|
+
provide(LP_CONFIG, layered(inject(LP_CONFIG, {}), config));
|
|
5
|
+
}
|
|
6
|
+
function lpConfig(config) {
|
|
7
|
+
return {
|
|
8
|
+
install: (app) => {
|
|
9
|
+
const outer = app.runWithContext(() => inject(LP_CONFIG, {}));
|
|
10
|
+
app.provide(LP_CONFIG, layered(outer, config));
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function layered(outer, inner) {
|
|
15
|
+
return () => mergeConfig(toValue(outer), toValue(inner));
|
|
16
|
+
}
|
|
17
|
+
function mergeConfig(outer, inner) {
|
|
18
|
+
const labels = { ...outer.labels };
|
|
19
|
+
for (const [scope, values] of Object.entries(inner.labels ?? {})) {
|
|
20
|
+
labels[scope] = { ...labels[scope], ...values };
|
|
21
|
+
}
|
|
22
|
+
return { ...outer, ...inner, linkComponent: inner.linkComponent ?? outer.linkComponent, labels };
|
|
23
|
+
}
|
|
24
|
+
function useLpConfig() {
|
|
25
|
+
const config = inject(LP_CONFIG, {});
|
|
26
|
+
return computed(() => toValue(config));
|
|
27
|
+
}
|
|
28
|
+
function useLabels(scope, defaults, own) {
|
|
29
|
+
const config = useLpConfig();
|
|
30
|
+
return computed(() => ({ ...defaults, ...config.value.labels?.[scope], ...own?.() }));
|
|
31
|
+
}
|
|
32
|
+
function useLinkComponent() {
|
|
33
|
+
const config = useLpConfig();
|
|
34
|
+
return computed(() => config.value.linkComponent ?? "a");
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
LP_CONFIG,
|
|
38
|
+
lpConfig,
|
|
39
|
+
mergeConfig,
|
|
40
|
+
provideLpConfig,
|
|
41
|
+
useLabels,
|
|
42
|
+
useLinkComponent,
|
|
43
|
+
useLpConfig
|
|
44
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ref, computed, toValue, watch } from "vue";
|
|
2
|
+
const NON_TEXT_INPUTS = /* @__PURE__ */ new Set(["checkbox", "radio", "button", "submit", "reset", "range", "color", "file", "image"]);
|
|
2
3
|
const TYPEAHEAD_RESET_MS = 700;
|
|
3
4
|
function useRovingFocus(items, options = {}) {
|
|
4
5
|
const {
|
|
@@ -69,7 +70,13 @@ function useRovingFocus(items, options = {}) {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
}
|
|
73
|
+
function isTextEntry(target) {
|
|
74
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
75
|
+
if (target.isContentEditable || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement) return true;
|
|
76
|
+
return target instanceof HTMLInputElement && !NON_TEXT_INPUTS.has(target.type);
|
|
77
|
+
}
|
|
72
78
|
function onKeydown(event) {
|
|
79
|
+
if (event.isComposing || isTextEntry(event.target)) return;
|
|
73
80
|
const vertical = orientation === "vertical" || orientation === "both";
|
|
74
81
|
const horizontal = orientation === "horizontal" || orientation === "both";
|
|
75
82
|
switch (event.key) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ComputedRef, type CSSProperties, type Ref } from "vue";
|
|
1
|
+
import { type ComputedRef, type CSSProperties, type MaybeRefOrGetter, type Ref } from "vue";
|
|
2
2
|
export interface UseTiltOptions {
|
|
3
3
|
/** Max rotation toward the pointer, in degrees (per axis). Default 10. */
|
|
4
4
|
max?: number;
|
|
@@ -20,4 +20,4 @@ export interface UseTilt {
|
|
|
20
20
|
/** True while the pointer is over the element (drives lift/scale). */
|
|
21
21
|
active: Ref<boolean>;
|
|
22
22
|
}
|
|
23
|
-
export declare function useTilt(
|
|
23
|
+
export declare function useTilt(source?: MaybeRefOrGetter<UseTiltOptions>): UseTilt;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { useReducedMotion } from "motion-v";
|
|
2
|
-
import {
|
|
3
|
-
function useTilt(
|
|
4
|
-
const
|
|
5
|
-
const lift = options.lift ?? 8;
|
|
6
|
-
const scale = options.scale ?? 1.03;
|
|
2
|
+
import { computed, toValue, ref } from "vue";
|
|
3
|
+
function useTilt(source = {}) {
|
|
4
|
+
const options = computed(() => toValue(source));
|
|
7
5
|
const reduceMotion = useReducedMotion();
|
|
8
6
|
const active = ref(false);
|
|
9
7
|
const nx = ref(0);
|
|
@@ -28,6 +26,7 @@ function useTilt(options = {}) {
|
|
|
28
26
|
if (reduceMotion.value) {
|
|
29
27
|
return { transition: "transform 0.2s ease" };
|
|
30
28
|
}
|
|
29
|
+
const { max = 10, lift = 8, scale = 1.03 } = options.value;
|
|
31
30
|
const rx = active.value ? -ny.value * max : 0;
|
|
32
31
|
const ry = active.value ? nx.value * max : 0;
|
|
33
32
|
const z = active.value ? lift : 0;
|
|
@@ -39,7 +38,7 @@ function useTilt(options = {}) {
|
|
|
39
38
|
};
|
|
40
39
|
});
|
|
41
40
|
const glareStyle = computed(() => {
|
|
42
|
-
if (!options.glare || reduceMotion.value || !active.value) {
|
|
41
|
+
if (!options.value.glare || reduceMotion.value || !active.value) {
|
|
43
42
|
return { opacity: 0 };
|
|
44
43
|
}
|
|
45
44
|
const px = (nx.value + 1) * 50;
|
|
@@ -7,6 +7,9 @@ export interface ToastAction {
|
|
|
7
7
|
}
|
|
8
8
|
export interface ToastItem {
|
|
9
9
|
id: number;
|
|
10
|
+
/** Bumped when a toast with the same `key` replaces this one in place. */
|
|
11
|
+
rev: number;
|
|
12
|
+
key?: string;
|
|
10
13
|
title?: string;
|
|
11
14
|
message: string;
|
|
12
15
|
variant: ToastVariant;
|
|
@@ -18,12 +21,28 @@ export interface ToastItem {
|
|
|
18
21
|
onClick?: () => void | Promise<void>;
|
|
19
22
|
}
|
|
20
23
|
export interface ToastOptions {
|
|
24
|
+
/**
|
|
25
|
+
* A toast with the same key replaces the one on screen instead of stacking
|
|
26
|
+
* beside it: "Saved" ten times is one toast, and a progress toast can turn
|
|
27
|
+
* into its result.
|
|
28
|
+
*/
|
|
29
|
+
key?: string;
|
|
21
30
|
title?: string;
|
|
22
31
|
duration?: number;
|
|
23
32
|
actions?: ToastAction[];
|
|
24
33
|
onClick?: () => void | Promise<void>;
|
|
25
34
|
}
|
|
26
35
|
declare function push(variant: ToastVariant, message: string, options?: ToastOptions): number;
|
|
36
|
+
export interface ToastPromiseMessages<T> {
|
|
37
|
+
loading: string;
|
|
38
|
+
success: string | ((value: T) => string);
|
|
39
|
+
error: string | ((error: unknown) => string);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* One toast that follows a promise: a persistent "loading" toast that turns
|
|
43
|
+
* into the success or error toast when it settles. Returns the promise itself.
|
|
44
|
+
*/
|
|
45
|
+
declare function promise<T>(task: Promise<T>, messages: ToastPromiseMessages<T>, options?: ToastOptions): Promise<T>;
|
|
27
46
|
declare function dismiss(id: number): void;
|
|
28
47
|
declare function clear(): void;
|
|
29
48
|
export declare function useToast(): {
|
|
@@ -33,6 +52,7 @@ export declare function useToast(): {
|
|
|
33
52
|
success: (message: string, o?: ToastOptions) => number;
|
|
34
53
|
warning: (message: string, o?: ToastOptions) => number;
|
|
35
54
|
error: (message: string, o?: ToastOptions) => number;
|
|
55
|
+
promise: typeof promise;
|
|
36
56
|
/** Low-level push for bridges (e.g. @leavepulse/ui-sdk apiError). */
|
|
37
57
|
push: typeof push;
|
|
38
58
|
dismiss: typeof dismiss;
|
|
@@ -6,10 +6,10 @@ const g = globalThis;
|
|
|
6
6
|
const state = g[STATE_KEY] ??= { queue: reactive([]), nextId: 0 };
|
|
7
7
|
const queue = state.queue;
|
|
8
8
|
function push(variant, message, options = {}) {
|
|
9
|
-
|
|
9
|
+
if (typeof window === "undefined") return -1;
|
|
10
10
|
const duration = options.duration ?? DEFAULT_DURATION;
|
|
11
|
-
const
|
|
12
|
-
|
|
11
|
+
const fields = {
|
|
12
|
+
key: options.key,
|
|
13
13
|
title: options.title,
|
|
14
14
|
message,
|
|
15
15
|
variant,
|
|
@@ -18,11 +18,34 @@ function push(variant, message, options = {}) {
|
|
|
18
18
|
actions: options.actions,
|
|
19
19
|
onClick: options.onClick
|
|
20
20
|
};
|
|
21
|
+
const same = options.key === void 0 ? -1 : queue.findIndex((t) => t.key === options.key);
|
|
22
|
+
const existing = queue[same];
|
|
23
|
+
if (existing) {
|
|
24
|
+
queue.splice(same, 1, { ...fields, id: existing.id, rev: existing.rev + 1 });
|
|
25
|
+
return existing.id;
|
|
26
|
+
}
|
|
27
|
+
const id = state.nextId++;
|
|
21
28
|
const overflow = queue.length - (MAX_ACTIVE - 1);
|
|
22
29
|
if (overflow > 0) queue.splice(0, overflow);
|
|
23
|
-
queue.push(
|
|
30
|
+
queue.push({ ...fields, id, rev: 0 });
|
|
24
31
|
return id;
|
|
25
32
|
}
|
|
33
|
+
let promiseKey = 0;
|
|
34
|
+
function promise(task, messages, options = {}) {
|
|
35
|
+
const key = options.key ?? `@leavepulse/ui:promise-${promiseKey++}`;
|
|
36
|
+
push("info", messages.loading, { ...options, key, duration: 0, actions: void 0 });
|
|
37
|
+
task.then(
|
|
38
|
+
(value) => {
|
|
39
|
+
const text = typeof messages.success === "function" ? messages.success(value) : messages.success;
|
|
40
|
+
push("success", text, { ...options, key });
|
|
41
|
+
},
|
|
42
|
+
(error) => {
|
|
43
|
+
const text = typeof messages.error === "function" ? messages.error(error) : messages.error;
|
|
44
|
+
push("error", text, { ...options, key });
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
return task;
|
|
48
|
+
}
|
|
26
49
|
function dismiss(id) {
|
|
27
50
|
const i = queue.findIndex((t) => t.id === id);
|
|
28
51
|
if (i !== -1) queue.splice(i, 1);
|
|
@@ -38,6 +61,7 @@ function useToast() {
|
|
|
38
61
|
success: (message, o) => push("success", message, o),
|
|
39
62
|
warning: (message, o) => push("warning", message, o),
|
|
40
63
|
error: (message, o) => push("error", message, o),
|
|
64
|
+
promise,
|
|
41
65
|
/** Low-level push for bridges (e.g. @leavepulse/ui-sdk apiError). */
|
|
42
66
|
push,
|
|
43
67
|
dismiss,
|