vuecssuiant-designantdreactantantd-vueenterprisefrontendui-designvue-antdvue-antd-uivue3vuecomponent
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
4.5 KiB
167 lines
4.5 KiB
import type { VNode } from 'vue'; |
|
import { |
|
onMounted, |
|
getCurrentInstance, |
|
watch, |
|
onBeforeUnmount, |
|
ref, |
|
nextTick, |
|
defineComponent, |
|
withDirectives, |
|
} from 'vue'; |
|
import ResizeObserver from '../vc-resize-observer'; |
|
import classNames from '../_util/classNames'; |
|
import calculateNodeHeight from './calculateNodeHeight'; |
|
import raf from '../_util/raf'; |
|
import warning from '../_util/warning'; |
|
import antInput from '../_util/antInputDirective'; |
|
import omit from '../_util/omit'; |
|
import { textAreaProps } from './inputProps'; |
|
|
|
const RESIZE_STATUS_NONE = 0; |
|
const RESIZE_STATUS_RESIZING = 1; |
|
const RESIZE_STATUS_RESIZED = 2; |
|
|
|
const ResizableTextArea = defineComponent({ |
|
compatConfig: { MODE: 3 }, |
|
name: 'ResizableTextArea', |
|
inheritAttrs: false, |
|
props: textAreaProps(), |
|
setup(props, { attrs, emit, expose }) { |
|
let nextFrameActionId: any; |
|
let resizeFrameId: any; |
|
const textAreaRef = ref(); |
|
const textareaStyles = ref({}); |
|
const resizeStatus = ref(RESIZE_STATUS_NONE); |
|
onBeforeUnmount(() => { |
|
raf.cancel(nextFrameActionId); |
|
raf.cancel(resizeFrameId); |
|
}); |
|
|
|
// https://github.com/ant-design/ant-design/issues/21870 |
|
const fixFirefoxAutoScroll = () => { |
|
try { |
|
if (document.activeElement === textAreaRef.value) { |
|
const currentStart = textAreaRef.value.selectionStart; |
|
const currentEnd = textAreaRef.value.selectionEnd; |
|
textAreaRef.value.setSelectionRange(currentStart, currentEnd); |
|
} |
|
} catch (e) { |
|
// Fix error in Chrome: |
|
// Failed to read the 'selectionStart' property from 'HTMLInputElement' |
|
// http://stackoverflow.com/q/21177489/3040605 |
|
} |
|
}; |
|
|
|
const resizeTextarea = () => { |
|
const autoSize = props.autoSize || props.autosize; |
|
if (!autoSize || !textAreaRef.value) { |
|
return; |
|
} |
|
const { minRows, maxRows } = autoSize; |
|
textareaStyles.value = calculateNodeHeight(textAreaRef.value, false, minRows, maxRows); |
|
resizeStatus.value = RESIZE_STATUS_RESIZING; |
|
raf.cancel(resizeFrameId); |
|
resizeFrameId = raf(() => { |
|
resizeStatus.value = RESIZE_STATUS_RESIZED; |
|
resizeFrameId = raf(() => { |
|
resizeStatus.value = RESIZE_STATUS_NONE; |
|
fixFirefoxAutoScroll(); |
|
}); |
|
}); |
|
}; |
|
|
|
const resizeOnNextFrame = () => { |
|
raf.cancel(nextFrameActionId); |
|
nextFrameActionId = raf(resizeTextarea); |
|
}; |
|
|
|
const handleResize = (size: { width: number; height: number }) => { |
|
if (resizeStatus.value !== RESIZE_STATUS_NONE) { |
|
return; |
|
} |
|
emit('resize', size); |
|
|
|
const autoSize = props.autoSize || props.autosize; |
|
if (autoSize) { |
|
resizeOnNextFrame(); |
|
} |
|
}; |
|
warning( |
|
props.autosize === undefined, |
|
'Input.TextArea', |
|
'autosize is deprecated, please use autoSize instead.', |
|
); |
|
|
|
const renderTextArea = () => { |
|
const { prefixCls, autoSize, autosize, disabled } = props; |
|
const otherProps = omit(props, [ |
|
'prefixCls', |
|
'onPressEnter', |
|
'autoSize', |
|
'autosize', |
|
'defaultValue', |
|
'allowClear', |
|
'type', |
|
'lazy', |
|
'maxlength', |
|
'valueModifiers', |
|
]); |
|
const cls = classNames(prefixCls, attrs.class, { |
|
[`${prefixCls}-disabled`]: disabled, |
|
}); |
|
const style = [ |
|
attrs.style, |
|
textareaStyles.value, |
|
resizeStatus.value === RESIZE_STATUS_RESIZING |
|
? { overflowX: 'hidden', overflowY: 'hidden' } |
|
: null, |
|
]; |
|
const textareaProps: any = { |
|
...otherProps, |
|
...attrs, |
|
style, |
|
class: cls, |
|
}; |
|
if (!textareaProps.autofocus) { |
|
delete textareaProps.autofocus; |
|
} |
|
if (textareaProps.rows === 0) { |
|
delete textareaProps.rows; |
|
} |
|
return ( |
|
<ResizeObserver onResize={handleResize} disabled={!(autoSize || autosize)}> |
|
{withDirectives((<textarea {...textareaProps} ref={textAreaRef} />) as VNode, [ |
|
[antInput], |
|
])} |
|
</ResizeObserver> |
|
); |
|
}; |
|
|
|
watch( |
|
() => props.value, |
|
() => { |
|
nextTick(() => { |
|
resizeTextarea(); |
|
}); |
|
}, |
|
); |
|
onMounted(() => { |
|
nextTick(() => { |
|
resizeTextarea(); |
|
}); |
|
}); |
|
const instance = getCurrentInstance(); |
|
expose({ |
|
resizeTextarea, |
|
textArea: textAreaRef, |
|
instance, |
|
}); |
|
|
|
return () => { |
|
return renderTextArea(); |
|
}; |
|
}, |
|
}); |
|
|
|
export default ResizableTextArea;
|
|
|