@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
|
@@ -22,6 +22,7 @@ import { computed, useId } from "vue"
|
|
|
22
22
|
import { usePillTransition } from "../composables/usePillTransition"
|
|
23
23
|
import LpIcon from "./LpIcon.vue"
|
|
24
24
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
25
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
25
26
|
|
|
26
27
|
const props = withDefaults(
|
|
27
28
|
defineProps<{
|
|
@@ -35,22 +36,37 @@ const props = withDefaults(
|
|
|
35
36
|
/** Pages shown on each side of the current one in the inner run. */
|
|
36
37
|
siblings?: number
|
|
37
38
|
disabled?: boolean
|
|
39
|
+
labels?: Partial<Record<"nav" | "previous" | "next", string>>
|
|
38
40
|
}>(),
|
|
39
41
|
{ page: 1, pageSize: 10, siblings: 1 },
|
|
40
42
|
)
|
|
41
43
|
|
|
44
|
+
const text = useLabels(
|
|
45
|
+
"pagination",
|
|
46
|
+
{ nav: "Pagination", previous: "Previous page", next: "Next page" },
|
|
47
|
+
() => props.labels,
|
|
48
|
+
)
|
|
49
|
+
|
|
42
50
|
const emit = defineEmits<{
|
|
43
51
|
(e: "update:page", value: number): void
|
|
44
52
|
(e: "change", value: number): void
|
|
45
53
|
}>()
|
|
46
54
|
|
|
47
55
|
const count = computed(() => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
// Guarded so a zero page size or a total still loading never yields an
|
|
57
|
+
// "Infinity" or "NaN" page.
|
|
58
|
+
const n =
|
|
59
|
+
props.pageCount != null
|
|
60
|
+
? props.pageCount
|
|
61
|
+
: props.total != null && props.pageSize > 0
|
|
62
|
+
? Math.ceil(props.total / props.pageSize)
|
|
63
|
+
: 1
|
|
64
|
+
return Number.isFinite(n) ? Math.max(1, Math.floor(n)) : 1
|
|
51
65
|
})
|
|
52
66
|
|
|
53
|
-
const current = computed(() =>
|
|
67
|
+
const current = computed(() =>
|
|
68
|
+
Number.isFinite(props.page) ? Math.min(Math.max(1, Math.floor(props.page)), count.value) : 1,
|
|
69
|
+
)
|
|
54
70
|
|
|
55
71
|
// A fixed run of page slots. Each slot is either a real page or a gap ("…").
|
|
56
72
|
// The number of slots is constant for a given `count`, so the row width is
|
|
@@ -122,12 +138,12 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
122
138
|
<template>
|
|
123
139
|
<nav
|
|
124
140
|
:class="rootClass"
|
|
125
|
-
v-bind="rest" aria-label="
|
|
141
|
+
v-bind="rest" :aria-label="text.nav">
|
|
126
142
|
<button
|
|
127
143
|
type="button"
|
|
128
144
|
:class="[navBase, navArrow]"
|
|
129
145
|
:disabled="disabled || current === 1"
|
|
130
|
-
aria-label="
|
|
146
|
+
:aria-label="text.previous"
|
|
131
147
|
@click="go(current - 1)"
|
|
132
148
|
>
|
|
133
149
|
<LpIcon name="lucide:chevron-left" :size="16" />
|
|
@@ -171,7 +187,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
171
187
|
type="button"
|
|
172
188
|
:class="[navBase, navArrow]"
|
|
173
189
|
:disabled="disabled || current === count"
|
|
174
|
-
aria-label="
|
|
190
|
+
:aria-label="text.next"
|
|
175
191
|
@click="go(current + 1)"
|
|
176
192
|
>
|
|
177
193
|
<LpIcon name="lucide:chevron-right" :size="16" />
|
|
@@ -6,6 +6,7 @@ export default { inheritAttrs: false }
|
|
|
6
6
|
// Password field = LpInput + a show/hide toggle in its trailing slot.
|
|
7
7
|
// No duplicated field styling — the shell lives in LpInput.
|
|
8
8
|
import { ref } from "vue"
|
|
9
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
9
10
|
import LpIcon from "./LpIcon.vue"
|
|
10
11
|
import LpInput from "./LpInput.vue"
|
|
11
12
|
|
|
@@ -23,6 +24,7 @@ defineProps<{
|
|
|
23
24
|
}>()
|
|
24
25
|
defineEmits<{ (e: "update:modelValue", value: string): void }>()
|
|
25
26
|
|
|
27
|
+
const text = useLabels("passwordInput", { show: "Show password", hide: "Hide password" })
|
|
26
28
|
const visible = ref(false)
|
|
27
29
|
const inner = ref<InstanceType<typeof LpInput> | null>(null)
|
|
28
30
|
defineExpose({
|
|
@@ -35,6 +37,7 @@ defineExpose({
|
|
|
35
37
|
<template>
|
|
36
38
|
<LpInput
|
|
37
39
|
ref="inner"
|
|
40
|
+
v-bind="$attrs"
|
|
38
41
|
:model-value="modelValue"
|
|
39
42
|
:type="visible ? 'text' : 'password'"
|
|
40
43
|
:placeholder="placeholder"
|
|
@@ -51,7 +54,7 @@ defineExpose({
|
|
|
51
54
|
<button
|
|
52
55
|
type="button"
|
|
53
56
|
class="group rounded-md p-1 text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:text-ink"
|
|
54
|
-
:aria-label="visible ?
|
|
57
|
+
:aria-label="visible ? text.hide : text.show"
|
|
55
58
|
@click="visible = !visible"
|
|
56
59
|
>
|
|
57
60
|
<LpIcon
|
|
@@ -13,8 +13,14 @@ export default { inheritAttrs: false }
|
|
|
13
13
|
* match wins). The model is the full string ("+380501234567"); a `change` event
|
|
14
14
|
* also carries the parsed { country, dialCode, number } for forms that want it.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* Numbers are formatted as they are typed ("+380 50 123 4567") with
|
|
17
|
+
* libphonenumber-js, loaded on first use (see phone.ts). Digits past the
|
|
18
|
+
* longest number the country allows are dropped with a passing note, and a
|
|
19
|
+
* number that cannot be right is marked once the field is left, with the reason
|
|
20
|
+
* floating under it. v-model stays the formatted text the field shows; `change`
|
|
21
|
+
* also carries `e164` ("+380501234567"), `possible` and `valid`, which
|
|
22
|
+
* is what a form should store. Until the library arrives the field takes digits
|
|
23
|
+
* as before, unformatted.
|
|
18
24
|
*/
|
|
19
25
|
import {
|
|
20
26
|
ComboboxAnchor,
|
|
@@ -27,11 +33,24 @@ import {
|
|
|
27
33
|
ComboboxTrigger,
|
|
28
34
|
ComboboxViewport,
|
|
29
35
|
} from "reka-ui"
|
|
30
|
-
import { computed, onMounted, ref } from "vue"
|
|
36
|
+
import { computed, onMounted, ref, shallowRef, useAttrs } from "vue"
|
|
37
|
+
import { useField, useFieldControl } from "../composables/useField"
|
|
38
|
+
import { useFieldNotice, type FieldNoticeOptions } from "../composables/useFieldNotice"
|
|
31
39
|
import { useInputFilter } from "../composables/useInputFilter"
|
|
32
|
-
import {
|
|
40
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
41
|
+
import { type Country, filterCountries, flagEmoji, loadCountries, matchCountryByValue } from "./countries"
|
|
33
42
|
import { OPTION_ITEM, POPOVER_PANEL } from "./dropdown"
|
|
43
|
+
import LpFieldNotice from "./LpFieldNotice.vue"
|
|
34
44
|
import LpIcon from "./LpIcon.vue"
|
|
45
|
+
import {
|
|
46
|
+
caretAfterDigits,
|
|
47
|
+
digitsBefore,
|
|
48
|
+
digitsOf,
|
|
49
|
+
formatPhone,
|
|
50
|
+
loadPhoneExamples,
|
|
51
|
+
loadPhoneLib,
|
|
52
|
+
type PhoneLib,
|
|
53
|
+
} from "./phone"
|
|
35
54
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
36
55
|
|
|
37
56
|
export interface PhoneDetail {
|
|
@@ -41,6 +60,17 @@ export interface PhoneDetail {
|
|
|
41
60
|
dialCode: string
|
|
42
61
|
/** National part (digits after the dial code). */
|
|
43
62
|
number: string
|
|
63
|
+
/** Normalised "+380501234567"; empty when there are no digits. */
|
|
64
|
+
e164: string
|
|
65
|
+
/** The length fits the country. False until the formatter has loaded. */
|
|
66
|
+
possible: boolean
|
|
67
|
+
/** A real number for the country. False until the formatter has loaded. */
|
|
68
|
+
valid: boolean
|
|
69
|
+
/**
|
|
70
|
+
* "MOBILE", "FIXED_LINE"… when known. The `min` metadata the field loads does
|
|
71
|
+
* not carry number types, so this stays undefined unless that changes.
|
|
72
|
+
*/
|
|
73
|
+
type?: string
|
|
44
74
|
}
|
|
45
75
|
|
|
46
76
|
const props = withDefaults(
|
|
@@ -51,6 +81,7 @@ const props = withDefaults(
|
|
|
51
81
|
only?: string[]
|
|
52
82
|
/** Default country (ISO2) used when the field is empty. */
|
|
53
83
|
defaultCountry?: string
|
|
84
|
+
/** Unset shows an example number for the active country. */
|
|
54
85
|
placeholder?: string
|
|
55
86
|
disabled?: boolean
|
|
56
87
|
invalid?: boolean
|
|
@@ -59,7 +90,7 @@ const props = withDefaults(
|
|
|
59
90
|
searchPlaceholder?: string
|
|
60
91
|
}>(),
|
|
61
92
|
{
|
|
62
|
-
placeholder:
|
|
93
|
+
placeholder: undefined,
|
|
63
94
|
size: "md",
|
|
64
95
|
emptyText: "No country",
|
|
65
96
|
searchPlaceholder: "Search country…",
|
|
@@ -74,7 +105,9 @@ const emit = defineEmits<{
|
|
|
74
105
|
// Phone keys only: digits, "+", spaces and the usual separators. Free typing of
|
|
75
106
|
// "+code" stays possible; letters and junk are blocked at the source.
|
|
76
107
|
const PHONE_CHARS = /[\d+()\-\s]/
|
|
77
|
-
const { onBeforeInput, onPaste } = useInputFilter({
|
|
108
|
+
const { onBeforeInput, onInput: filterInput, onCompositionEnd, onPaste } = useInputFilter({
|
|
109
|
+
restrict: () => PHONE_CHARS,
|
|
110
|
+
})
|
|
78
111
|
|
|
79
112
|
// The dial-code table is code-split; it's empty until the lazy chunk resolves.
|
|
80
113
|
// Everything degrades gracefully meanwhile: digits still parse, the flag shows a
|
|
@@ -100,21 +133,19 @@ const activeCountry = computed<Country | undefined>(
|
|
|
100
133
|
const open = ref(false)
|
|
101
134
|
const query = ref("")
|
|
102
135
|
|
|
103
|
-
const filtered = computed(() =>
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return countries.value.filter(
|
|
107
|
-
(c) => c.name.toLowerCase().includes(q) || c.dial.includes(q.replace(/\D/g, "")),
|
|
108
|
-
)
|
|
109
|
-
})
|
|
136
|
+
const filtered = computed(() => filterCountries(countries.value, query.value))
|
|
137
|
+
|
|
138
|
+
const phoneLib = shallowRef<PhoneLib>()
|
|
110
139
|
|
|
111
140
|
function parse(value: string): PhoneDetail {
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
141
|
+
const formatted = formatPhone(value, phoneLib.value)
|
|
142
|
+
const country =
|
|
143
|
+
(formatted.country && countries.value.find((c) => c.iso2 === formatted.country)) ??
|
|
144
|
+
matchCountryByValue(value, countries.value)
|
|
145
|
+
const digits = digitsOf(formatted.e164)
|
|
146
|
+
const base = { e164: formatted.e164, possible: formatted.possible, valid: formatted.valid, type: formatted.type }
|
|
147
|
+
if (country) return { ...base, country, dialCode: country.dial, number: digits.slice(country.dial.length) }
|
|
148
|
+
return { ...base, country: undefined, dialCode: "", number: digits }
|
|
118
149
|
}
|
|
119
150
|
|
|
120
151
|
function setValue(value: string) {
|
|
@@ -122,8 +153,85 @@ function setValue(value: string) {
|
|
|
122
153
|
emit("change", parse(value))
|
|
123
154
|
}
|
|
124
155
|
|
|
156
|
+
/*
|
|
157
|
+
* Notes float under the field: the LpFormField around it shows them when there
|
|
158
|
+
* is one, otherwise the field shows its own.
|
|
159
|
+
*/
|
|
160
|
+
const notice = useFieldNotice()
|
|
161
|
+
const outerField = useField()
|
|
162
|
+
const ownNotice = computed(() => !outerField?.value.notify)
|
|
163
|
+
function say(message: string, options?: FieldNoticeOptions) {
|
|
164
|
+
if (outerField?.value.notify) outerField.value.notify(message, options)
|
|
165
|
+
else notice.show(message, options)
|
|
166
|
+
}
|
|
167
|
+
function unsay() {
|
|
168
|
+
if (outerField?.value.clearNotice) outerField.value.clearNotice()
|
|
169
|
+
else notice.clear()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Shown invalid after the field was left with a number that cannot be right. */
|
|
173
|
+
const flagged = ref(false)
|
|
174
|
+
const countryName = (iso?: string) =>
|
|
175
|
+
(iso && countries.value.find((c) => c.iso2 === iso)?.name) || ""
|
|
176
|
+
|
|
177
|
+
/*
|
|
178
|
+
* Reformat after every edit and put the caret back after the same number of
|
|
179
|
+
* digits it followed, so spaces the formatter adds or moves never push it
|
|
180
|
+
* around.
|
|
181
|
+
*/
|
|
125
182
|
function onInput(e: Event) {
|
|
126
|
-
|
|
183
|
+
filterInput(e)
|
|
184
|
+
const el = e.target as HTMLInputElement
|
|
185
|
+
const before = digitsBefore(el.value, el.selectionStart ?? el.value.length)
|
|
186
|
+
const result = formatPhone(el.value, phoneLib.value)
|
|
187
|
+
if (el.value !== result.display) {
|
|
188
|
+
el.value = result.display
|
|
189
|
+
const caret = caretAfterDigits(result.display, Math.min(before, digitsOf(result.display).length))
|
|
190
|
+
el.setSelectionRange(caret, caret)
|
|
191
|
+
}
|
|
192
|
+
if (flagged.value) {
|
|
193
|
+
flagged.value = false
|
|
194
|
+
unsay()
|
|
195
|
+
}
|
|
196
|
+
if (result.dropped) {
|
|
197
|
+
const name = countryName(result.country)
|
|
198
|
+
say(
|
|
199
|
+
(name ? text.value.tooLongFor : text.value.tooLong)
|
|
200
|
+
.replace("{country}", name)
|
|
201
|
+
.replace("{max}", String(digitsOf(result.e164).length - (result.callingCode?.length ?? 0))),
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
setValue(result.display)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/*
|
|
208
|
+
* Backspace or Delete next to a space the formatter put there would only remove
|
|
209
|
+
* the space, and formatting would put it straight back: the key would seem
|
|
210
|
+
* dead. The digit beside it goes instead.
|
|
211
|
+
*/
|
|
212
|
+
function onKeyEdit(e: InputEvent) {
|
|
213
|
+
const el = e.target as HTMLInputElement
|
|
214
|
+
const start = el.selectionStart ?? 0
|
|
215
|
+
const end = el.selectionEnd ?? 0
|
|
216
|
+
const backward = e.inputType === "deleteContentBackward"
|
|
217
|
+
const forward = e.inputType === "deleteContentForward"
|
|
218
|
+
if ((!backward && !forward) || start !== end) return
|
|
219
|
+
const value = el.value
|
|
220
|
+
const at = backward ? start - 1 : start
|
|
221
|
+
if (at < 0 || at >= value.length || /\d/.test(value[at]!)) return
|
|
222
|
+
let i = at
|
|
223
|
+
while (i >= 0 && i < value.length && !/\d/.test(value[i]!)) i += backward ? -1 : 1
|
|
224
|
+
if (i < 0 || i >= value.length) return
|
|
225
|
+
e.preventDefault()
|
|
226
|
+
el.value = value.slice(0, i) + value.slice(i + 1)
|
|
227
|
+
const caret = backward ? i : start
|
|
228
|
+
el.setSelectionRange(caret, caret)
|
|
229
|
+
el.dispatchEvent(new Event("input", { bubbles: true }))
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function onBeforeEdit(e: InputEvent) {
|
|
233
|
+
onKeyEdit(e)
|
|
234
|
+
if (!e.defaultPrevented) onBeforeInput(e)
|
|
127
235
|
}
|
|
128
236
|
|
|
129
237
|
/**
|
|
@@ -139,16 +247,60 @@ function chooseCountry(c: Country) {
|
|
|
139
247
|
query.value = ""
|
|
140
248
|
}
|
|
141
249
|
|
|
142
|
-
// Load the dial-code table on mount (lazy chunk), then seed the default
|
|
143
|
-
// country's "+code" into an empty field so the user starts with the prefix in
|
|
144
|
-
// place — but never clobber a value the consumer already provided.
|
|
145
250
|
onMounted(async () => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
251
|
+
const [list, phone] = await Promise.all([loadCountries(), loadPhoneLib().catch(() => undefined)])
|
|
252
|
+
allCountries.value = list
|
|
253
|
+
phoneLib.value = phone
|
|
254
|
+
// A value set before the formatter arrived is shown formatted from now on.
|
|
255
|
+
if (phone && props.modelValue) {
|
|
256
|
+
const display = formatPhone(props.modelValue, phone).display
|
|
257
|
+
if (display !== props.modelValue) setValue(display)
|
|
258
|
+
}
|
|
259
|
+
if (props.placeholder === undefined) examplesFor.value = await loadPhoneExamples().catch(() => undefined)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
/* An example number for the active country, when the consumer gave no text. */
|
|
263
|
+
const examplesFor = shallowRef<Awaited<ReturnType<typeof loadPhoneExamples>>>()
|
|
264
|
+
const shownPlaceholder = computed(() => {
|
|
265
|
+
if (props.placeholder !== undefined) return props.placeholder
|
|
266
|
+
const phone = phoneLib.value
|
|
267
|
+
const iso = activeCountry.value?.iso2
|
|
268
|
+
if (phone && examplesFor.value && iso) {
|
|
269
|
+
try {
|
|
270
|
+
return phone.getExampleNumber(iso as never, examplesFor.value)?.formatInternational() ?? text.value.placeholder
|
|
271
|
+
} catch {
|
|
272
|
+
return text.value.placeholder
|
|
273
|
+
}
|
|
149
274
|
}
|
|
275
|
+
return text.value.placeholder
|
|
150
276
|
})
|
|
151
277
|
|
|
278
|
+
// The default country's "+code" is put in place when the user enters an empty
|
|
279
|
+
// field, and taken back out if they leave without typing a number. Seeding it
|
|
280
|
+
// on mount instead made every untouched optional field submit "+380 ", pass
|
|
281
|
+
// `required`, and read as edited.
|
|
282
|
+
const seeded = computed(() => (fallback.value ? `+${fallback.value.dial} ` : ""))
|
|
283
|
+
function onFocus() {
|
|
284
|
+
if (!props.modelValue && seeded.value) setValue(seeded.value)
|
|
285
|
+
}
|
|
286
|
+
function onBlur() {
|
|
287
|
+
if (seeded.value && props.modelValue?.trim() === seeded.value.trim()) {
|
|
288
|
+
setValue("")
|
|
289
|
+
return
|
|
290
|
+
}
|
|
291
|
+
const result = phoneLib.value && props.modelValue ? formatPhone(props.modelValue, phoneLib.value) : undefined
|
|
292
|
+
if (!result || result.valid || digitsOf(result.e164).length <= (activeCountry.value?.dial.length ?? 0)) return
|
|
293
|
+
flagged.value = true
|
|
294
|
+
const name = countryName(result.country) || activeCountry.value?.name || ""
|
|
295
|
+
const reason =
|
|
296
|
+
result.problem === "too-short"
|
|
297
|
+
? (name ? text.value.tooShortFor : text.value.tooShort)
|
|
298
|
+
: result.problem === "invalid-country"
|
|
299
|
+
? text.value.unknownCountry
|
|
300
|
+
: text.value.invalidNumber
|
|
301
|
+
say(reason.replace("{country}", name), { tone: "danger", duration: 0 })
|
|
302
|
+
}
|
|
303
|
+
|
|
152
304
|
const sizeClass = {
|
|
153
305
|
sm: "h-(--size-control-sm) text-xs",
|
|
154
306
|
md: "h-(--size-control-md) text-sm",
|
|
@@ -157,23 +309,47 @@ const sizeClass = {
|
|
|
157
309
|
|
|
158
310
|
// The control is full-width with its own frame — the two things a consumer
|
|
159
311
|
// placing it in a narrow column has to change, so `class="w-48"` must win.
|
|
312
|
+
// style lays out the frame; every other attr (name, autocomplete, @blur…)
|
|
313
|
+
// belongs to the number <input>.
|
|
314
|
+
const attrs = useAttrs()
|
|
315
|
+
const field = useFieldControl(() => ({
|
|
316
|
+
wrapped: false,
|
|
317
|
+
id: typeof attrs.id === "string" ? attrs.id : undefined,
|
|
318
|
+
invalid: props.invalid || flagged.value,
|
|
319
|
+
disabled: props.disabled,
|
|
320
|
+
}))
|
|
321
|
+
const text = useLabels("phoneInput", {
|
|
322
|
+
selectCountry: "Select country",
|
|
323
|
+
placeholder: "Phone number",
|
|
324
|
+
tooLong: "A phone number has at most {max} digits",
|
|
325
|
+
tooLongFor: "Numbers in {country} have at most {max} digits after the code",
|
|
326
|
+
tooShort: "This number is too short",
|
|
327
|
+
tooShortFor: "Too short for {country}",
|
|
328
|
+
invalidNumber: "Not a valid phone number",
|
|
329
|
+
unknownCountry: "Unknown country code",
|
|
330
|
+
})
|
|
331
|
+
const noticeId = `${field.value.id ?? "lp-phone"}-notice`
|
|
160
332
|
const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
161
333
|
[
|
|
162
|
-
"flex w-full items-stretch rounded-control border bg-surface-soft text-ink transition-colors duration-[var(--duration-fast)] focus-within:ring-2 focus-within:ring-ring",
|
|
334
|
+
"relative flex w-full items-stretch rounded-control border bg-surface-soft text-ink transition-colors duration-[var(--duration-fast)] focus-within:ring-2 focus-within:ring-ring",
|
|
163
335
|
sizeClass[props.size],
|
|
164
|
-
|
|
336
|
+
field.value.invalid
|
|
165
337
|
? "border-danger focus-within:border-danger focus-within:ring-danger-soft"
|
|
166
338
|
: "border-line focus-within:border-brand",
|
|
167
|
-
|
|
339
|
+
field.value.disabled ? "cursor-not-allowed opacity-55" : "",
|
|
168
340
|
].join(" "),
|
|
169
341
|
)
|
|
342
|
+
const inputAttrs = computed(() => {
|
|
343
|
+
const { style: _s, ...others } = rest.value
|
|
344
|
+
return others
|
|
345
|
+
})
|
|
170
346
|
</script>
|
|
171
347
|
|
|
172
348
|
<template>
|
|
173
349
|
<div
|
|
174
350
|
data-lp-ring-owner
|
|
175
351
|
:class="rootClass"
|
|
176
|
-
|
|
352
|
+
:style="attrs.style as never"
|
|
177
353
|
>
|
|
178
354
|
<!-- reset-search-term-on-{select,blur}: as in LpSelect, the trigger shows
|
|
179
355
|
the selection and the box only filters, so reka seeding the chosen value
|
|
@@ -184,15 +360,15 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
184
360
|
:ignore-filter="true"
|
|
185
361
|
:reset-search-term-on-select="false"
|
|
186
362
|
:reset-search-term-on-blur="false"
|
|
187
|
-
:disabled="disabled"
|
|
363
|
+
:disabled="field.disabled"
|
|
188
364
|
class="flex"
|
|
189
365
|
>
|
|
190
366
|
<ComboboxAnchor as-child>
|
|
191
367
|
<ComboboxTrigger
|
|
192
368
|
type="button"
|
|
193
|
-
:disabled="disabled"
|
|
194
|
-
class="flex shrink-0 items-center gap-1 rounded-l-control border-r border-line px-2.5 outline-none hover:bg-
|
|
195
|
-
aria-label="
|
|
369
|
+
:disabled="field.disabled"
|
|
370
|
+
class="flex shrink-0 items-center gap-1 rounded-l-control border-r border-line px-2.5 outline-none hover:bg-ink/5 data-[state=open]:bg-ink/5"
|
|
371
|
+
:aria-label="text.selectCountry"
|
|
196
372
|
>
|
|
197
373
|
<span class="text-base leading-none">{{ activeCountry ? flagEmoji(activeCountry.iso2) : "🌐" }}</span>
|
|
198
374
|
<LpIcon
|
|
@@ -205,6 +381,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
205
381
|
|
|
206
382
|
<ComboboxPortal>
|
|
207
383
|
<ComboboxContent
|
|
384
|
+
:collision-padding="8"
|
|
208
385
|
position="popper"
|
|
209
386
|
align="start"
|
|
210
387
|
:side-offset="6"
|
|
@@ -247,17 +424,25 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
247
424
|
</ComboboxRoot>
|
|
248
425
|
|
|
249
426
|
<input
|
|
250
|
-
:value="modelValue"
|
|
251
|
-
:placeholder="placeholder"
|
|
252
|
-
:disabled="disabled"
|
|
253
|
-
:aria-invalid="invalid || undefined"
|
|
254
427
|
type="tel"
|
|
255
428
|
inputmode="tel"
|
|
256
429
|
autocomplete="tel"
|
|
430
|
+
v-bind="inputAttrs"
|
|
431
|
+
:id="field.id"
|
|
432
|
+
:value="modelValue"
|
|
433
|
+
:placeholder="shownPlaceholder"
|
|
434
|
+
:disabled="field.disabled"
|
|
435
|
+
:required="field.required || undefined"
|
|
436
|
+
:aria-invalid="field.invalid || undefined"
|
|
437
|
+
:aria-describedby="[field.describedBy, notice.message.value ? noticeId : ''].filter(Boolean).join(' ') || undefined"
|
|
257
438
|
class="lp-autofill min-w-0 flex-1 self-stretch bg-transparent px-3 outline-none placeholder:text-muted"
|
|
258
|
-
@beforeinput="
|
|
439
|
+
@beforeinput="onBeforeEdit"
|
|
259
440
|
@paste="onPaste"
|
|
441
|
+
@compositionend="onCompositionEnd"
|
|
260
442
|
@input="onInput"
|
|
443
|
+
@focus="onFocus"
|
|
444
|
+
@blur="onBlur"
|
|
261
445
|
/>
|
|
446
|
+
<LpFieldNotice v-if="ownNotice" :notice="notice" :id="noticeId" />
|
|
262
447
|
</div>
|
|
263
448
|
</template>
|