UNPKG

bootstrap-vue-next

Version:

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

1 lines 13.7 kB
{"version":3,"file":"BFormTextarea-ClcoJrSM.mjs","names":[],"sources":["../src/composables/useTextareaResize.ts","../src/components/BFormTextarea/BFormTextarea.vue","../src/components/BFormTextarea/BFormTextarea.vue"],"sourcesContent":["import {useToNumber} from '@vueuse/core'\nimport type {Numberish} from '../types/CommonTypes'\nimport {\n computed,\n type CSSProperties,\n type MaybeRefOrGetter,\n nextTick,\n onMounted,\n ref,\n type ShallowRef,\n toValue,\n} from 'vue'\nimport {isVisible} from '../utils/dom'\n\nexport const useTextareaResize = (\n input: Readonly<ShallowRef<HTMLTextAreaElement | null>>,\n {\n maxRows,\n noAutoShrink,\n rows,\n }: {\n rows: MaybeRefOrGetter<Numberish>\n maxRows: MaybeRefOrGetter<Numberish | undefined>\n noAutoShrink: MaybeRefOrGetter<boolean>\n }\n) => {\n const height = ref<number | null | string>(0)\n const maxRowsNumber = useToNumber(\n computed(() => toValue(maxRows) || Number.NaN),\n {\n method: 'parseInt',\n nanToZero: true,\n }\n )\n const rowsNumber = useToNumber(rows, {\n method: 'parseInt',\n nanToZero: true,\n })\n const computedMinRows = computed(() => Math.max(rowsNumber.value || 2, 2))\n const computedMaxRows = computed(() => Math.max(computedMinRows.value, maxRowsNumber.value || 0))\n const computedRows = computed(() =>\n computedMinRows.value === computedMaxRows.value ? computedMinRows.value : null\n )\n\n const handleHeightChange = async () => {\n // Element must be visible (not hidden) and in document\n // Must be checked after above checks\n if (!input.value || !isVisible(input.value)) {\n height.value = null\n return\n }\n\n // Get current computed styles\n const computedStyle = getComputedStyle(input.value)\n // Height of one line of text in px\n const lineHeight = Number.parseFloat(computedStyle.lineHeight) || 1\n // Calculate height of border and padding\n const border =\n (Number.parseFloat(computedStyle.borderTopWidth) || 0) +\n (Number.parseFloat(computedStyle.borderBottomWidth) || 0)\n const padding =\n (Number.parseFloat(computedStyle.paddingTop) || 0) +\n (Number.parseFloat(computedStyle.paddingBottom) || 0)\n // Calculate offset\n const offset = border + padding\n // Minimum height for min rows (which must be 2 rows or greater for cross-browser support)\n const minHeight = lineHeight * computedMinRows.value + offset\n\n // Get the current style height (with `px` units)\n const oldHeight = input.value.style.height || computedStyle.height\n // Probe scrollHeight by temporarily changing the height to `auto`\n height.value = 'auto'\n await nextTick() // We need to wait for the dom to update. These cannot be batched in the same tick\n // Re-check input.value after await since the element may have been unmounted\n // (e.g., during SSR hydration when BFormGroup re-renders its wrapper element)\n if (!input.value) return\n const {scrollHeight} = input.value\n // Place the original old height back on the element, just in case `computedProp`\n // returns the same value as before\n height.value = oldHeight\n await nextTick() // We need to wait for the dom to update. These cannot be batched in the same tick\n if (!input.value) return\n\n // Calculate content height in 'rows' (scrollHeight includes padding but not border)\n const contentRows = Math.max((scrollHeight - padding) / lineHeight, 2)\n // Calculate number of rows to display (limited within min/max rows)\n const rows = Math.min(Math.max(contentRows, computedMinRows.value), computedMaxRows.value)\n // Calculate the required height of the textarea including border and padding (in pixels)\n const newHeight = Math.max(Math.ceil(rows * lineHeight + offset), minHeight)\n\n // Computed height remains the larger of `oldHeight` and new `height`,\n // when height is in `sticky` mode (prop `no-auto-shrink` is true)\n if (toValue(noAutoShrink) && (Number.parseFloat(oldHeight.toString()) || 0) > newHeight) {\n height.value = oldHeight\n return\n }\n\n // Return the new computed CSS height in px units\n height.value = `${newHeight}px`\n }\n\n onMounted(handleHeightChange)\n\n const computedStyles = computed<CSSProperties>(() => ({\n resize: 'none',\n height:\n typeof height.value === 'string'\n ? height.value\n : height.value\n ? `${height.value}px`\n : undefined,\n }))\n\n return {\n onInput: handleHeightChange,\n computedStyles,\n computedRows,\n }\n}\n","<template>\n <textarea\n :id=\"computedId\"\n ref=\"_input\"\n :class=\"computedClasses\"\n :name=\"props.name || undefined\"\n :form=\"props.form || undefined\"\n :value=\"modelValue ?? undefined\"\n :disabled=\"isDisabled\"\n :placeholder=\"props.placeholder\"\n :required=\"props.required || undefined\"\n :autocomplete=\"props.autocomplete || undefined\"\n :readonly=\"props.readonly || props.plaintext\"\n :aria-required=\"props.required || undefined\"\n :aria-invalid=\"computedAriaInvalid\"\n :rows=\"computedRows || 2\"\n :style=\"computedStyles\"\n :wrap=\"props.wrap || undefined\"\n @input=\"\n (e) => {\n onInput(e)\n handleHeightChange()\n }\n \"\n @change=\"onChange\"\n @blur=\"onBlur\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport type {BFormTextareaProps} from '../../types/ComponentProps'\nimport {computed, type CSSProperties, useTemplateRef} from 'vue'\nimport {useDefaults} from '../../composables/useDefaults'\nimport {normalizeInput} from '../../utils/normalizeInput'\nimport {useFormInput} from '../../composables/useFormInput'\nimport {useTextareaResize} from '../../composables/useTextareaResize'\n\nconst _props = withDefaults(defineProps<Omit<BFormTextareaProps, 'modelValue'>>(), {\n // CommonInputProps\n ariaInvalid: undefined,\n autocomplete: undefined,\n autofocus: false,\n debounce: 0,\n debounceMaxWait: Number.NaN,\n disabled: false,\n form: undefined,\n formatter: undefined,\n id: undefined,\n lazyFormatter: false,\n list: undefined,\n modelValue: '',\n name: undefined,\n placeholder: undefined,\n plaintext: false,\n readonly: false,\n required: false,\n size: undefined,\n state: undefined,\n // End CommonInputProps\n noResize: false,\n noAutoShrink: false,\n maxRows: undefined,\n rows: 2,\n wrap: 'soft',\n})\nconst props = useDefaults(_props, 'BFormTextarea')\n\nconst [modelValue, modelModifiers] = defineModel<\n Exclude<BFormTextareaProps['modelValue'], undefined>,\n 'trim' | 'lazy' | 'number'\n>({\n default: '',\n set: (v) => normalizeInput(v, modelModifiers),\n})\n\nconst input = useTemplateRef('_input')\n\nconst {\n computedId,\n computedAriaInvalid,\n onInput,\n stateClass,\n onChange,\n onBlur,\n focus,\n blur,\n isDisabled,\n} = useFormInput(props, input, modelValue, modelModifiers)\n\nconst computedClasses = computed(() => [\n stateClass.value,\n props.plaintext ? 'form-control-plaintext' : 'form-control',\n {\n [`form-control-${props.size}`]: !!props.size,\n },\n])\n\nconst {\n computedStyles: resizeStyles,\n onInput: handleHeightChange,\n computedRows,\n} = useTextareaResize(input, {\n maxRows: () => props.maxRows,\n rows: () => props.rows,\n noAutoShrink: () => props.noAutoShrink,\n})\n\nconst computedStyles = computed<CSSProperties>(() => ({\n resize: props.noResize ? 'none' : undefined,\n ...(props.maxRows || props.noAutoShrink ? resizeStyles.value : undefined),\n}))\n\ndefineExpose({\n blur,\n element: input,\n flushDebounce: onBlur,\n focus,\n})\n</script>\n","<template>\n <textarea\n :id=\"computedId\"\n ref=\"_input\"\n :class=\"computedClasses\"\n :name=\"props.name || undefined\"\n :form=\"props.form || undefined\"\n :value=\"modelValue ?? undefined\"\n :disabled=\"isDisabled\"\n :placeholder=\"props.placeholder\"\n :required=\"props.required || undefined\"\n :autocomplete=\"props.autocomplete || undefined\"\n :readonly=\"props.readonly || props.plaintext\"\n :aria-required=\"props.required || undefined\"\n :aria-invalid=\"computedAriaInvalid\"\n :rows=\"computedRows || 2\"\n :style=\"computedStyles\"\n :wrap=\"props.wrap || undefined\"\n @input=\"\n (e) => {\n onInput(e)\n handleHeightChange()\n }\n \"\n @change=\"onChange\"\n @blur=\"onBlur\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport type {BFormTextareaProps} from '../../types/ComponentProps'\nimport {computed, type CSSProperties, useTemplateRef} from 'vue'\nimport {useDefaults} from '../../composables/useDefaults'\nimport {normalizeInput} from '../../utils/normalizeInput'\nimport {useFormInput} from '../../composables/useFormInput'\nimport {useTextareaResize} from '../../composables/useTextareaResize'\n\nconst _props = withDefaults(defineProps<Omit<BFormTextareaProps, 'modelValue'>>(), {\n // CommonInputProps\n ariaInvalid: undefined,\n autocomplete: undefined,\n autofocus: false,\n debounce: 0,\n debounceMaxWait: Number.NaN,\n disabled: false,\n form: undefined,\n formatter: undefined,\n id: undefined,\n lazyFormatter: false,\n list: undefined,\n modelValue: '',\n name: undefined,\n placeholder: undefined,\n plaintext: false,\n readonly: false,\n required: false,\n size: undefined,\n state: undefined,\n // End CommonInputProps\n noResize: false,\n noAutoShrink: false,\n maxRows: undefined,\n rows: 2,\n wrap: 'soft',\n})\nconst props = useDefaults(_props, 'BFormTextarea')\n\nconst [modelValue, modelModifiers] = defineModel<\n Exclude<BFormTextareaProps['modelValue'], undefined>,\n 'trim' | 'lazy' | 'number'\n>({\n default: '',\n set: (v) => normalizeInput(v, modelModifiers),\n})\n\nconst input = useTemplateRef('_input')\n\nconst {\n computedId,\n computedAriaInvalid,\n onInput,\n stateClass,\n onChange,\n onBlur,\n focus,\n blur,\n isDisabled,\n} = useFormInput(props, input, modelValue, modelModifiers)\n\nconst computedClasses = computed(() => [\n stateClass.value,\n props.plaintext ? 'form-control-plaintext' : 'form-control',\n {\n [`form-control-${props.size}`]: !!props.size,\n },\n])\n\nconst {\n computedStyles: resizeStyles,\n onInput: handleHeightChange,\n computedRows,\n} = useTextareaResize(input, {\n maxRows: () => props.maxRows,\n rows: () => props.rows,\n noAutoShrink: () => props.noAutoShrink,\n})\n\nconst computedStyles = computed<CSSProperties>(() => ({\n resize: props.noResize ? 'none' : undefined,\n ...(props.maxRows || props.noAutoShrink ? resizeStyles.value : undefined),\n}))\n\ndefineExpose({\n blur,\n element: input,\n flushDebounce: onBlur,\n focus,\n})\n</script>\n"],"mappings":"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"}