@leavepulse/ui 0.44.0 → 0.45.1
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 +49 -3
- package/dist/assets/{LpCommandPalette-DCP5g2ik.css → LpCommandPalette-HzAA4gri.css} +21 -21
- package/dist/assets/{LpRollingText-CR1mb0Vt.css → LpRollingText-BGnE6q9j.css} +3 -0
- package/dist/assets/{LpShortcutsDialog-ChUyU7oV.css → LpShortcutsDialog-CqYX3yy6.css} +28 -7
- package/dist/assets/{LpTable-DDQNdf86.css → LpTable-CPwWxTJS.css} +49 -9
- package/dist/chunks/{ContextMenuItems-CrfeNPWT.js → ContextMenuItems-BtKU7074.js} +2 -1
- package/dist/chunks/{LayoutCanvas-D-jPOAvg.js → LayoutCanvas-BkaGXTjx.js} +1 -1
- package/dist/chunks/{LayoutNode-D8Xy9N-o.js → LayoutNode-B3_9E1Xa.js} +9 -5
- package/dist/chunks/{LpActionBar-BbwCF6l5.js → LpActionBar-CnfFVIwC.js} +3 -3
- package/dist/chunks/{LpAddressInput-BrOqkZqg.js → LpAddressInput-CJOYGZlI.js} +7 -7
- package/dist/chunks/{LpAlert-DkOIjsbq.js → LpAlert-B6ilrM7T.js} +1 -1
- package/dist/chunks/{LpAppShell-BUKPcHco.js → LpAppShell-B07Lf48e.js} +4 -4
- package/dist/chunks/{LpAutocomplete-DxV3Y9h4.js → LpAutocomplete-C8IgSMsi.js} +2 -1
- package/dist/chunks/{LpAvatar-ClBbEYZN.js → LpAvatar-C1XStdOo.js} +3 -3
- package/dist/chunks/{LpBreadcrumbs-B8IJgFIc.js → LpBreadcrumbs-CQJd73Gm.js} +1 -1
- package/dist/chunks/{LpButton-kqJBHIIb.js → LpButton-CKSwC6Mu.js} +1 -1
- package/dist/chunks/{LpCalendar-B7Ko5RdR.js → LpCalendar-DrA2McTU.js} +1 -1
- package/dist/chunks/{LpCard-CpRByR0q.js → LpCard-C2IvucmX.js} +1 -1
- package/dist/chunks/{LpCheckbox-DpqchzH4.js → LpCheckbox-Bkj13CbW.js} +1 -1
- package/dist/chunks/{LpCodeBlock-DeHJPq7p.js → LpCodeBlock-ca7ANFyW.js} +1 -1
- package/dist/chunks/{LpColorPicker-Bhf1qxhB.js → LpColorPicker-zz4VGkG4.js} +46 -20
- package/dist/chunks/{LpConfirmDialog-CIUOSnWo.js → LpConfirmDialog-BTVAR4tF.js} +3 -3
- package/dist/chunks/{LpConfirmHost-CDm0Wtpv.js → LpConfirmHost-Ci_uCHgQ.js} +1 -1
- package/dist/chunks/{LpContextMenu-DQw5dGfG.js → LpContextMenu-BqkAYa9X.js} +2 -1
- package/dist/chunks/{LpDatePicker-Drirx1AK.js → LpDatePicker-BrdFtSTy.js} +16 -10
- package/dist/chunks/LpDateRangePicker-Drd7EbIz.js +210 -0
- package/dist/chunks/{LpDisclosure-ChQYmjlR.js → LpDisclosure-Cc23-JOT.js} +1 -1
- package/dist/chunks/{LpDrawer-bScx-KMO.js → LpDrawer-CfRhHwmE.js} +3 -3
- 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-CSI_5SK2.js → LpFileTree-CNurOvkV.js} +3 -3
- package/dist/chunks/{LpFileTreeNode-B4MiSQX2.js → LpFileTreeNode-D_qRRdsH.js} +3 -3
- package/dist/chunks/{LpFormField-CIk5qosh.js → LpFormField-CtVYj6tO.js} +29 -15
- package/dist/chunks/{LpIcon-CCnX5_2j.js → LpIcon-DYtaid0K.js} +20 -6
- package/dist/chunks/{LpInput-C0e8y8_d.js → LpInput-D-YGQbEv.js} +39 -16
- package/dist/chunks/{LpLightbox-Mr0W_6HK.js → LpLightbox-DSDahF23.js} +1 -1
- package/dist/chunks/{LpLogViewer-YBOaFnc_.js → LpLogViewer-KypbgOyo.js} +252 -58
- package/dist/chunks/{LpMapPicker-NUzS9e37.js → LpMapPicker-Cc84LaM3.js} +1 -1
- package/dist/chunks/{LpMediaGallery-DfJmMW0I.js → LpMediaGallery-YKr-cQPN.js} +2 -2
- package/dist/chunks/{LpModal-DPwGKBPv.js → LpModal-BzTVeNqe.js} +3 -3
- package/dist/chunks/{LpModalNav-DPg5H9f3.js → LpModalNav-CjABSGCu.js} +14 -8
- package/dist/chunks/{LpNotificationBell-t5yn-siX.js → LpNotificationBell-CCwyJP5C.js} +26 -14
- package/dist/chunks/{LpNumberField-DWnQKktS.js → LpNumberField-B77O-z28.js} +1 -1
- package/dist/chunks/{LpPagination-DFngX7Xo.js → LpPagination-BcDlo3r_.js} +1 -1
- package/dist/chunks/{LpPasswordInput-CWVDuBJ5.js → LpPasswordInput-eKzQhbQP.js} +2 -2
- package/dist/chunks/{LpPhoneInput-CD-ZTeSd.js → LpPhoneInput-BCbrrhgE.js} +126 -23
- package/dist/chunks/{LpPie-BLco31Nx.js → LpPie-BEJ2SfI6.js} +9 -10
- package/dist/chunks/{LpPopover-p_e_shN3.js → LpPopover-DCKeqiVn.js} +1 -0
- 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-Dejd7cIx.js → LpSegmented-C09EEam2.js} +1 -1
- package/dist/chunks/{LpSelect-DrRD9I7L.js → LpSelect-DNIL6MB9.js} +20 -23
- package/dist/chunks/{LpSidebar-BgwUNWpJ.js → LpSidebar-Bwws_X29.js} +3 -3
- package/dist/chunks/{LpSidebarNav-Bqxz8aWT.js → LpSidebarNav-kx-LdqNU.js} +3 -3
- package/dist/chunks/{LpStat-dXtyrdX9.js → LpStat-B-7udBRP.js} +1 -1
- package/dist/chunks/{LpStepper-Bk-XaQYU.js → LpStepper-C7XiVKZj.js} +34 -19
- package/dist/chunks/{LpSwitch-B1aLKulj.js → LpSwitch-OrcOJX9m.js} +1 -1
- package/dist/chunks/LpTableFilter-BV8PpLM9.js +158 -0
- package/dist/chunks/{LpTabs-_qMVaZJ6.js → LpTabs-29iW1XOx.js} +1 -1
- package/dist/chunks/LpTagsInput-D1SuqbhB.js +300 -0
- package/dist/chunks/{LpTextarea-yP8hGedR.js → LpTextarea-uuScWo6-.js} +1 -1
- package/dist/chunks/{LpThemeProvider-Wjm7Tuit.js → LpThemeProvider-Be4r2TQX.js} +1 -1
- package/dist/chunks/{LpThemeSwitcher-CKLc10KZ.js → LpThemeSwitcher-B4aVPbNC.js} +19 -22
- package/dist/chunks/{LpTimeField-C5HRgS5H.js → LpTimeField-Cqe7fXnh.js} +1 -1
- package/dist/chunks/{LpToaster--oT6lfhL.js → LpToaster-DTtWuKvq.js} +76 -9
- package/dist/chunks/{LpTooltip-CBdzeKNf.js → LpTooltip-CXVKcOXT.js} +3 -2
- package/dist/chunks/{LpTopologyCanvas-DocsLal3.js → LpTopologyCanvas-6ozNrQKh.js} +2 -1
- package/dist/chunks/{LpUptimeBar-DLtxOMRy.js → LpUptimeBar-ugcv25xj.js} +2 -2
- package/dist/chunks/icons-CkA0njzm.js +43 -0
- package/dist/chunks/{useTheme-C8JHUlPM.js → useTheme-DJm1JSsY.js} +47 -50
- package/dist/component-names.d.ts +1 -1
- package/dist/components/ContextMenuItems.vue.js +1 -1
- package/dist/components/LayoutCanvas.vue.js +1 -1
- package/dist/components/LayoutNode.vue.js +1 -1
- package/dist/components/LpActionBar.vue.js +1 -1
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.js +1 -1
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpAutocomplete.vue.js +1 -1
- package/dist/components/LpAvatar.vue.js +1 -1
- package/dist/components/LpBreadcrumbs.vue.js +1 -1
- package/dist/components/LpButton.vue.d.ts +2 -2
- 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 +16 -1
- package/dist/components/LpChart.vue.js +63 -5
- package/dist/components/LpCheckbox.vue.js +1 -1
- 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 +6 -0
- package/dist/components/LpCommandPalette.vue.js +62 -11
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpConfirmHost.vue.js +1 -1
- package/dist/components/LpContextMenu.vue.js +1 -1
- 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.js +1 -1
- package/dist/components/LpDropdownMenu.vue.js +1 -1
- package/dist/components/LpDropzone.vue.js +1 -1
- 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.js +1 -1
- package/dist/components/LpFileTreeNode.vue.js +1 -1
- package/dist/components/LpFormField.vue.d.ts +4 -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 +2 -2
- package/dist/components/LpInput.vue.d.ts +19 -0
- package/dist/components/LpInput.vue.js +1 -1
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpLogViewer.vue.d.ts +52 -1
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpMapPicker.vue.js +1 -1
- package/dist/components/LpMediaGallery.vue.js +1 -1
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.js +1 -1
- package/dist/components/LpNumberField.vue.js +1 -1
- 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.js +1 -1
- package/dist/components/LpPopover.vue.js +1 -1
- package/dist/components/LpProgress.vue.d.ts +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.js +1 -1
- package/dist/components/LpSegmented.vue.js +1 -1
- package/dist/components/LpSelect.vue.js +1 -1
- package/dist/components/LpShortcutsDialog.vue.d.ts +6 -0
- package/dist/components/LpShortcutsDialog.vue.js +56 -14
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.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 +84 -2
- package/dist/components/LpTable.vue.js +481 -107
- package/dist/components/LpTableFilter.vue.d.ts +31 -0
- package/dist/components/LpTableFilter.vue.js +4 -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.js +1 -1
- package/dist/components/LpThemeProvider.vue.js +1 -1
- package/dist/components/LpThemeSwitcher.vue.d.ts +2 -2
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpTimeField.vue.js +1 -1
- package/dist/components/LpToaster.vue.d.ts +2 -0
- package/dist/components/LpToaster.vue.js +1 -1
- package/dist/components/LpTooltip.vue.js +1 -1
- package/dist/components/LpTopologyCanvas.vue.js +1 -1
- package/dist/components/LpUptimeBar.vue.js +1 -1
- package/dist/components/chart.js +3 -2
- package/dist/components/commandSearch.js +3 -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/logSearch.d.ts +18 -0
- package/dist/components/logSearch.js +36 -0
- package/dist/components/phone.d.ts +41 -0
- package/dist/components/phone.js +80 -0
- package/dist/components/tableFilter.d.ts +36 -0
- package/dist/components/tableFilter.js +84 -0
- package/dist/components/tags.d.ts +20 -0
- package/dist/components/tags.js +37 -0
- package/dist/composables/useField.d.ts +4 -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/useHotkeys.d.ts +65 -5
- package/dist/composables/useHotkeys.js +170 -9
- package/dist/composables/useInputMask.d.ts +59 -0
- package/dist/composables/useInputMask.js +170 -0
- package/dist/composables/useLocale.d.ts +2 -0
- package/dist/composables/useLocale.js +9 -0
- package/dist/composables/usePaintSelect.d.ts +22 -0
- package/dist/composables/usePaintSelect.js +169 -0
- package/dist/composables/useShift.js +7 -2
- 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 +29 -3
- package/dist/index.js +195 -138
- package/dist/kit-icons.d.ts +2 -0
- package/dist/theme/backdrop.d.ts +12 -0
- package/dist/theme/presets.d.ts +0 -2
- package/package.json +13 -4
- package/src/component-names.ts +4 -0
- package/src/components/ContextMenuItems.vue +1 -1
- package/src/components/LayoutNode.vue +11 -2
- package/src/components/LpActionBar.vue +1 -1
- package/src/components/LpAutocomplete.vue +1 -0
- package/src/components/LpAvatar.vue +2 -2
- package/src/components/LpButton.vue +1 -1
- package/src/components/LpChart.vue +80 -1
- package/src/components/LpColorPicker.vue +34 -1
- package/src/components/LpCommandPalette.vue +82 -8
- package/src/components/LpContextMenu.vue +1 -1
- package/src/components/LpDatePicker.vue +11 -4
- package/src/components/LpDateRangePicker.vue +226 -0
- package/src/components/LpDrawer.vue +1 -1
- package/src/components/LpDropdownMenu.vue +1 -0
- package/src/components/LpFieldNotice.vue +65 -0
- package/src/components/LpFormField.vue +33 -4
- package/src/components/LpIcon.vue +22 -4
- package/src/components/LpInput.vue +46 -6
- package/src/components/LpLogViewer.vue +225 -22
- package/src/components/LpModal.vue +2 -2
- package/src/components/LpModalNav.vue +13 -2
- package/src/components/LpNotificationBell.vue +15 -7
- package/src/components/LpPhoneInput.vue +179 -20
- package/src/components/LpPie.vue +8 -5
- package/src/components/LpPopover.vue +1 -0
- package/src/components/LpRangeCalendar.vue +365 -0
- package/src/components/LpRollingText.vue +3 -0
- package/src/components/LpScrollArea.vue +22 -2
- package/src/components/LpSelect.vue +0 -0
- package/src/components/LpShortcutsDialog.vue +77 -8
- package/src/components/LpStepper.vue +23 -10
- package/src/components/LpSwitch.vue +1 -1
- package/src/components/LpTable.vue +584 -65
- package/src/components/LpTableFilter.vue +175 -0
- package/src/components/LpTagsInput.vue +364 -0
- package/src/components/LpThemeSwitcher.vue +19 -23
- package/src/components/LpToaster.vue +91 -5
- package/src/components/LpTooltip.vue +9 -2
- package/src/components/LpTopologyCanvas.vue +7 -1
- package/src/components/chart.ts +5 -3
- package/src/components/commandSearch.ts +6 -0
- package/src/components/dateRange.ts +134 -0
- package/src/components/exportData.ts +228 -0
- package/src/components/logSearch.ts +46 -0
- package/src/components/phone.ts +140 -0
- package/src/components/tableFilter.ts +122 -0
- package/src/components/tags.ts +62 -0
- package/src/composables/useField.ts +4 -0
- package/src/composables/useFieldNotice.ts +59 -0
- package/src/composables/useForm.ts +314 -0
- package/src/composables/useHotkeys.ts +269 -13
- package/src/composables/useInputMask.ts +254 -0
- package/src/composables/useLocale.ts +14 -0
- package/src/composables/usePaintSelect.ts +243 -0
- package/src/composables/useShift.ts +13 -2
- 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 +37 -3
- package/src/kit-icons.ts +5 -0
- package/src/nuxt.ts +30 -2
- package/src/runtime/theme.plugin.ts +2 -2
- package/src/theme/backdrop.ts +69 -0
- package/src/theme/presets.ts +6 -64
- package/src/tokens/tokens.css +53 -4
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default { inheritAttrs: false }
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
/*
|
|
7
|
+
* Range field + popover: presets on one side, a range calendar on the other.
|
|
8
|
+
* The model is `{ start, end }` of ISO days, like LpDatePicker's single day.
|
|
9
|
+
* Picking the second end or a preset fills the field and closes the popover.
|
|
10
|
+
* Two months sit side by side where there is room, one on a phone.
|
|
11
|
+
*/
|
|
12
|
+
import { PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from "reka-ui"
|
|
13
|
+
import { computed, onBeforeUnmount, onMounted, ref, useAttrs } from "vue"
|
|
14
|
+
import { useFieldControl } from "../composables/useField"
|
|
15
|
+
import { useLocale } from "../composables/useLocale"
|
|
16
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
17
|
+
import { mergeClass } from "../composables/useMergedClass"
|
|
18
|
+
import {
|
|
19
|
+
clampRange,
|
|
20
|
+
type DateRange,
|
|
21
|
+
type DateRangePreset,
|
|
22
|
+
defaultRangePresets,
|
|
23
|
+
formatRange,
|
|
24
|
+
normalizeRange,
|
|
25
|
+
sameRange,
|
|
26
|
+
} from "./dateRange"
|
|
27
|
+
import { POPOVER_PANEL } from "./dropdown"
|
|
28
|
+
import LpIcon from "./LpIcon.vue"
|
|
29
|
+
import LpRollingText from "./LpRollingText.vue"
|
|
30
|
+
import LpRangeCalendar, { type RangeChange } from "./LpRangeCalendar.vue"
|
|
31
|
+
|
|
32
|
+
export interface DateRangePickerLabels extends Record<string, string> {
|
|
33
|
+
placeholder: string
|
|
34
|
+
clear: string
|
|
35
|
+
presets: string
|
|
36
|
+
today: string
|
|
37
|
+
last7: string
|
|
38
|
+
last30: string
|
|
39
|
+
thisMonth: string
|
|
40
|
+
lastMonth: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const props = withDefaults(
|
|
44
|
+
defineProps<{
|
|
45
|
+
/** Selected range (v-model). */
|
|
46
|
+
modelValue?: DateRange
|
|
47
|
+
placeholder?: string
|
|
48
|
+
min?: string
|
|
49
|
+
max?: string
|
|
50
|
+
isDisabled?: (iso: string) => boolean
|
|
51
|
+
isDateUnavailable?: (iso: string) => boolean
|
|
52
|
+
/** Longest allowed range, in days. */
|
|
53
|
+
maximumDays?: number
|
|
54
|
+
/** Shortcut ranges; an empty list hides them. Defaults to the built-in set. */
|
|
55
|
+
presets?: DateRangePreset[]
|
|
56
|
+
clearable?: boolean
|
|
57
|
+
disabled?: boolean
|
|
58
|
+
invalid?: boolean
|
|
59
|
+
size?: "sm" | "md" | "lg"
|
|
60
|
+
locale?: string
|
|
61
|
+
/** Intl format for the displayed ends. */
|
|
62
|
+
format?: Intl.DateTimeFormatOptions
|
|
63
|
+
labels?: Partial<DateRangePickerLabels>
|
|
64
|
+
}>(),
|
|
65
|
+
{ size: "md" },
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
const emit = defineEmits<{
|
|
69
|
+
(e: "update:modelValue", value: DateRange | undefined): void
|
|
70
|
+
}>()
|
|
71
|
+
|
|
72
|
+
const text = useLabels<DateRangePickerLabels>(
|
|
73
|
+
"dateRangePicker",
|
|
74
|
+
{
|
|
75
|
+
placeholder: "Pick a date range",
|
|
76
|
+
clear: "Clear",
|
|
77
|
+
presets: "Presets",
|
|
78
|
+
today: "Today",
|
|
79
|
+
last7: "Last 7 days",
|
|
80
|
+
last30: "Last 30 days",
|
|
81
|
+
thisMonth: "This month",
|
|
82
|
+
lastMonth: "Last month",
|
|
83
|
+
},
|
|
84
|
+
() => props.labels,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
const open = ref(false)
|
|
88
|
+
const value = computed(() => normalizeRange(props.modelValue))
|
|
89
|
+
const locale = useLocale(() => props.locale)
|
|
90
|
+
const display = computed(() => (value.value ? formatRange(value.value, locale.value, props.format) : ""))
|
|
91
|
+
|
|
92
|
+
const presetList = computed<DateRangePreset[]>(() => {
|
|
93
|
+
return props.presets ?? defaultRangePresets(text.value)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
* A range picked on the calendar keeps the popover open: the field already
|
|
98
|
+
* shows it, and the user can still pull either end before clicking away.
|
|
99
|
+
* Closing on the second click left no moment to adjust. A preset is a whole
|
|
100
|
+
* answer, so it closes.
|
|
101
|
+
*/
|
|
102
|
+
function pick(range: DateRange, change?: RangeChange) {
|
|
103
|
+
emit("update:modelValue", range)
|
|
104
|
+
if (!change) open.value = false
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function pickPreset(preset: DateRangePreset) {
|
|
108
|
+
const range = clampRange(preset.range(), props.min, props.max)
|
|
109
|
+
if (range) pick(range)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function presetFits(preset: DateRangePreset): boolean {
|
|
113
|
+
return !!clampRange(preset.range(), props.min, props.max)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const wide = ref(false)
|
|
117
|
+
let query: MediaQueryList | undefined
|
|
118
|
+
const onQuery = () => {
|
|
119
|
+
wide.value = !!query?.matches
|
|
120
|
+
}
|
|
121
|
+
onMounted(() => {
|
|
122
|
+
query = window.matchMedia("(min-width: 640px)")
|
|
123
|
+
onQuery()
|
|
124
|
+
query.addEventListener("change", onQuery)
|
|
125
|
+
})
|
|
126
|
+
onBeforeUnmount(() => query?.removeEventListener("change", onQuery))
|
|
127
|
+
|
|
128
|
+
const shellSize = {
|
|
129
|
+
sm: "h-(--size-control-sm) text-xs",
|
|
130
|
+
md: "h-(--size-control-md) text-sm",
|
|
131
|
+
lg: "h-(--size-control-lg) text-sm",
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const attrs = useAttrs()
|
|
135
|
+
const field = useFieldControl(() => ({
|
|
136
|
+
wrapped: false,
|
|
137
|
+
id: typeof attrs.id === "string" ? attrs.id : undefined,
|
|
138
|
+
invalid: props.invalid,
|
|
139
|
+
disabled: props.disabled,
|
|
140
|
+
}))
|
|
141
|
+
const showClear = computed(() => props.clearable && !!value.value && !field.value.disabled)
|
|
142
|
+
</script>
|
|
143
|
+
|
|
144
|
+
<template>
|
|
145
|
+
<div :class="mergeClass('relative w-full', attrs.class as string)" :style="attrs.style as never">
|
|
146
|
+
<PopoverRoot v-model:open="open">
|
|
147
|
+
<PopoverTrigger as-child>
|
|
148
|
+
<button
|
|
149
|
+
v-bind="{ ...attrs, class: undefined, style: undefined }"
|
|
150
|
+
:id="field.id"
|
|
151
|
+
type="button"
|
|
152
|
+
:disabled="field.disabled"
|
|
153
|
+
:aria-invalid="field.invalid || undefined"
|
|
154
|
+
:aria-describedby="field.describedBy"
|
|
155
|
+
class="group flex w-full items-center gap-2 rounded-control border bg-surface-soft px-3 text-left text-ink outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring data-[state=open]:border-brand disabled:cursor-not-allowed disabled:opacity-55"
|
|
156
|
+
:class="[
|
|
157
|
+
shellSize[size],
|
|
158
|
+
field.invalid ? 'border-danger focus-visible:ring-danger-soft' : 'border-line focus-visible:border-brand',
|
|
159
|
+
]"
|
|
160
|
+
>
|
|
161
|
+
<LpIcon name="lucide:calendar-range" :size="16" class="shrink-0 text-muted" />
|
|
162
|
+
<LpRollingText
|
|
163
|
+
:value="display || placeholder || text.placeholder"
|
|
164
|
+
align="start"
|
|
165
|
+
class="min-w-0 flex-1"
|
|
166
|
+
:class="display ? '' : 'text-muted'"
|
|
167
|
+
/>
|
|
168
|
+
<span v-if="showClear" class="w-[15px] shrink-0" aria-hidden="true" />
|
|
169
|
+
<LpIcon
|
|
170
|
+
name="lucide:chevron-down"
|
|
171
|
+
:size="15"
|
|
172
|
+
class="shrink-0 text-muted transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-data-[state=open]:rotate-180 motion-reduce:transition-none"
|
|
173
|
+
/>
|
|
174
|
+
</button>
|
|
175
|
+
</PopoverTrigger>
|
|
176
|
+
<PopoverPortal>
|
|
177
|
+
<PopoverContent
|
|
178
|
+
:side-offset="6"
|
|
179
|
+
align="start"
|
|
180
|
+
:collision-padding="8"
|
|
181
|
+
:class="[POPOVER_PANEL, 'z-(--z-popover) flex max-w-[calc(100vw-1rem)] flex-col rounded-card p-0 outline-none sm:flex-row']"
|
|
182
|
+
>
|
|
183
|
+
<div
|
|
184
|
+
v-if="presetList.length"
|
|
185
|
+
role="group"
|
|
186
|
+
:aria-label="text.presets"
|
|
187
|
+
class="flex gap-1 overflow-x-auto border-b border-line p-2 sm:w-40 sm:flex-col sm:overflow-visible sm:border-r sm:border-b-0"
|
|
188
|
+
>
|
|
189
|
+
<button
|
|
190
|
+
v-for="(preset, i) in presetList"
|
|
191
|
+
:key="`${i}:${preset.label}`"
|
|
192
|
+
type="button"
|
|
193
|
+
:disabled="!presetFits(preset)"
|
|
194
|
+
:aria-pressed="sameRange(value, preset.range())"
|
|
195
|
+
class="shrink-0 rounded-control px-2.5 py-1.5 text-left text-sm whitespace-nowrap text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:bg-surface-soft hover:text-ink focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40 aria-pressed:bg-brand-soft aria-pressed:text-ink"
|
|
196
|
+
@click="pickPreset(preset)"
|
|
197
|
+
>
|
|
198
|
+
{{ preset.label }}
|
|
199
|
+
</button>
|
|
200
|
+
</div>
|
|
201
|
+
<LpRangeCalendar
|
|
202
|
+
:model-value="value"
|
|
203
|
+
:min="min"
|
|
204
|
+
:max="max"
|
|
205
|
+
:is-disabled="isDisabled"
|
|
206
|
+
:is-date-unavailable="isDateUnavailable"
|
|
207
|
+
:maximum-days="maximumDays"
|
|
208
|
+
:months="wide ? 2 : 1"
|
|
209
|
+
:locale="locale"
|
|
210
|
+
class="border-0 bg-transparent"
|
|
211
|
+
@update:model-value="pick"
|
|
212
|
+
/>
|
|
213
|
+
</PopoverContent>
|
|
214
|
+
</PopoverPortal>
|
|
215
|
+
</PopoverRoot>
|
|
216
|
+
<button
|
|
217
|
+
v-if="showClear"
|
|
218
|
+
type="button"
|
|
219
|
+
:aria-label="text.clear"
|
|
220
|
+
class="absolute top-1/2 right-[calc(0.75rem+15px+0.5rem)] flex -translate-y-1/2 items-center rounded-sm text-muted outline-none transition-colors hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
|
|
221
|
+
@click="emit('update:modelValue', undefined)"
|
|
222
|
+
>
|
|
223
|
+
<LpIcon name="lucide:x" :size="15" />
|
|
224
|
+
</button>
|
|
225
|
+
</div>
|
|
226
|
+
</template>
|
|
@@ -162,7 +162,7 @@ watch(() => props.open, (open) => (open ? claim() : release()), { immediate: tru
|
|
|
162
162
|
onBeforeUnmount(release)
|
|
163
163
|
|
|
164
164
|
const contentClass = computed(() => {
|
|
165
|
-
const surface = frosted.value ? (layer.value.nested ? "lp-glass-overlay--nested" : "lp-glass-overlay") : "bg-surface-raised"
|
|
165
|
+
const surface = frosted.value ? (layer.value.nested ? "lp-glass-overlay lp-glass-overlay--nested" : "lp-glass-overlay") : "bg-surface-raised"
|
|
166
166
|
const base = `fixed z-(--z-modal) flex flex-col border-line ${surface} shadow-panel outline-none`
|
|
167
167
|
const map: Record<Direction, string> = {
|
|
168
168
|
left: "inset-y-0 left-0 border-r rounded-r-card",
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* The floating note from useFieldNotice: anchored under the nearest
|
|
4
|
+
* `relative` box, over whatever sits below it (usually the hint), so it never
|
|
5
|
+
* moves the layout. It slides in, slides out, and nudges sideways when the
|
|
6
|
+
* same note is raised again while showing.
|
|
7
|
+
*/
|
|
8
|
+
import { ref, watch } from "vue"
|
|
9
|
+
import { prefersReducedMotion } from "../composables/easing"
|
|
10
|
+
import type { FieldNotice } from "../composables/useFieldNotice"
|
|
11
|
+
import LpIcon from "./LpIcon.vue"
|
|
12
|
+
|
|
13
|
+
const props = defineProps<{
|
|
14
|
+
notice: FieldNotice
|
|
15
|
+
id?: string
|
|
16
|
+
}>()
|
|
17
|
+
|
|
18
|
+
const el = ref<HTMLElement | null>(null)
|
|
19
|
+
|
|
20
|
+
watch(
|
|
21
|
+
() => props.notice.seq.value,
|
|
22
|
+
(_, before) => {
|
|
23
|
+
if (before === undefined || !el.value || prefersReducedMotion()) return
|
|
24
|
+
el.value.animate(
|
|
25
|
+
[
|
|
26
|
+
{ transform: "translateX(0)" },
|
|
27
|
+
{ transform: "translateX(-3px)" },
|
|
28
|
+
{ transform: "translateX(3px)" },
|
|
29
|
+
{ transform: "translateX(0)" },
|
|
30
|
+
],
|
|
31
|
+
{ duration: 240, easing: "ease-out" },
|
|
32
|
+
)
|
|
33
|
+
},
|
|
34
|
+
)
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<template>
|
|
38
|
+
<Transition
|
|
39
|
+
enter-active-class="transition-[opacity,translate] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none"
|
|
40
|
+
leave-active-class="transition-[opacity,translate] duration-fast ease-out motion-reduce:transition-none"
|
|
41
|
+
enter-from-class="-translate-y-1 opacity-0"
|
|
42
|
+
leave-to-class="-translate-y-1 opacity-0"
|
|
43
|
+
>
|
|
44
|
+
<div
|
|
45
|
+
v-if="notice.message.value"
|
|
46
|
+
:id="id"
|
|
47
|
+
ref="el"
|
|
48
|
+
role="status"
|
|
49
|
+
class="pointer-events-none absolute top-full left-0 z-20 mt-1.5 flex max-w-full items-center gap-1.5 rounded-control border px-2 py-1 text-xs shadow-sm"
|
|
50
|
+
:class="
|
|
51
|
+
notice.tone.value === 'danger'
|
|
52
|
+
? 'border-danger/40 bg-surface-raised bg-[linear-gradient(var(--color-danger-soft),var(--color-danger-soft))] text-danger'
|
|
53
|
+
: 'border-line bg-surface-raised text-ink'
|
|
54
|
+
"
|
|
55
|
+
>
|
|
56
|
+
<LpIcon
|
|
57
|
+
:name="notice.tone.value === 'danger' ? 'lucide:circle-alert' : 'lucide:info'"
|
|
58
|
+
:size="13"
|
|
59
|
+
class="shrink-0"
|
|
60
|
+
:class="notice.tone.value === 'danger' ? '' : 'text-muted'"
|
|
61
|
+
/>
|
|
62
|
+
<span class="truncate">{{ notice.message.value }}</span>
|
|
63
|
+
</div>
|
|
64
|
+
</Transition>
|
|
65
|
+
</template>
|
|
@@ -17,8 +17,10 @@ export default { inheritAttrs: false }
|
|
|
17
17
|
*/
|
|
18
18
|
import { Label } from "reka-ui"
|
|
19
19
|
import { computed, useId, useSlots } from "vue"
|
|
20
|
+
import LpFieldNotice from "./LpFieldNotice.vue"
|
|
20
21
|
import LpShift from "./LpShift.vue"
|
|
21
22
|
import { fieldMessageId, provideField } from "../composables/useField"
|
|
23
|
+
import { useFieldNotice } from "../composables/useFieldNotice"
|
|
22
24
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
23
25
|
|
|
24
26
|
const props = defineProps<{
|
|
@@ -37,6 +39,8 @@ const id = computed(() => props.id ?? generated)
|
|
|
37
39
|
const messageId = computed(() => fieldMessageId(id.value))
|
|
38
40
|
const slots = useSlots()
|
|
39
41
|
const hasMessage = computed(() => !!props.error || !!props.hint || !!slots.hint)
|
|
42
|
+
const notice = useFieldNotice()
|
|
43
|
+
const noticeId = computed(() => `${id.value}-notice`)
|
|
40
44
|
|
|
41
45
|
// The control inside reads its id, description and states from here, so the
|
|
42
46
|
// label, the message line and aria-invalid stay tied to it without the
|
|
@@ -45,13 +49,20 @@ provideField(
|
|
|
45
49
|
computed(() => ({
|
|
46
50
|
id: id.value,
|
|
47
51
|
labelId: props.label ? `${id.value}-label` : undefined,
|
|
48
|
-
describedBy:
|
|
52
|
+
describedBy:
|
|
53
|
+
[hasMessage.value ? messageId.value : "", notice.message.value ? noticeId.value : ""]
|
|
54
|
+
.filter(Boolean)
|
|
55
|
+
.join(" ") || undefined,
|
|
49
56
|
invalid: !!props.invalid || !!props.error,
|
|
50
57
|
required: !!props.required,
|
|
51
58
|
disabled: !!props.disabled,
|
|
59
|
+
notify: notice.show,
|
|
60
|
+
clearNotice: notice.clear,
|
|
52
61
|
})),
|
|
53
62
|
)
|
|
54
63
|
|
|
64
|
+
defineExpose({ notify: notice.show, clearNotice: notice.clear })
|
|
65
|
+
|
|
55
66
|
// The field stacks label and control with a fixed gap; a denser form needs that gap smaller.
|
|
56
67
|
const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
57
68
|
"flex min-w-0 flex-col gap-1.5",
|
|
@@ -72,7 +83,13 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
72
83
|
<span v-if="required" class="text-danger">*</span>
|
|
73
84
|
</Label>
|
|
74
85
|
|
|
75
|
-
|
|
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>
|
|
76
93
|
|
|
77
94
|
<!-- Swapping hint→error changes the text but not the height, so the eased
|
|
78
95
|
box only ever moves when a message appears or clears.
|
|
@@ -83,12 +100,24 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
83
100
|
leave every message-less field a row taller than before — and would be
|
|
84
101
|
a step the easing could not cover, since it exists even at height 0. -->
|
|
85
102
|
<LpShift axis="height">
|
|
86
|
-
<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>
|
|
87
111
|
<!-- The hint is a slot as well as a prop: most are a sentence, but some
|
|
88
112
|
carry a link or a unit in <code>, and a prop can only be text. Callers
|
|
89
113
|
had been reaching for `#hint` on the assumption it existed — it
|
|
90
114
|
rendered nothing, so the explanation silently went missing. -->
|
|
91
|
-
<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
|
+
>
|
|
92
121
|
<slot name="hint">{{ hint }}</slot>
|
|
93
122
|
</p>
|
|
94
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"
|
|
@@ -7,6 +7,7 @@ import { tv, type VariantProps } from "tailwind-variants"
|
|
|
7
7
|
import { computed, ref, useAttrs, useSlots } from "vue"
|
|
8
8
|
import { useFieldControl } from "../composables/useField"
|
|
9
9
|
import { useInputFilter } from "../composables/useInputFilter"
|
|
10
|
+
import { DEFAULT_MASK_TOKENS, useInputMask, type MaskSpec, type MaskTokens } from "../composables/useInputMask"
|
|
10
11
|
import { mergeClass } from "../composables/useMergedClass"
|
|
11
12
|
import LpFormField from "./LpFormField.vue"
|
|
12
13
|
import LpIcon from "./LpIcon.vue"
|
|
@@ -73,17 +74,56 @@ const props = withDefaults(
|
|
|
73
74
|
* disallowed input/paste at the source — cursor- and IME-safe.
|
|
74
75
|
*/
|
|
75
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
|
|
76
85
|
}>(),
|
|
77
86
|
{ type: "text", size: "md" },
|
|
78
87
|
)
|
|
79
88
|
|
|
80
|
-
defineEmits<{
|
|
89
|
+
const emit = defineEmits<{
|
|
90
|
+
(e: "update:modelValue", value: string): void
|
|
91
|
+
(e: "update:raw", value: string): void
|
|
92
|
+
}>()
|
|
81
93
|
|
|
82
94
|
const { isInvalid, onBeforeInput, onInput: filterInput, onCompositionEnd, onPaste } = useInputFilter({
|
|
83
95
|
pattern: () => props.pattern,
|
|
84
96
|
restrict: () => props.restrict,
|
|
85
97
|
})
|
|
86
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
|
+
|
|
87
127
|
// Pattern failure folds into the same invalid state as the explicit prop/error.
|
|
88
128
|
const patternInvalid = computed(() => isInvalid(props.modelValue))
|
|
89
129
|
|
|
@@ -134,7 +174,7 @@ defineExpose({
|
|
|
134
174
|
:error="hasField ? error : undefined"
|
|
135
175
|
:required="hasField ? required : undefined"
|
|
136
176
|
:id="hasField ? field.id : undefined"
|
|
137
|
-
:class="hasField ? rootClass :
|
|
177
|
+
:class="hasField ? rootClass : 'contents'"
|
|
138
178
|
:style="hasField ? rootStyle : undefined"
|
|
139
179
|
>
|
|
140
180
|
<div
|
|
@@ -150,7 +190,7 @@ defineExpose({
|
|
|
150
190
|
v-bind="inputAttrs"
|
|
151
191
|
:id="field.id"
|
|
152
192
|
:type="type"
|
|
153
|
-
:value="
|
|
193
|
+
:value="shown"
|
|
154
194
|
:placeholder="placeholder"
|
|
155
195
|
:disabled="field.disabled"
|
|
156
196
|
:required="field.required || undefined"
|
|
@@ -161,10 +201,10 @@ defineExpose({
|
|
|
161
201
|
hasLeading ? 'pl-2' : padX[size ?? 'md'],
|
|
162
202
|
hasTrailing ? '' : padX[size ?? 'md'],
|
|
163
203
|
]"
|
|
164
|
-
@beforeinput="
|
|
204
|
+
@beforeinput="onBefore"
|
|
165
205
|
@paste="onPaste"
|
|
166
|
-
@compositionend="
|
|
167
|
-
@input="
|
|
206
|
+
@compositionend="onComposed"
|
|
207
|
+
@input="onInputEvent"
|
|
168
208
|
/>
|
|
169
209
|
<slot name="trailing" />
|
|
170
210
|
</div>
|