@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
|
@@ -16,18 +16,52 @@ export default { inheritAttrs: false }
|
|
|
16
16
|
* eased box, so the thing being typed into never moves.
|
|
17
17
|
*/
|
|
18
18
|
import { Label } from "reka-ui"
|
|
19
|
-
import { useId } from "vue"
|
|
19
|
+
import { computed, useId, useSlots } from "vue"
|
|
20
|
+
import LpFieldNotice from "./LpFieldNotice.vue"
|
|
20
21
|
import LpShift from "./LpShift.vue"
|
|
22
|
+
import { fieldMessageId, provideField } from "../composables/useField"
|
|
23
|
+
import { useFieldNotice } from "../composables/useFieldNotice"
|
|
21
24
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
22
25
|
|
|
23
|
-
defineProps<{
|
|
26
|
+
const props = defineProps<{
|
|
24
27
|
label?: string
|
|
25
28
|
hint?: string
|
|
26
29
|
error?: string
|
|
27
30
|
required?: boolean
|
|
31
|
+
/** Id of the control; generated when unset. The label points at it. */
|
|
32
|
+
id?: string
|
|
33
|
+
invalid?: boolean
|
|
34
|
+
disabled?: boolean
|
|
28
35
|
}>()
|
|
29
36
|
|
|
30
|
-
const
|
|
37
|
+
const generated = useId()
|
|
38
|
+
const id = computed(() => props.id ?? generated)
|
|
39
|
+
const messageId = computed(() => fieldMessageId(id.value))
|
|
40
|
+
const slots = useSlots()
|
|
41
|
+
const hasMessage = computed(() => !!props.error || !!props.hint || !!slots.hint)
|
|
42
|
+
const notice = useFieldNotice()
|
|
43
|
+
const noticeId = computed(() => `${id.value}-notice`)
|
|
44
|
+
|
|
45
|
+
// The control inside reads its id, description and states from here, so the
|
|
46
|
+
// label, the message line and aria-invalid stay tied to it without the
|
|
47
|
+
// consumer threading ids by hand.
|
|
48
|
+
provideField(
|
|
49
|
+
computed(() => ({
|
|
50
|
+
id: id.value,
|
|
51
|
+
labelId: props.label ? `${id.value}-label` : undefined,
|
|
52
|
+
describedBy:
|
|
53
|
+
[hasMessage.value ? messageId.value : "", notice.message.value ? noticeId.value : ""]
|
|
54
|
+
.filter(Boolean)
|
|
55
|
+
.join(" ") || undefined,
|
|
56
|
+
invalid: !!props.invalid || !!props.error,
|
|
57
|
+
required: !!props.required,
|
|
58
|
+
disabled: !!props.disabled,
|
|
59
|
+
notify: notice.show,
|
|
60
|
+
clearNotice: notice.clear,
|
|
61
|
+
})),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
defineExpose({ notify: notice.show, clearNotice: notice.clear })
|
|
31
65
|
|
|
32
66
|
// The field stacks label and control with a fixed gap; a denser form needs that gap smaller.
|
|
33
67
|
const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
@@ -44,12 +78,18 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
44
78
|
<div
|
|
45
79
|
:class="rootClass"
|
|
46
80
|
v-bind="rest">
|
|
47
|
-
<Label v-if="label" :for="id" class="text-sm font-medium text-ink">
|
|
81
|
+
<Label v-if="label" :id="`${id}-label`" :for="id" class="text-sm font-medium text-ink">
|
|
48
82
|
{{ label }}
|
|
49
83
|
<span v-if="required" class="text-danger">*</span>
|
|
50
84
|
</Label>
|
|
51
85
|
|
|
52
|
-
|
|
86
|
+
<!-- The note from `notify` floats from here, over the message line below,
|
|
87
|
+
so raising one never moves the form; the line fades out under it
|
|
88
|
+
rather than showing through. -->
|
|
89
|
+
<div class="relative">
|
|
90
|
+
<slot :id="id" :describedBy="hasMessage ? messageId : undefined" />
|
|
91
|
+
<LpFieldNotice :notice="notice" :id="noticeId" />
|
|
92
|
+
</div>
|
|
53
93
|
|
|
54
94
|
<!-- Swapping hint→error changes the text but not the height, so the eased
|
|
55
95
|
box only ever moves when a message appears or clears.
|
|
@@ -60,12 +100,24 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
60
100
|
leave every message-less field a row taller than before — and would be
|
|
61
101
|
a step the easing could not cover, since it exists even at height 0. -->
|
|
62
102
|
<LpShift axis="height">
|
|
63
|
-
<p
|
|
103
|
+
<p
|
|
104
|
+
v-if="error"
|
|
105
|
+
:id="messageId"
|
|
106
|
+
class="pt-1.5 text-xs text-danger transition-opacity duration-fast"
|
|
107
|
+
:class="notice.message.value ? 'opacity-0' : ''"
|
|
108
|
+
>
|
|
109
|
+
{{ error }}
|
|
110
|
+
</p>
|
|
64
111
|
<!-- The hint is a slot as well as a prop: most are a sentence, but some
|
|
65
112
|
carry a link or a unit in <code>, and a prop can only be text. Callers
|
|
66
113
|
had been reaching for `#hint` on the assumption it existed — it
|
|
67
114
|
rendered nothing, so the explanation silently went missing. -->
|
|
68
|
-
<p
|
|
115
|
+
<p
|
|
116
|
+
v-else-if="hint || $slots.hint"
|
|
117
|
+
:id="messageId"
|
|
118
|
+
class="pt-1.5 text-xs text-muted transition-opacity duration-fast"
|
|
119
|
+
:class="notice.message.value ? 'opacity-0' : ''"
|
|
120
|
+
>
|
|
69
121
|
<slot name="hint">{{ hint }}</slot>
|
|
70
122
|
</p>
|
|
71
123
|
</LpShift>
|
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
* inline <svg> synchronously from getIcon() so the server and client produce
|
|
9
9
|
* identical markup. The <Icon> component only resolves icons after mount, which
|
|
10
10
|
* left empty SSR placeholders and caused hydration mismatches. We fall back to
|
|
11
|
-
* <Icon> only for icons that aren't registered
|
|
11
|
+
* <Icon> only for non-lucide icons that aren't registered (fetched on demand).
|
|
12
|
+
* A lucide icon outside the kit's own set loads from the bundled collection
|
|
13
|
+
* (see icons.ts); until then an empty box of the same size holds its place.
|
|
12
14
|
*/
|
|
13
|
-
import { computed } from "vue"
|
|
15
|
+
import { computed, watchEffect } from "vue"
|
|
14
16
|
import { Icon, getIcon } from "@iconify/vue"
|
|
15
|
-
import
|
|
17
|
+
import { iconsVersion, requestIcon } from "../icons"
|
|
16
18
|
|
|
17
19
|
const props = defineProps<{
|
|
18
20
|
/** iconify name, e.g. "lucide:check". Bare names assume the lucide set. */
|
|
@@ -25,7 +27,15 @@ function resolve(name: string): string {
|
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
const fullName = computed(() => resolve(props.name))
|
|
28
|
-
const data = computed(() =>
|
|
30
|
+
const data = computed(() => {
|
|
31
|
+
void iconsVersion.value
|
|
32
|
+
return getIcon(fullName.value)
|
|
33
|
+
})
|
|
34
|
+
const lucide = computed(() => fullName.value.startsWith("lucide:"))
|
|
35
|
+
|
|
36
|
+
watchEffect(() => {
|
|
37
|
+
if (!data.value && lucide.value) requestIcon(fullName.value)
|
|
38
|
+
})
|
|
29
39
|
const dim = computed(() => props.size ?? "1em")
|
|
30
40
|
</script>
|
|
31
41
|
|
|
@@ -39,6 +49,14 @@ const dim = computed(() => props.size ?? "1em")
|
|
|
39
49
|
aria-hidden="true"
|
|
40
50
|
v-html="data.body"
|
|
41
51
|
/>
|
|
52
|
+
<svg
|
|
53
|
+
v-else-if="lucide"
|
|
54
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
55
|
+
:width="dim"
|
|
56
|
+
:height="dim"
|
|
57
|
+
viewBox="0 0 24 24"
|
|
58
|
+
aria-hidden="true"
|
|
59
|
+
/>
|
|
42
60
|
<Icon
|
|
43
61
|
v-else
|
|
44
62
|
:icon="fullName"
|
|
@@ -24,6 +24,8 @@ export default { inheritAttrs: false }
|
|
|
24
24
|
* already know from every avatar cropper.
|
|
25
25
|
*/
|
|
26
26
|
import { computed, onBeforeUnmount, ref, shallowRef, watch } from "vue"
|
|
27
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
28
|
+
import { coverScale, cropGeometry, isSideways } from "./imageCrop"
|
|
27
29
|
import { useHotkeys } from "../composables/useHotkeys"
|
|
28
30
|
import { useReveal } from "../composables/useReveal"
|
|
29
31
|
import { useZoomPan } from "../composables/useZoomPan"
|
|
@@ -81,15 +83,18 @@ const props = withDefaults(
|
|
|
81
83
|
// mount. An editor inside a dialog that opens a minute later still draws.
|
|
82
84
|
const { el: revealAnchor, revealed } = useReveal({ immediate: () => !props.animate })
|
|
83
85
|
|
|
84
|
-
const text =
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
const text = useLabels(
|
|
87
|
+
"imageEditor",
|
|
88
|
+
{
|
|
89
|
+
rotateLeft: "Rotate left",
|
|
90
|
+
rotateRight: "Rotate right",
|
|
91
|
+
zoomIn: "Zoom in",
|
|
92
|
+
zoomOut: "Zoom out",
|
|
93
|
+
reset: "Reset",
|
|
94
|
+
canvas: "Crop area — arrows pan, +/− zoom, R rotates, Alt+wheel turns freely",
|
|
95
|
+
},
|
|
96
|
+
() => props.labels,
|
|
97
|
+
)
|
|
93
98
|
|
|
94
99
|
const ASPECTS: Record<string, number | null> = {
|
|
95
100
|
free: null,
|
|
@@ -105,7 +110,24 @@ const rotation = ref(0)
|
|
|
105
110
|
const viewport = ref<HTMLElement | null>(null)
|
|
106
111
|
const zoom = useZoomPan({ min: 1, max: 6 })
|
|
107
112
|
|
|
108
|
-
|
|
113
|
+
const frameSize = ref<{ width: number; height: number } | null>(null)
|
|
114
|
+
let frameObserver: ResizeObserver | null = null
|
|
115
|
+
|
|
116
|
+
watch(viewport, (el) => {
|
|
117
|
+
zoom.setViewport(el)
|
|
118
|
+
frameObserver?.disconnect()
|
|
119
|
+
frameObserver = null
|
|
120
|
+
if (!el) return
|
|
121
|
+
// Fractional, not clientWidth: a rounded-down frame left a hairline of
|
|
122
|
+
// background showing along the edges of a turned picture.
|
|
123
|
+
frameSize.value = { width: el.clientWidth, height: el.clientHeight }
|
|
124
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
125
|
+
frameObserver = new ResizeObserver(([entry]) => {
|
|
126
|
+
frameSize.value = { width: entry.contentRect.width, height: entry.contentRect.height }
|
|
127
|
+
})
|
|
128
|
+
frameObserver.observe(el)
|
|
129
|
+
}
|
|
130
|
+
})
|
|
109
131
|
|
|
110
132
|
/** Which way up it ends, 0-270, whatever the accumulated angle. */
|
|
111
133
|
const upright = computed(() => ((rotation.value % 360) + 360) % 360)
|
|
@@ -114,14 +136,24 @@ const upright = computed(() => ((rotation.value % 360) + 360) % 360)
|
|
|
114
136
|
* back to where it started is not an edit, however far the dial travelled. */
|
|
115
137
|
const edited = computed(() => upright.value !== 0 || zoom.scale.value !== 1)
|
|
116
138
|
|
|
139
|
+
// Each file gets a generation: a slow decode of an earlier file must neither
|
|
140
|
+
// replace the current picture nor leak the bitmap it produced.
|
|
141
|
+
let generation = 0
|
|
142
|
+
|
|
117
143
|
watch(
|
|
118
144
|
() => props.file,
|
|
119
145
|
async (file) => {
|
|
120
146
|
revoke()
|
|
121
147
|
rotation.value = 0
|
|
122
148
|
zoom.reset()
|
|
149
|
+
const mine = ++generation
|
|
123
150
|
objectUrl.value = URL.createObjectURL(file)
|
|
124
|
-
|
|
151
|
+
const decoded = await decode(file, objectUrl.value)
|
|
152
|
+
if (mine !== generation) {
|
|
153
|
+
if (decoded instanceof ImageBitmap) decoded.close()
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
source.value = decoded
|
|
125
157
|
},
|
|
126
158
|
{ immediate: true },
|
|
127
159
|
)
|
|
@@ -172,7 +204,11 @@ function revoke() {
|
|
|
172
204
|
source.value = null
|
|
173
205
|
}
|
|
174
206
|
|
|
175
|
-
onBeforeUnmount(
|
|
207
|
+
onBeforeUnmount(() => {
|
|
208
|
+
generation++
|
|
209
|
+
revoke()
|
|
210
|
+
frameObserver?.disconnect()
|
|
211
|
+
})
|
|
176
212
|
|
|
177
213
|
function rotate(by: number) {
|
|
178
214
|
// Accumulated, never wrapped to 0-359. Normalising made the fourth quarter
|
|
@@ -218,7 +254,9 @@ const boxRatio = computed(() => {
|
|
|
218
254
|
if (ratio) return ratio
|
|
219
255
|
const size = sourceSize.value
|
|
220
256
|
if (!size) return 1
|
|
221
|
-
|
|
257
|
+
// Snapped to the nearest quarter: a few degrees of levelling must not flip a
|
|
258
|
+
// landscape frame to portrait.
|
|
259
|
+
const turned = isSideways(rotation.value)
|
|
222
260
|
const w = turned ? size.height : size.width
|
|
223
261
|
const h = turned ? size.width : size.height
|
|
224
262
|
return w / h
|
|
@@ -226,7 +264,31 @@ const boxRatio = computed(() => {
|
|
|
226
264
|
|
|
227
265
|
const frameStyle = computed(() => ({ aspectRatio: String(boxRatio.value) }))
|
|
228
266
|
|
|
267
|
+
/*
|
|
268
|
+
* The picture is sized to cover the frame AFTER its turn, and centred. Filling
|
|
269
|
+
* the frame with object-cover and then rotating the element left empty bands
|
|
270
|
+
* on any non-square frame turned sideways, and at a free angle bare corners.
|
|
271
|
+
*/
|
|
272
|
+
const sized = computed(() => {
|
|
273
|
+
const size = sourceSize.value
|
|
274
|
+
const frame = frameSize.value
|
|
275
|
+
if (!size || !frame || !frame.width || !frame.height) return null
|
|
276
|
+
const cover = coverScale(size.width, size.height, frame.width, frame.height, rotation.value)
|
|
277
|
+
const width = size.width * cover
|
|
278
|
+
const height = size.height * cover
|
|
279
|
+
return {
|
|
280
|
+
width: `${width}px`,
|
|
281
|
+
height: `${height}px`,
|
|
282
|
+
left: "50%",
|
|
283
|
+
top: "50%",
|
|
284
|
+
marginLeft: `${-width / 2}px`,
|
|
285
|
+
marginTop: `${-height / 2}px`,
|
|
286
|
+
maxWidth: "none",
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
|
|
229
290
|
const imageStyle = computed(() => ({
|
|
291
|
+
...sized.value,
|
|
230
292
|
...zoom.style.value,
|
|
231
293
|
// Rotation sits innermost, after the composable's scale and translate, so a
|
|
232
294
|
// drag still moves the picture the way the pointer went once it is turned.
|
|
@@ -254,51 +316,31 @@ async function exportImage(): Promise<Blob | null> {
|
|
|
254
316
|
if (!picture || !size || !frame) return null
|
|
255
317
|
if (!edited.value && props.aspect === "free") return null
|
|
256
318
|
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
// `offset` is in content pixels — the same space the cover scale maps from —
|
|
272
|
-
// so it converts with `cover`, not with the zoomed scale.
|
|
273
|
-
const centreX = clamp(
|
|
274
|
-
uprightW / 2 - zoom.offset.value.x / cover,
|
|
275
|
-
cropW / 2,
|
|
276
|
-
uprightW - cropW / 2,
|
|
277
|
-
)
|
|
278
|
-
const centreY = clamp(
|
|
279
|
-
uprightH / 2 - zoom.offset.value.y / cover,
|
|
280
|
-
cropH / 2,
|
|
281
|
-
uprightH - cropH / 2,
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
const scale = Math.min(1, props.maxEdge / Math.max(cropW, cropH))
|
|
319
|
+
// The crop in upright space, from the same geometry the preview is drawn
|
|
320
|
+
// with, then the whole source drawn through one transform: no per-quarter
|
|
321
|
+
// special cases, and a free angle comes out as it looked on screen.
|
|
322
|
+
const crop = cropGeometry({
|
|
323
|
+
sourceWidth: size.width,
|
|
324
|
+
sourceHeight: size.height,
|
|
325
|
+
frameWidth: frameSize.value?.width ?? frame.clientWidth,
|
|
326
|
+
frameHeight: frameSize.value?.height ?? frame.clientHeight,
|
|
327
|
+
rotation: rotation.value,
|
|
328
|
+
scale: zoom.scale.value,
|
|
329
|
+
offset: zoom.offset.value,
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
const scale = Math.min(1, props.maxEdge / Math.max(crop.width, crop.height))
|
|
285
333
|
const canvas = document.createElement("canvas")
|
|
286
|
-
canvas.width = Math.max(1, Math.round(
|
|
287
|
-
canvas.height = Math.max(1, Math.round(
|
|
334
|
+
canvas.width = Math.max(1, Math.round(crop.width * scale))
|
|
335
|
+
canvas.height = Math.max(1, Math.round(crop.height * scale))
|
|
288
336
|
const ctx = canvas.getContext("2d")
|
|
289
337
|
if (!ctx) return null
|
|
290
338
|
|
|
291
339
|
ctx.translate(canvas.width / 2, canvas.height / 2)
|
|
292
|
-
ctx.rotate((upright.value * Math.PI) / 180)
|
|
293
340
|
ctx.scale(scale, scale)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const readH = turned ? cropW : cropH
|
|
298
|
-
const readX = (turned ? centreY : centreX) - readW / 2
|
|
299
|
-
const readY = (turned ? centreX : centreY) - readH / 2
|
|
300
|
-
|
|
301
|
-
ctx.drawImage(picture, readX, readY, readW, readH, -readW / 2, -readH / 2, readW, readH)
|
|
341
|
+
ctx.translate(-crop.centre.x, -crop.centre.y)
|
|
342
|
+
ctx.rotate((rotation.value * Math.PI) / 180)
|
|
343
|
+
ctx.drawImage(picture, -size.width / 2, -size.height / 2)
|
|
302
344
|
|
|
303
345
|
return await new Promise<Blob | null>((resolve) =>
|
|
304
346
|
canvas.toBlob(resolve, outputType.value, props.quality),
|
|
@@ -312,10 +354,6 @@ const outputType = computed(() => {
|
|
|
312
354
|
return ENCODABLE.has(props.file.type) ? props.file.type : "image/png"
|
|
313
355
|
})
|
|
314
356
|
|
|
315
|
-
function clamp(value: number, low: number, high: number) {
|
|
316
|
-
return Math.min(Math.max(value, low), Math.max(low, high))
|
|
317
|
-
}
|
|
318
|
-
|
|
319
357
|
/*
|
|
320
358
|
* The wheel.
|
|
321
359
|
*
|
|
@@ -425,8 +463,9 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
425
463
|
:src="objectUrl"
|
|
426
464
|
alt=""
|
|
427
465
|
draggable="false"
|
|
428
|
-
class="lp-editor__image absolute
|
|
466
|
+
class="lp-editor__image absolute select-none motion-reduce:transition-none"
|
|
429
467
|
:class="[
|
|
468
|
+
sized ? '' : 'inset-0 size-full object-cover',
|
|
430
469
|
zoom.panning.value ? 'cursor-grabbing' : 'cursor-grab',
|
|
431
470
|
animate && !revealed ? 'lp-editor__image--waiting' : '',
|
|
432
471
|
]"
|
|
@@ -5,7 +5,9 @@ export default { inheritAttrs: false }
|
|
|
5
5
|
<script setup lang="ts">
|
|
6
6
|
import { tv, type VariantProps } from "tailwind-variants"
|
|
7
7
|
import { computed, ref, useAttrs, useSlots } from "vue"
|
|
8
|
+
import { useFieldControl } from "../composables/useField"
|
|
8
9
|
import { useInputFilter } from "../composables/useInputFilter"
|
|
10
|
+
import { DEFAULT_MASK_TOKENS, useInputMask, type MaskSpec, type MaskTokens } from "../composables/useInputMask"
|
|
9
11
|
import { mergeClass } from "../composables/useMergedClass"
|
|
10
12
|
import LpFormField from "./LpFormField.vue"
|
|
11
13
|
import LpIcon from "./LpIcon.vue"
|
|
@@ -72,17 +74,56 @@ const props = withDefaults(
|
|
|
72
74
|
* disallowed input/paste at the source — cursor- and IME-safe.
|
|
73
75
|
*/
|
|
74
76
|
restrict?: RegExp
|
|
77
|
+
/**
|
|
78
|
+
* Format-as-you-type mask ("+0 (000) 000-00-00", a list, or a function of
|
|
79
|
+
* the raw value). The model holds the masked text; `update:raw` carries
|
|
80
|
+
* the accepted characters alone.
|
|
81
|
+
*/
|
|
82
|
+
mask?: MaskSpec
|
|
83
|
+
/** Extra or replacement mask tokens, one-character RegExps by key. */
|
|
84
|
+
maskTokens?: MaskTokens
|
|
75
85
|
}>(),
|
|
76
86
|
{ type: "text", size: "md" },
|
|
77
87
|
)
|
|
78
88
|
|
|
79
|
-
defineEmits<{
|
|
89
|
+
const emit = defineEmits<{
|
|
90
|
+
(e: "update:modelValue", value: string): void
|
|
91
|
+
(e: "update:raw", value: string): void
|
|
92
|
+
}>()
|
|
80
93
|
|
|
81
|
-
const { isInvalid, onBeforeInput, onPaste } = useInputFilter({
|
|
94
|
+
const { isInvalid, onBeforeInput, onInput: filterInput, onCompositionEnd, onPaste } = useInputFilter({
|
|
82
95
|
pattern: () => props.pattern,
|
|
83
96
|
restrict: () => props.restrict,
|
|
84
97
|
})
|
|
85
98
|
|
|
99
|
+
const masking = useInputMask({
|
|
100
|
+
mask: () => props.mask,
|
|
101
|
+
tokens: () => (props.maskTokens ? { ...DEFAULT_MASK_TOKENS, ...props.maskTokens } : undefined),
|
|
102
|
+
onChange: (_masked, raw) => emit("update:raw", raw),
|
|
103
|
+
})
|
|
104
|
+
const shown = computed(() => (masking.active.value ? masking.format(props.modelValue).masked : props.modelValue))
|
|
105
|
+
|
|
106
|
+
function onBefore(event: InputEvent): void {
|
|
107
|
+
onBeforeInput(event)
|
|
108
|
+
if (!event.defaultPrevented) masking.onBeforeInput(event)
|
|
109
|
+
if (event.defaultPrevented && masking.active.value && event.target instanceof HTMLInputElement) {
|
|
110
|
+
emit("update:modelValue", event.target.value)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function onInputEvent(event: Event): void {
|
|
115
|
+
if (masking.active.value) masking.onInput(event)
|
|
116
|
+
else filterInput(event)
|
|
117
|
+
emit("update:modelValue", (event.target as HTMLInputElement).value)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function onComposed(event: CompositionEvent): void {
|
|
121
|
+
if (masking.active.value) {
|
|
122
|
+
masking.onCompositionEnd(event)
|
|
123
|
+
emit("update:modelValue", (event.target as HTMLInputElement).value)
|
|
124
|
+
} else onCompositionEnd(event)
|
|
125
|
+
}
|
|
126
|
+
|
|
86
127
|
// Pattern failure folds into the same invalid state as the explicit prop/error.
|
|
87
128
|
const patternInvalid = computed(() => isInvalid(props.modelValue))
|
|
88
129
|
|
|
@@ -93,9 +134,15 @@ const hasTrailing = computed(() => !!slots.trailing)
|
|
|
93
134
|
const hasField = computed(
|
|
94
135
|
() => !!props.label || !!props.hint || !!props.error,
|
|
95
136
|
)
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
137
|
+
const field = useFieldControl(() => ({
|
|
138
|
+
wrapped: hasField.value,
|
|
139
|
+
hasMessage: !!props.error || !!props.hint,
|
|
140
|
+
id: typeof attrs.id === "string" ? attrs.id : undefined,
|
|
141
|
+
invalid: props.invalid || !!props.error || patternInvalid.value,
|
|
142
|
+
required: props.required,
|
|
143
|
+
disabled: props.disabled,
|
|
144
|
+
}))
|
|
145
|
+
const isInvalidState = computed(() => field.value.invalid)
|
|
99
146
|
const shellClass = computed(() =>
|
|
100
147
|
shell({ size: props.size, invalid: isInvalidState.value }),
|
|
101
148
|
)
|
|
@@ -126,7 +173,8 @@ defineExpose({
|
|
|
126
173
|
:hint="hasField ? hint : undefined"
|
|
127
174
|
:error="hasField ? error : undefined"
|
|
128
175
|
:required="hasField ? required : undefined"
|
|
129
|
-
:
|
|
176
|
+
:id="hasField ? field.id : undefined"
|
|
177
|
+
:class="hasField ? rootClass : 'contents'"
|
|
130
178
|
:style="hasField ? rootStyle : undefined"
|
|
131
179
|
>
|
|
132
180
|
<div
|
|
@@ -140,20 +188,23 @@ defineExpose({
|
|
|
140
188
|
<input
|
|
141
189
|
ref="inputEl"
|
|
142
190
|
v-bind="inputAttrs"
|
|
191
|
+
:id="field.id"
|
|
143
192
|
:type="type"
|
|
144
|
-
:value="
|
|
193
|
+
:value="shown"
|
|
145
194
|
:placeholder="placeholder"
|
|
146
|
-
:disabled="disabled"
|
|
147
|
-
:required="required || undefined"
|
|
195
|
+
:disabled="field.disabled"
|
|
196
|
+
:required="field.required || undefined"
|
|
148
197
|
:aria-invalid="isInvalidState || undefined"
|
|
198
|
+
:aria-describedby="field.describedBy"
|
|
149
199
|
:class="[
|
|
150
200
|
'lp-autofill min-w-0 flex-1 self-stretch bg-transparent outline-none placeholder:text-muted',
|
|
151
201
|
hasLeading ? 'pl-2' : padX[size ?? 'md'],
|
|
152
202
|
hasTrailing ? '' : padX[size ?? 'md'],
|
|
153
203
|
]"
|
|
154
|
-
@beforeinput="
|
|
204
|
+
@beforeinput="onBefore"
|
|
155
205
|
@paste="onPaste"
|
|
156
|
-
@
|
|
206
|
+
@compositionend="onComposed"
|
|
207
|
+
@input="onInputEvent"
|
|
157
208
|
/>
|
|
158
209
|
<slot name="trailing" />
|
|
159
210
|
</div>
|