@leavepulse/ui 0.43.4 → 0.45.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 +121 -7
- package/dist/assets/{LpCommandPalette-B-0Est4Z.css → LpCommandPalette-nGNnzXqQ.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/{LpRollingText-CR1mb0Vt.css → LpRollingText-BGnE6q9j.css} +3 -0
- package/dist/assets/{LpShortcutsDialog-C6YoQ8_M.css → LpShortcutsDialog-CqYX3yy6.css} +28 -7
- package/dist/assets/{LpSkeleton-D7xU8IPV.css → LpSkeleton-H3Z5dEIk.css} +4 -1
- package/dist/assets/{LpTable-BgPLJOXa.css → LpTable-CPwWxTJS.css} +49 -9
- package/dist/chunks/ContextMenuItems-BtKU7074.js +124 -0
- package/dist/chunks/{LayoutCanvas-Dd20boUG.js → LayoutCanvas-BkaGXTjx.js} +26 -9
- package/dist/chunks/{LayoutNode-DGpZA9LX.js → LayoutNode-B3_9E1Xa.js} +28 -12
- package/dist/chunks/{LpActionBar-BlvtGB2P.js → LpActionBar-CnfFVIwC.js} +26 -12
- package/dist/chunks/{LpAddressInput-B-Zvxsn7.js → LpAddressInput-BwX8uo4_.js} +54 -49
- package/dist/chunks/{LpAlert-BAYHDSS1.js → LpAlert-B6ilrM7T.js} +16 -9
- package/dist/chunks/{LpAppShell-BGQVIY-3.js → LpAppShell-C53_RaGF.js} +53 -26
- package/dist/chunks/{LpAutocomplete-DExlIZe_.js → LpAutocomplete-C8IgSMsi.js} +52 -24
- package/dist/chunks/{LpAvatar-B3-xAOH3.js → LpAvatar-C1XStdOo.js} +19 -10
- package/dist/chunks/LpBreadcrumbs-CQJd73Gm.js +95 -0
- package/dist/chunks/{LpButton-5VXXCjwF.js → LpButton-CKSwC6Mu.js} +45 -15
- package/dist/chunks/{LpCalendar-B7Ko5RdR.js → LpCalendar-DrA2McTU.js} +1 -1
- package/dist/chunks/{LpCard-BhI1N_vL.js → LpCard-C2IvucmX.js} +1 -1
- package/dist/chunks/{LpCheckbox-CVccKYaH.js → LpCheckbox-Bkj13CbW.js} +8 -4
- package/dist/chunks/{LpCodeBlock-Bo0B4TFR.js → LpCodeBlock-ca7ANFyW.js} +49 -17
- package/dist/chunks/{LpColorPicker-BlXea7LM.js → LpColorPicker-zz4VGkG4.js} +75 -26
- package/dist/chunks/{LpConfirmDialog-Q-bxff8z.js → LpConfirmDialog-DCw5jR1t.js} +59 -17
- package/dist/chunks/LpConfirmHost-CbywMpMP.js +49 -0
- package/dist/chunks/LpContextMenu-BqkAYa9X.js +69 -0
- package/dist/chunks/LpDatePicker-DuusgT69.js +195 -0
- package/dist/chunks/LpDateRangePicker-DHeuznfC.js +208 -0
- package/dist/chunks/{LpDisclosure-ChQYmjlR.js → LpDisclosure-Cc23-JOT.js} +1 -1
- package/dist/chunks/{LpDrawer-BZ6k3hvo.js → LpDrawer-Dt4aB4Fr.js} +65 -28
- package/dist/chunks/{LpDropdownMenu-DJpoqRCb.js → LpDropdownMenu-DdJGQzPn.js} +2 -1
- package/dist/chunks/{LpEmptyState-Dsdqz1Wj.js → LpEmptyState-BJbZXC8D.js} +1 -1
- package/dist/chunks/LpFieldNotice-wtieLUTj.js +64 -0
- package/dist/chunks/{LpFileTree-_5vFBgaK.js → LpFileTree-CNurOvkV.js} +25 -10
- package/dist/chunks/{LpFileTreeNode-BdcIYKIE.js → LpFileTreeNode-D_qRRdsH.js} +3 -3
- package/dist/chunks/LpFormField-CtVYj6tO.js +101 -0
- package/dist/chunks/{LpIcon-CCnX5_2j.js → LpIcon-DYtaid0K.js} +20 -6
- package/dist/chunks/{LpInput-9EXWUM9B.js → LpInput-D-YGQbEv.js} +58 -20
- package/dist/chunks/{LpLightbox-2sYVMY-S.js → LpLightbox-DSDahF23.js} +148 -61
- package/dist/chunks/{LpLink-DCND-zyu.js → LpLink-Krkjhe_y.js} +15 -6
- package/dist/chunks/{LpLogViewer-CaRy5MeS.js → LpLogViewer-KypbgOyo.js} +300 -72
- package/dist/chunks/{LpMapPicker-ul4oYno6.js → LpMapPicker-Cc84LaM3.js} +39 -14
- package/dist/chunks/{LpMediaGallery-FF-NquIo.js → LpMediaGallery-YKr-cQPN.js} +7 -4
- package/dist/chunks/{LpModal-Bt0pQCI_.js → LpModal-DMMDQHAu.js} +52 -29
- package/dist/chunks/{LpModalNav-YaifHrXC.js → LpModalNav-BVPDyIup.js} +10 -7
- package/dist/chunks/{LpNotificationBell-DyGGsMAM.js → LpNotificationBell-CCwyJP5C.js} +79 -33
- package/dist/chunks/{LpNumberField-CWuX08iX.js → LpNumberField-B77O-z28.js} +21 -8
- 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-BcDlo3r_.js} +31 -20
- package/dist/chunks/{LpPasswordInput--sjYCkLS.js → LpPasswordInput-eKzQhbQP.js} +11 -8
- package/dist/chunks/LpPhoneInput-BCbrrhgE.js +336 -0
- package/dist/chunks/{LpPie-D6OnzwlJ.js → LpPie-BEJ2SfI6.js} +84 -28
- package/dist/chunks/{LpPopover-8268UmlC.js → LpPopover-DCKeqiVn.js} +5 -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/LpRangeCalendar-DZuaRV0W.js +331 -0
- package/dist/chunks/{LpRollingText-CM5gkSeF.js → LpRollingText-DB7upf-5.js} +1 -1
- package/dist/chunks/{LpScrollArea-D8xyKXcF.js → LpScrollArea-Bry1fdOM.js} +14 -2
- package/dist/chunks/{LpSection-aM6J_u51.js → LpSection-tKEH0jYf.js} +1 -1
- package/dist/chunks/{LpSegmented-fV0Sk92p.js → LpSegmented-C09EEam2.js} +8 -4
- package/dist/chunks/{LpSelect-ANIVtrpM.js → LpSelect-DNIL6MB9.js} +50 -31
- package/dist/chunks/{LpSidebar-ClBUwoEz.js → LpSidebar-CQNB0-yU.js} +30 -22
- package/dist/chunks/{LpSidebarNav-llOcs_Fn.js → LpSidebarNav-kx-LdqNU.js} +16 -11
- 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-B-7udBRP.js} +2 -2
- package/dist/chunks/{LpStepper-Bk-XaQYU.js → LpStepper-C7XiVKZj.js} +34 -19
- package/dist/chunks/{LpSwitch-Blizvqyt.js → LpSwitch-OrcOJX9m.js} +41 -27
- package/dist/chunks/LpTableFilter-BV8PpLM9.js +158 -0
- package/dist/chunks/{LpTableOfContents-CjvxPMpU.js → LpTableOfContents-BUcAfGPQ.js} +36 -41
- package/dist/chunks/{LpTabs-Cefe_lys.js → LpTabs-29iW1XOx.js} +20 -6
- package/dist/chunks/LpTagsInput-D1SuqbhB.js +300 -0
- package/dist/chunks/LpTextarea-uuScWo6-.js +77 -0
- package/dist/chunks/LpThemeProvider-Be4r2TQX.js +35 -0
- package/dist/chunks/LpThemeSwitcher-B4aVPbNC.js +166 -0
- package/dist/chunks/{LpTilt-DuyKbpkA.js → LpTilt-B7KHX1zH.js} +2 -2
- package/dist/chunks/{LpTimeField-K6nAGboX.js → LpTimeField-Cqe7fXnh.js} +14 -8
- package/dist/chunks/{LpToaster-gDB1EaU3.js → LpToaster-DpJvoCOh.js} +94 -31
- package/dist/chunks/{LpTooltip-hCLPAqMG.js → LpTooltip-CXVKcOXT.js} +14 -6
- package/dist/chunks/LpTooltipProvider-CftjV7Xc.js +25 -0
- package/dist/chunks/{LpTopologyCanvas-Cm1L39uL.js → LpTopologyCanvas-6ozNrQKh.js} +8 -2
- package/dist/chunks/{LpUptimeBar-DvAMno0S.js → LpUptimeBar-ugcv25xj.js} +44 -15
- package/dist/chunks/icons-CkA0njzm.js +43 -0
- package/dist/chunks/{LpThemeSwitcher-Cue-myST.js → useTheme-DJm1JSsY.js} +215 -260
- 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/LpCalendar.vue.js +1 -1
- package/dist/components/LpCard.vue.js +1 -1
- package/dist/components/LpChart.vue.d.ts +47 -3
- package/dist/components/LpChart.vue.js +377 -97
- 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.d.ts +2 -2
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.d.ts +14 -3
- package/dist/components/LpCommandPalette.vue.js +142 -77
- 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/LpDateRangePicker.vue.d.ts +60 -0
- package/dist/components/LpDateRangePicker.vue.js +4 -0
- package/dist/components/LpDisclosure.vue.js +1 -1
- package/dist/components/LpDrawer.vue.d.ts +10 -0
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpDropdownMenu.vue.js +1 -1
- package/dist/components/LpDropzone.vue.js +23 -7
- package/dist/components/LpEmptyState.vue.js +1 -1
- package/dist/components/LpFieldNotice.vue.d.ts +8 -0
- package/dist/components/LpFieldNotice.vue.js +4 -0
- 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 +13 -1
- package/dist/components/LpFormField.vue.js +1 -1
- package/dist/components/LpIcon.vue.d.ts +0 -1
- package/dist/components/LpIcon.vue.js +1 -1
- package/dist/components/LpImageEditor.vue.js +93 -58
- package/dist/components/LpInput.vue.d.ts +22 -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 +72 -3
- 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.d.ts +13 -0
- 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.d.ts +1 -1
- package/dist/components/LpProgress.vue.js +1 -1
- package/dist/components/LpRadioGroup.vue.js +1 -1
- package/dist/components/LpRangeCalendar.vue.d.ts +51 -0
- package/dist/components/LpRangeCalendar.vue.js +4 -0
- package/dist/components/LpRollingText.vue.d.ts +1 -1
- package/dist/components/LpRollingText.vue.js +1 -1
- package/dist/components/LpScrollArea.vue.d.ts +2 -2
- package/dist/components/LpScrollArea.vue.js +1 -1
- package/dist/components/LpSection.vue.d.ts +1 -1
- package/dist/components/LpSection.vue.js +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 +7 -1
- package/dist/components/LpShortcutsDialog.vue.js +60 -15
- 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/LpStepper.vue.js +1 -1
- package/dist/components/LpSwitch.vue.js +1 -1
- package/dist/components/LpTable.vue.d.ts +107 -3
- package/dist/components/LpTable.vue.js +540 -128
- package/dist/components/LpTableFilter.vue.d.ts +31 -0
- package/dist/components/LpTableFilter.vue.js +4 -0
- 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/LpTagsInput.vue.d.ts +77 -0
- package/dist/components/LpTagsInput.vue.js +4 -0
- 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 +15 -2
- 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 +42 -6
- 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.js +3 -0
- 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/dateRange.d.ts +43 -0
- package/dist/components/dateRange.js +76 -0
- package/dist/components/exportData.d.ts +58 -0
- package/dist/components/exportData.js +165 -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/logSearch.d.ts +18 -0
- package/dist/components/logSearch.js +36 -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/phone.d.ts +41 -0
- package/dist/components/phone.js +80 -0
- package/dist/components/sidebar.d.ts +7 -1
- package/dist/components/tableFilter.d.ts +36 -0
- package/dist/components/tableFilter.js +84 -0
- package/dist/components/tableSort.d.ts +8 -0
- package/dist/components/tableSort.js +31 -0
- package/dist/components/tags.d.ts +20 -0
- package/dist/components/tags.js +37 -0
- package/dist/composables/useField.d.ts +38 -0
- package/dist/composables/useField.js +45 -0
- package/dist/composables/useFieldNotice.d.ts +17 -0
- package/dist/composables/useFieldNotice.js +28 -0
- package/dist/composables/useForm.d.ts +53 -0
- package/dist/composables/useForm.js +207 -0
- package/dist/composables/useFormDraft.d.ts +10 -3
- package/dist/composables/useFormDraft.js +9 -14
- package/dist/composables/useHotkeys.d.ts +81 -5
- package/dist/composables/useHotkeys.js +223 -25
- package/dist/composables/useInputFilter.d.ts +17 -0
- package/dist/composables/useInputFilter.js +47 -10
- package/dist/composables/useInputMask.d.ts +59 -0
- package/dist/composables/useInputMask.js +170 -0
- package/dist/composables/useLpConfig.d.ts +24 -0
- package/dist/composables/useLpConfig.js +44 -0
- package/dist/composables/usePaintSelect.d.ts +22 -0
- package/dist/composables/usePaintSelect.js +169 -0
- package/dist/composables/useRovingFocus.js +7 -0
- package/dist/composables/useShift.js +7 -2
- 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/icons-all.d.ts +1 -0
- package/dist/icons-all.js +3 -0
- package/dist/icons.d.ts +14 -1
- package/dist/index.d.ts +60 -4
- package/dist/index.js +245 -141
- package/dist/kit-icons.d.ts +2 -0
- 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/backdrop.d.ts +12 -0
- package/dist/theme/presets.d.ts +0 -2
- package/dist/theme/useTheme.d.ts +32 -9
- package/package.json +18 -8
- package/src/component-names.ts +9 -7
- package/src/components/ContextMenuItems.vue +88 -0
- package/src/components/LayoutCanvas.vue +26 -6
- package/src/components/LayoutNode.vue +30 -4
- package/src/components/LpActionBar.vue +31 -8
- 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 +47 -10
- package/src/components/LpAvatar.vue +20 -6
- package/src/components/LpBreadcrumbs.vue +65 -41
- package/src/components/LpButton.vue +60 -14
- package/src/components/LpChart.vue +351 -28
- package/src/components/LpCheckbox.vue +7 -3
- package/src/components/LpCodeBlock.vue +50 -14
- package/src/components/LpColorPicker.vue +73 -10
- package/src/components/LpCommandPalette.vue +115 -20
- package/src/components/LpConfirmDialog.vue +68 -17
- package/src/components/LpConfirmHost.vue +59 -0
- package/src/components/LpContextMenu.vue +15 -116
- package/src/components/LpDatePicker.vue +104 -68
- package/src/components/LpDateRangePicker.vue +224 -0
- package/src/components/LpDrawer.vue +56 -19
- package/src/components/LpDropdownMenu.vue +1 -0
- package/src/components/LpDropzone.vue +21 -4
- package/src/components/LpFieldNotice.vue +65 -0
- package/src/components/LpFileTree.vue +33 -7
- package/src/components/LpFormField.vue +59 -7
- package/src/components/LpIcon.vue +22 -4
- package/src/components/LpImageEditor.vue +96 -57
- package/src/components/LpInput.vue +62 -11
- package/src/components/LpLightbox.vue +140 -29
- package/src/components/LpLink.vue +24 -13
- package/src/components/LpLogViewer.vue +311 -39
- package/src/components/LpMapPicker.vue +36 -13
- package/src/components/LpMediaGallery.vue +8 -2
- package/src/components/LpModal.vue +54 -28
- package/src/components/LpModalNav.vue +5 -2
- package/src/components/LpNotificationBell.vue +77 -23
- 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 +224 -39
- package/src/components/LpPie.vue +102 -20
- package/src/components/LpPopover.vue +4 -0
- package/src/components/LpProgress.vue +5 -3
- package/src/components/LpRadioGroup.vue +9 -2
- package/src/components/LpRangeCalendar.vue +365 -0
- package/src/components/LpRollingText.vue +3 -0
- package/src/components/LpScrollArea.vue +22 -2
- package/src/components/LpSegmented.vue +12 -2
- package/src/components/LpSelect.vue +0 -0
- package/src/components/LpShortcutsDialog.vue +83 -9
- 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/LpStepper.vue +23 -10
- package/src/components/LpSwitch.vue +31 -8
- package/src/components/LpTable.vue +668 -90
- package/src/components/LpTableFilter.vue +175 -0
- package/src/components/LpTableOfContents.vue +50 -32
- package/src/components/LpTabs.vue +23 -6
- package/src/components/LpTagsInput.vue +364 -0
- package/src/components/LpTextarea.vue +44 -6
- package/src/components/LpThemeProvider.vue +41 -0
- package/src/components/LpThemeSwitcher.vue +76 -33
- package/src/components/LpTilt.vue +2 -2
- package/src/components/LpTimeField.vue +14 -3
- package/src/components/LpToaster.vue +115 -27
- package/src/components/LpTooltip.vue +29 -5
- package/src/components/LpTooltipProvider.vue +24 -0
- package/src/components/LpTopologyCanvas.vue +19 -2
- package/src/components/LpUptimeBar.vue +46 -8
- package/src/components/chart.ts +82 -8
- package/src/components/codeHighlight.ts +111 -15
- package/src/components/color.ts +30 -9
- package/src/components/commandSearch.ts +6 -0
- package/src/components/confirm.ts +58 -0
- package/src/components/countries.ts +12 -0
- package/src/components/dateRange.ts +134 -0
- package/src/components/exportData.ts +228 -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/logSearch.ts +46 -0
- package/src/components/modalDepth.ts +48 -6
- package/src/components/otp.ts +16 -0
- package/src/components/phone.ts +140 -0
- package/src/components/sidebar.ts +7 -1
- package/src/components/tableFilter.ts +122 -0
- package/src/components/tableSort.ts +37 -0
- package/src/components/tags.ts +62 -0
- package/src/composables/useField.ts +83 -0
- package/src/composables/useFieldNotice.ts +59 -0
- package/src/composables/useForm.ts +314 -0
- package/src/composables/useFormDraft.ts +25 -20
- package/src/composables/useHotkeys.ts +355 -28
- package/src/composables/useInputFilter.ts +77 -14
- package/src/composables/useInputMask.ts +254 -0
- package/src/composables/useLpConfig.ts +87 -0
- package/src/composables/usePaintSelect.ts +243 -0
- package/src/composables/useRovingFocus.ts +11 -0
- package/src/composables/useShift.ts +13 -2
- package/src/composables/useTilt.ts +5 -6
- package/src/composables/useToast.ts +56 -4
- package/src/composables/useZoomPan.ts +6 -6
- package/src/iconSubset.d.mts +10 -0
- package/src/iconSubset.mjs +51 -0
- package/src/icons-all.ts +10 -0
- package/src/icons.ts +59 -8
- package/src/index.ts +73 -4
- package/src/kit-icons.ts +5 -0
- package/src/layout/tree.ts +30 -0
- package/src/layout/useLayout.ts +68 -18
- package/src/nuxt.ts +87 -17
- package/src/runtime/config.plugin.ts +14 -0
- package/src/runtime/theme.plugin.ts +43 -23
- package/src/theme/backdrop.ts +69 -0
- package/src/theme/presets.ts +6 -64
- package/src/theme/useTheme.ts +206 -100
- package/src/tokens/tokens.css +46 -0
- 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/LpPhoneInput-BDSKVl_z.js +0 -209
- package/dist/chunks/LpTextarea-t7F6gv8E.js +0 -47
|
@@ -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
|
|
|
@@ -37,18 +37,31 @@ const props = withDefaults(
|
|
|
37
37
|
variant?: AlertVariants["variant"]
|
|
38
38
|
title?: string
|
|
39
39
|
icon?: string
|
|
40
|
+
/**
|
|
41
|
+
* Announce the alert to screen readers when it appears. "assertive"
|
|
42
|
+
* (role=alert) interrupts and is for something that needs action now,
|
|
43
|
+
* "polite" (role=status) waits its turn, false reads it in place. By
|
|
44
|
+
* default only `danger` interrupts: an info or success banner that is part
|
|
45
|
+
* of the page from the start should not be shouted on load.
|
|
46
|
+
*/
|
|
47
|
+
live?: "assertive" | "polite" | false
|
|
40
48
|
}>(),
|
|
41
|
-
{ variant: "info" },
|
|
49
|
+
{ variant: "info", live: undefined },
|
|
42
50
|
)
|
|
43
51
|
|
|
52
|
+
const role = computed(() => {
|
|
53
|
+
const live = props.live ?? (props.variant === "danger" ? "assertive" : false)
|
|
54
|
+
return live === "assertive" ? "alert" : live === "polite" ? "status" : undefined
|
|
55
|
+
})
|
|
56
|
+
|
|
44
57
|
const classes = computed(() => alert({ variant: props.variant }))
|
|
45
58
|
const resolvedIcon = computed(() => props.icon ?? ICONS[props.variant ?? "info"])
|
|
46
59
|
const iconTone = computed(() => ICON_TONE[props.variant ?? "info"])
|
|
47
60
|
</script>
|
|
48
61
|
|
|
49
62
|
<template>
|
|
50
|
-
<div :class="classes" role="
|
|
51
|
-
<LpIcon :name="resolvedIcon" :size="18" :class="['mt-0.5 shrink-0', iconTone]" />
|
|
63
|
+
<div :class="classes" :role="role">
|
|
64
|
+
<LpIcon :name="resolvedIcon" :size="18" :class="['mt-0.5 shrink-0', iconTone]" aria-hidden="true" />
|
|
52
65
|
<div class="min-w-0">
|
|
53
66
|
<p v-if="title" class="font-semibold text-ink">{{ title }}</p>
|
|
54
67
|
<div :class="['text-muted-strong', title ? 'mt-0.5' : '']">
|
|
@@ -31,7 +31,8 @@ export default { inheritAttrs: false }
|
|
|
31
31
|
* Overlays that aren't part of the frame (command palette, toaster) live in the
|
|
32
32
|
* host next to <LpAppShell>.
|
|
33
33
|
*/
|
|
34
|
-
import { computed, watch } from "vue"
|
|
34
|
+
import { computed, ref, useId, watch } from "vue"
|
|
35
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
35
36
|
import LpButton from "./LpButton.vue"
|
|
36
37
|
import LpIcon from "./LpIcon.vue"
|
|
37
38
|
import LpScrollArea from "./LpScrollArea.vue"
|
|
@@ -81,6 +82,8 @@ const props = withDefaults(
|
|
|
81
82
|
fullBleed?: boolean
|
|
82
83
|
/** Loading skeleton for the nav. */
|
|
83
84
|
loading?: boolean
|
|
85
|
+
/** Text overrides: the burger's name and the skip link. */
|
|
86
|
+
labels?: Partial<{ openMenu: string; skipToContent: string }>
|
|
84
87
|
}>(),
|
|
85
88
|
{
|
|
86
89
|
mobileBreakpoint: "lg",
|
|
@@ -140,10 +143,30 @@ const drawerOpen = computed({
|
|
|
140
143
|
get: () => props.open ?? false,
|
|
141
144
|
set: (v) => emit("update:open", v),
|
|
142
145
|
})
|
|
146
|
+
const text = useLabels(
|
|
147
|
+
"appShell",
|
|
148
|
+
{ openMenu: "Open menu", skipToContent: "Skip to content" },
|
|
149
|
+
() => props.labels,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
/*
|
|
153
|
+
* The page scrolls inside the shell's own region, not the window, so the
|
|
154
|
+
* router's scroll behaviour never reaches it: without this a route change from
|
|
155
|
+
* a scrolled page landed mid-way down the next one.
|
|
156
|
+
*/
|
|
157
|
+
const scrollArea = ref<{ viewportEl: HTMLElement | null } | null>(null)
|
|
158
|
+
const mainId = `lp-main-${useId()}`
|
|
159
|
+
|
|
160
|
+
// Focus instead of following the hash, which a router would treat as a navigation.
|
|
161
|
+
function skipToMain() {
|
|
162
|
+
document.getElementById(mainId)?.focus()
|
|
163
|
+
}
|
|
164
|
+
|
|
143
165
|
watch(
|
|
144
166
|
() => props.path,
|
|
145
167
|
() => {
|
|
146
168
|
if (drawerOpen.value) emit("update:open", false)
|
|
169
|
+
scrollArea.value?.viewportEl?.scrollTo({ top: 0, left: 0, behavior: "instant" })
|
|
147
170
|
},
|
|
148
171
|
)
|
|
149
172
|
|
|
@@ -176,6 +199,13 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
176
199
|
<div
|
|
177
200
|
:class="rootClass"
|
|
178
201
|
v-bind="rest">
|
|
202
|
+
<a
|
|
203
|
+
:href="`#${mainId}`"
|
|
204
|
+
@click.prevent="skipToMain"
|
|
205
|
+
class="sr-only z-(--z-toast) rounded-control bg-surface-raised px-3 py-2 text-sm font-medium text-ink focus:not-sr-only focus:fixed focus:left-3 focus:top-3 focus:outline-none focus:ring-2 focus:ring-ring"
|
|
206
|
+
>
|
|
207
|
+
{{ text.skipToContent }}
|
|
208
|
+
</a>
|
|
179
209
|
<LpSidebar
|
|
180
210
|
:model-value="modelValue"
|
|
181
211
|
:items="items"
|
|
@@ -225,7 +255,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
225
255
|
variant="ghost"
|
|
226
256
|
size="sm"
|
|
227
257
|
square
|
|
228
|
-
aria-label="
|
|
258
|
+
:aria-label="text.openMenu"
|
|
229
259
|
@click="emit('update:open', true)"
|
|
230
260
|
>
|
|
231
261
|
<LpIcon name="lucide:menu" :size="18" />
|
|
@@ -265,16 +295,19 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
265
295
|
|
|
266
296
|
<!-- full-bleed: page owns the whole region (canvas/map); otherwise the
|
|
267
297
|
single scroll region with padding. -->
|
|
268
|
-
<
|
|
269
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
298
|
+
<main :id="mainId" tabindex="-1" class="flex min-h-0 flex-1 flex-col outline-none">
|
|
299
|
+
<div v-if="fullBleed" class="min-h-0 flex-1 overflow-hidden">
|
|
300
|
+
<slot />
|
|
301
|
+
</div>
|
|
302
|
+
<LpScrollArea
|
|
303
|
+
v-else
|
|
304
|
+
ref="scrollArea"
|
|
305
|
+
class="min-h-0 flex-1"
|
|
306
|
+
:content-class="`px-4 py-6 md:px-6 ${contentClass}`"
|
|
307
|
+
>
|
|
308
|
+
<slot />
|
|
309
|
+
</LpScrollArea>
|
|
310
|
+
</main>
|
|
278
311
|
</div>
|
|
279
312
|
|
|
280
313
|
<slot name="overlays" />
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default { inheritAttrs: false }
|
|
3
|
+
</script>
|
|
4
|
+
|
|
1
5
|
<script setup lang="ts">
|
|
2
6
|
/*
|
|
3
7
|
* Free-text autocomplete — a text field that suggests as you type but never
|
|
@@ -21,8 +25,10 @@ import {
|
|
|
21
25
|
ComboboxRoot,
|
|
22
26
|
ComboboxViewport,
|
|
23
27
|
} from "reka-ui"
|
|
24
|
-
import { computed, ref, watch } from "vue"
|
|
28
|
+
import { computed, ref, useAttrs, watch } from "vue"
|
|
29
|
+
import { useFieldControl } from "../composables/useField"
|
|
25
30
|
import { useInputFilter } from "../composables/useInputFilter"
|
|
31
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
26
32
|
import { CLOSE_ICON, OPTION_ITEM, POPOVER_PANEL } from "./dropdown"
|
|
27
33
|
import LpIcon from "./LpIcon.vue"
|
|
28
34
|
|
|
@@ -78,7 +84,7 @@ const emit = defineEmits<{
|
|
|
78
84
|
(e: "select", value: string): void
|
|
79
85
|
}>()
|
|
80
86
|
|
|
81
|
-
const { isInvalid, onBeforeInput, onPaste } = useInputFilter({
|
|
87
|
+
const { isInvalid, onBeforeInput, onInput: filterInput, onCompositionEnd, onPaste } = useInputFilter({
|
|
82
88
|
pattern: () => props.pattern,
|
|
83
89
|
restrict: () => props.restrict,
|
|
84
90
|
})
|
|
@@ -88,7 +94,22 @@ const text = computed<string>({
|
|
|
88
94
|
set: (v) => emit("update:modelValue", v),
|
|
89
95
|
})
|
|
90
96
|
|
|
91
|
-
|
|
97
|
+
// class/style lay out the whole control; every other attr (name, autocomplete,
|
|
98
|
+
// @blur, @keydown…) belongs to the text <input>.
|
|
99
|
+
const attrs = useAttrs()
|
|
100
|
+
const inputAttrs = computed(() => {
|
|
101
|
+
const { class: _c, style: _s, ...rest } = attrs
|
|
102
|
+
return rest
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const field = useFieldControl(() => ({
|
|
106
|
+
wrapped: false,
|
|
107
|
+
id: typeof attrs.id === "string" ? attrs.id : undefined,
|
|
108
|
+
invalid: props.invalid || isInvalid(props.modelValue),
|
|
109
|
+
disabled: props.disabled,
|
|
110
|
+
}))
|
|
111
|
+
const isInvalidState = computed(() => field.value.invalid)
|
|
112
|
+
const labels = useLabels("autocomplete", { clear: "Clear" })
|
|
92
113
|
|
|
93
114
|
// Normalise string|option to a stable shape.
|
|
94
115
|
const items = computed<AutocompleteOption[]>(() =>
|
|
@@ -135,6 +156,13 @@ watch(canOpen, (can) => {
|
|
|
135
156
|
if (focused.value && can) open.value = true
|
|
136
157
|
})
|
|
137
158
|
|
|
159
|
+
// reka opens the list on every keystroke; below `minChars` (or with nothing to
|
|
160
|
+
// show) that only produced an empty "No suggestions" panel, so opening is
|
|
161
|
+
// refused here. Closing is always allowed.
|
|
162
|
+
function onOpenChange(next: boolean) {
|
|
163
|
+
open.value = next && canOpen.value
|
|
164
|
+
}
|
|
165
|
+
|
|
138
166
|
const hasValue = computed(() => (props.modelValue ?? "").length > 0)
|
|
139
167
|
|
|
140
168
|
const anchorSize = {
|
|
@@ -144,6 +172,7 @@ const anchorSize = {
|
|
|
144
172
|
}
|
|
145
173
|
|
|
146
174
|
function onInput(event: Event) {
|
|
175
|
+
filterInput(event)
|
|
147
176
|
typing.value = true
|
|
148
177
|
text.value = (event.target as HTMLInputElement).value
|
|
149
178
|
}
|
|
@@ -172,12 +201,14 @@ function clear() {
|
|
|
172
201
|
|
|
173
202
|
<template>
|
|
174
203
|
<ComboboxRoot
|
|
175
|
-
|
|
204
|
+
:open="open"
|
|
176
205
|
:ignore-filter="true"
|
|
177
206
|
:reset-search-term-on-blur="false"
|
|
178
207
|
:reset-search-term-on-select="false"
|
|
179
|
-
:disabled="disabled"
|
|
180
|
-
class="relative"
|
|
208
|
+
:disabled="field.disabled"
|
|
209
|
+
:class="['relative', attrs.class]"
|
|
210
|
+
:style="attrs.style as never"
|
|
211
|
+
@update:open="onOpenChange"
|
|
181
212
|
>
|
|
182
213
|
<ComboboxAnchor
|
|
183
214
|
data-lp-ring-owner
|
|
@@ -198,14 +229,18 @@ function clear() {
|
|
|
198
229
|
so `text` (and the reactive `open`) track the field exactly.
|
|
199
230
|
-->
|
|
200
231
|
<ComboboxInput
|
|
232
|
+
v-bind="inputAttrs"
|
|
233
|
+
:id="field.id"
|
|
201
234
|
:model-value="text"
|
|
202
235
|
:placeholder="placeholder"
|
|
203
|
-
:disabled="disabled"
|
|
236
|
+
:disabled="field.disabled"
|
|
237
|
+
:required="field.required || undefined"
|
|
204
238
|
:aria-invalid="isInvalidState || undefined"
|
|
205
|
-
|
|
239
|
+
:aria-describedby="field.describedBy"
|
|
206
240
|
class="lp-autofill min-w-0 flex-1 self-stretch bg-transparent outline-none placeholder:text-muted"
|
|
207
241
|
@beforeinput="onBeforeInput"
|
|
208
242
|
@paste="onPaste"
|
|
243
|
+
@compositionend="onCompositionEnd"
|
|
209
244
|
@input="onInput"
|
|
210
245
|
@focus="onFocus"
|
|
211
246
|
@blur="focused = false"
|
|
@@ -214,8 +249,9 @@ function clear() {
|
|
|
214
249
|
v-if="clearable && hasValue"
|
|
215
250
|
type="button"
|
|
216
251
|
class="group flex shrink-0 items-center text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink"
|
|
217
|
-
aria-label="
|
|
218
|
-
@pointerdown.prevent
|
|
252
|
+
:aria-label="labels.clear"
|
|
253
|
+
@pointerdown.prevent
|
|
254
|
+
@click="clear"
|
|
219
255
|
>
|
|
220
256
|
<LpIcon
|
|
221
257
|
name="lucide:x"
|
|
@@ -227,6 +263,7 @@ function clear() {
|
|
|
227
263
|
|
|
228
264
|
<ComboboxPortal>
|
|
229
265
|
<ComboboxContent
|
|
266
|
+
:collision-padding="8"
|
|
230
267
|
position="popper"
|
|
231
268
|
:side-offset="6"
|
|
232
269
|
:class="[POPOVER_PANEL, 'z-(--z-popover) max-h-72 w-(--reka-combobox-trigger-width) overflow-hidden']"
|
|
@@ -10,6 +10,7 @@ import { AvatarFallback, AvatarImage, AvatarRoot } from "reka-ui"
|
|
|
10
10
|
import { tv, type VariantProps } from "tailwind-variants"
|
|
11
11
|
import { computed, onMounted, ref } from "vue"
|
|
12
12
|
import LpContextMenu, { type ContextMenuItemDef } from "./LpContextMenu.vue"
|
|
13
|
+
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
13
14
|
|
|
14
15
|
const avatar = tv({
|
|
15
16
|
base: "inline-flex shrink-0 select-none items-center justify-center overflow-hidden bg-surface-soft font-medium text-muted-strong",
|
|
@@ -65,8 +66,21 @@ const sizeStyle = computed(() =>
|
|
|
65
66
|
: undefined,
|
|
66
67
|
)
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
/* "John Doe" → "JD", "Ada" → "AD". Split by code point, so a name starting
|
|
70
|
+
with an emoji or an astral letter is not cut in half. */
|
|
71
|
+
const initials = computed(() => {
|
|
72
|
+
if (props.fallback) return props.fallback
|
|
73
|
+
const words = (props.alt ?? "").trim().split(/\s+/).filter(Boolean)
|
|
74
|
+
if (!words.length) return "?"
|
|
75
|
+
const first = Array.from(words[0])
|
|
76
|
+
const letters =
|
|
77
|
+
words.length > 1 ? [first[0], Array.from(words[words.length - 1])[0]] : first.slice(0, 2)
|
|
78
|
+
return letters.join("").toUpperCase()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// A consumer's `size-20` must beat the preset's `size-9`, not race it.
|
|
82
|
+
const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
83
|
+
avatar({ size: props.pixelSize ? undefined : props.size, shape: props.shape, ring: props.ring }),
|
|
70
84
|
)
|
|
71
85
|
|
|
72
86
|
// reka's AvatarImage resolves the image load on the client, so SSR renders the
|
|
@@ -83,13 +97,13 @@ onMounted(() => {
|
|
|
83
97
|
|
|
84
98
|
<template>
|
|
85
99
|
<LpContextMenu v-if="menuItems?.length" :items="menuItems">
|
|
86
|
-
<AvatarRoot :class="
|
|
87
|
-
<AvatarImage v-if="src && mounted" :src="src" :alt="alt" class="size-full object-cover" />
|
|
100
|
+
<AvatarRoot :class="rootClass" :style="sizeStyle" v-bind="rest">
|
|
101
|
+
<AvatarImage v-if="src && mounted" :src="src" :alt="alt" class="size-full object-cover animate-[fade-in_var(--duration-medium)_ease] motion-reduce:animate-none" />
|
|
88
102
|
<AvatarFallback>{{ initials }}</AvatarFallback>
|
|
89
103
|
</AvatarRoot>
|
|
90
104
|
</LpContextMenu>
|
|
91
|
-
<AvatarRoot v-else :class="
|
|
92
|
-
<AvatarImage v-if="src" :src="src" :alt="alt" class="size-full object-cover" />
|
|
105
|
+
<AvatarRoot v-else :class="rootClass" :style="sizeStyle" v-bind="rest">
|
|
106
|
+
<AvatarImage v-if="src && mounted" :src="src" :alt="alt" class="size-full object-cover animate-[fade-in_var(--duration-medium)_ease] motion-reduce:animate-none" />
|
|
93
107
|
<AvatarFallback>{{ initials }}</AvatarFallback>
|
|
94
108
|
</AvatarRoot>
|
|
95
109
|
</template>
|
|
@@ -6,32 +6,51 @@ export default { inheritAttrs: false }
|
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<script setup lang="ts">
|
|
9
|
-
import type
|
|
9
|
+
import { computed, type Component } from "vue"
|
|
10
10
|
import LpIcon from "./LpIcon.vue"
|
|
11
|
+
import { useLabels, useLinkComponent } from "../composables/useLpConfig"
|
|
11
12
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
13
|
+
import { resolveLink } from "./link"
|
|
12
14
|
|
|
13
15
|
export interface Crumb {
|
|
14
16
|
label: string
|
|
15
17
|
href?: string
|
|
16
|
-
/** Destination for router links;
|
|
18
|
+
/** Destination for router links; rendered through the link component. */
|
|
17
19
|
to?: string | Record<string, unknown>
|
|
18
20
|
/** Leading icon, for crumbs that carry a glyph (a home root, a section). */
|
|
19
21
|
icon?: string
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{ as: "a" },
|
|
32
|
-
)
|
|
24
|
+
const props = defineProps<{
|
|
25
|
+
items: Crumb[]
|
|
26
|
+
/**
|
|
27
|
+
* Component for linked crumbs. By default `to` and internal `href` use the
|
|
28
|
+
* link component from provideLpConfig and other hrefs a plain <a>.
|
|
29
|
+
*/
|
|
30
|
+
as?: string | Component
|
|
31
|
+
labels?: Partial<{ nav: string }>
|
|
32
|
+
}>()
|
|
33
33
|
defineEmits<{ (e: "navigate", item: Crumb, index: number): void }>()
|
|
34
34
|
|
|
35
|
+
const text = useLabels("breadcrumbs", { nav: "Breadcrumb" }, () => props.labels)
|
|
36
|
+
const linkComponent = useLinkComponent()
|
|
37
|
+
|
|
38
|
+
function linkOf(item: Crumb) {
|
|
39
|
+
if (props.as && props.as !== "a") {
|
|
40
|
+
if (item.to != null) return { is: props.as, props: { to: item.to } }
|
|
41
|
+
return item.href != null ? { is: "a", props: { href: item.href } } : null
|
|
42
|
+
}
|
|
43
|
+
return resolveLink(props.as === "a" ? "a" : linkComponent.value, { to: item.to, href: item.href })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const crumbs = computed(() =>
|
|
47
|
+
props.items.map((item, i) => ({
|
|
48
|
+
item,
|
|
49
|
+
last: i === props.items.length - 1,
|
|
50
|
+
link: i === props.items.length - 1 ? null : linkOf(item),
|
|
51
|
+
})),
|
|
52
|
+
)
|
|
53
|
+
|
|
35
54
|
// The trail sets its own gap, which a dense header bar needs to reduce.
|
|
36
55
|
const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
37
56
|
"flex items-center gap-1.5 text-sm",
|
|
@@ -39,35 +58,40 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
39
58
|
</script>
|
|
40
59
|
|
|
41
60
|
<template>
|
|
42
|
-
<nav
|
|
43
|
-
:class="rootClass"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
v-if="i > 0"
|
|
48
|
-
name="lucide:chevron-right"
|
|
49
|
-
:size="14"
|
|
50
|
-
class="text-muted"
|
|
51
|
-
/>
|
|
52
|
-
<component
|
|
53
|
-
:is="as"
|
|
54
|
-
v-if="(item.href || item.to) && i < items.length - 1"
|
|
55
|
-
:href="as === 'a' ? item.href : undefined"
|
|
56
|
-
:to="as === 'a' ? undefined : item.to"
|
|
57
|
-
class="inline-flex items-center gap-1.5 rounded-xs text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
|
|
58
|
-
@click="$emit('navigate', item, i)"
|
|
59
|
-
>
|
|
60
|
-
<LpIcon v-if="item.icon" :name="item.icon" :size="14" />
|
|
61
|
-
{{ item.label }}
|
|
62
|
-
</component>
|
|
63
|
-
<span
|
|
64
|
-
v-else
|
|
61
|
+
<nav :aria-label="text.nav" v-bind="rest">
|
|
62
|
+
<ol :class="rootClass">
|
|
63
|
+
<li
|
|
64
|
+
v-for="({ item, last, link }, i) in crumbs"
|
|
65
|
+
:key="i"
|
|
65
66
|
class="inline-flex items-center gap-1.5"
|
|
66
|
-
:class="i === items.length - 1 ? 'text-ink' : 'text-muted'"
|
|
67
67
|
>
|
|
68
|
-
<LpIcon
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
<LpIcon
|
|
69
|
+
v-if="i > 0"
|
|
70
|
+
name="lucide:chevron-right"
|
|
71
|
+
:size="14"
|
|
72
|
+
class="text-muted"
|
|
73
|
+
aria-hidden="true"
|
|
74
|
+
/>
|
|
75
|
+
<component
|
|
76
|
+
:is="link.is"
|
|
77
|
+
v-if="link"
|
|
78
|
+
v-bind="link.props"
|
|
79
|
+
class="inline-flex items-center gap-1.5 rounded-xs text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
|
|
80
|
+
@click="$emit('navigate', item, i)"
|
|
81
|
+
>
|
|
82
|
+
<LpIcon v-if="item.icon" :name="item.icon" :size="14" />
|
|
83
|
+
{{ item.label }}
|
|
84
|
+
</component>
|
|
85
|
+
<span
|
|
86
|
+
v-else
|
|
87
|
+
class="inline-flex items-center gap-1.5"
|
|
88
|
+
:class="last ? 'text-ink' : 'text-muted'"
|
|
89
|
+
:aria-current="last ? 'page' : undefined"
|
|
90
|
+
>
|
|
91
|
+
<LpIcon v-if="item.icon" :name="item.icon" :size="14" />
|
|
92
|
+
{{ item.label }}
|
|
93
|
+
</span>
|
|
94
|
+
</li>
|
|
95
|
+
</ol>
|
|
72
96
|
</nav>
|
|
73
97
|
</template>
|