UNPKG

bootstrap-vue-next

Version:

Seamless integration of Vue 3, Bootstrap 5, and TypeScript for modern, type-safe UI development

1 lines 7.99 kB
{"version":3,"file":"useFormInput-BgJCT9k_.mjs","names":[],"sources":["../src/utils/normalizeInput.ts","../src/composables/useFormInput.ts"],"sourcesContent":["import type {Numberish} from '../types/CommonTypes'\n\nexport const normalizeInput = (\n v: Numberish | null,\n modelModifiers: Record<'number' | 'lazy' | 'trim', true | undefined>\n) => {\n if (v === null) return\n let update = v\n if (modelModifiers.number && typeof update === 'string' && update !== '') {\n const parsed = Number.parseFloat(update)\n update = Number.isNaN(parsed) ? update : parsed\n }\n return update\n}\n","import type {Numberish} from '../types/CommonTypes'\nimport {computed, inject, nextTick, onActivated, onMounted, type Ref, type ShallowRef} from 'vue'\nimport {useAriaInvalid} from './useAriaInvalid'\nimport {useId} from './useId'\nimport {useFocus, useToNumber} from '@vueuse/core'\nimport type {CommonInputProps} from '../types/FormCommonInputProps'\nimport {formGroupKey} from '../utils/keys'\nimport {useDebounceFn} from '../utils/debounce'\nimport {useStateClass} from './useStateClass'\n\nexport const useFormInput = (\n props: Readonly<CommonInputProps>,\n input:\n | Readonly<ShallowRef<HTMLInputElement | null>>\n | Readonly<ShallowRef<HTMLTextAreaElement | null>>,\n modelValue: Ref<Numberish | null>,\n modelModifiers: Record<'number' | 'lazy' | 'trim', true | undefined>\n) => {\n const computedId = useId(() => props.id, 'input')\n const debounceNumber = useToNumber(() => props.debounce ?? 0, {nanToZero: true})\n const debounceMaxWaitNumber = useToNumber(() => props.debounceMaxWait ?? Number.NaN)\n\n // This automatically adds the appropriate \"for\" attribute to a BFormGroup label\n const formGroupData = inject(formGroupKey, null)?.(computedId)\n const computedState = computed(() =>\n props.state !== undefined ? props.state : (formGroupData?.state.value ?? null)\n )\n const isDisabled = computed(() => props.disabled || (formGroupData?.disabled.value ?? false))\n const computedAriaInvalid = useAriaInvalid(() => props.ariaInvalid, computedState)\n const stateClass = useStateClass(computedState)\n\n const internalUpdateModelValue = useDebounceFn(\n (value: Numberish) => {\n modelValue.value = value\n },\n () => (modelModifiers.lazy === true ? 0 : debounceNumber.value),\n {maxWait: () => (modelModifiers.lazy === true ? Number.NaN : debounceMaxWaitNumber.value)}\n )\n\n const updateModelValue = (value: Numberish, force = false, immediate = false) => {\n if (modelModifiers.lazy === true && force === false) return\n if (immediate) {\n modelValue.value = value\n } else {\n internalUpdateModelValue(value)\n }\n }\n\n const {focused} = useFocus(input, {\n initialValue: props.autofocus,\n })\n\n const _formatValue = (value: string, evt: Readonly<Event>, force = false) => {\n if (props.formatter !== undefined && (!props.lazyFormatter || force)) {\n return props.formatter(value, evt)\n }\n return value\n }\n onMounted(() => {\n if (input.value) {\n input.value.value = modelValue.value?.toString() ?? ''\n }\n })\n\n onActivated(() => {\n nextTick(() => {\n if (props.autofocus) {\n focused.value = true\n }\n })\n })\n\n const syncDisplayedValue = (nextValue: string) => {\n if (input.value && input.value.value !== nextValue) {\n input.value.value = nextValue\n }\n }\n\n const onInput = (evt: Readonly<Event>) => {\n const {value} = evt.target as HTMLInputElement\n const formattedValue = _formatValue(value, evt)\n if (evt.defaultPrevented) {\n evt.preventDefault()\n return\n }\n\n const nextModel = formattedValue\n\n updateModelValue(nextModel)\n // If the formatter changed the value, directly update the input's visual value\n // to keep the displayed text in sync with the model. Without this, if the\n // formatted value equals the previous model value, Vue's reactivity won't\n // re-render the input and the raw (unformatted) text remains visible.\n if (formattedValue !== value) {\n syncDisplayedValue(formattedValue)\n }\n }\n\n const onChange = (evt: Readonly<Event>) => {\n const {value} = evt.target as HTMLInputElement\n const formattedValue = _formatValue(value, evt)\n if (evt.defaultPrevented) {\n evt.preventDefault()\n return\n }\n\n const nextModel = formattedValue\n if (modelValue.value !== nextModel) {\n updateModelValue(formattedValue, true)\n }\n }\n\n const onBlur = (evt: Readonly<FocusEvent>) => {\n if (\n !modelModifiers.lazy &&\n !props.lazyFormatter &&\n !modelModifiers.trim &&\n debounceNumber.value <= 0\n )\n return\n\n const {value} = evt.target as HTMLInputElement\n const formattedValue = _formatValue(value, evt, true)\n\n const nextModel = modelModifiers.trim ? formattedValue.trim() : formattedValue\n\n // Cancel before modelValue.value comparison and update\n internalUpdateModelValue.cancel()\n if (modelValue.value !== nextModel) {\n updateModelValue(nextModel, true, true)\n }\n\n // If the formatter or trim changed the displayed text, directly sync the DOM.\n // This handles the case where lazyFormatter defers formatting to blur and the\n // formatted value equals the current model (so Vue's reactivity won't re-render).\n if (nextModel !== value) {\n syncDisplayedValue(nextModel)\n }\n }\n\n const focus = () => {\n if (!isDisabled.value) {\n focused.value = true\n }\n }\n\n const blur = () => {\n if (!isDisabled.value) {\n focused.value = false\n }\n }\n\n return {\n input,\n computedId,\n computedAriaInvalid,\n onInput,\n onChange,\n onBlur,\n focus,\n blur,\n stateClass,\n isDisabled,\n }\n}\n"],"mappings":"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"}