diff --git a/components/input/ClearableLabeledInput.tsx b/components/input/ClearableLabeledInput.tsx index 89f1ac406..cbb34193b 100644 --- a/components/input/ClearableLabeledInput.tsx +++ b/components/input/ClearableLabeledInput.tsx @@ -39,7 +39,7 @@ export default defineComponent({ addonAfter: PropTypes.any, readonly: PropTypes.looseBool, focused: PropTypes.looseBool, - bordered: PropTypes.looseBool, + bordered: PropTypes.looseBool.def(true), triggerFocus: { type: Function as PropType<() => void> }, }, setup(props, { slots, attrs }) { @@ -51,7 +51,7 @@ export default defineComponent({ } }; const renderClearIcon = (prefixCls: string) => { - const { allowClear, value, disabled, readonly, handleReset } = props; + const { allowClear, value, disabled, readonly, handleReset, suffix = slots.suffix } = props; if (!allowClear) { return null; } @@ -60,9 +60,12 @@ export default defineComponent({ return ( e.preventDefault()} class={classNames( { [`${className}-hidden`]: !needClear, + [`${className}-has-suffix`]: !!suffix, }, className, )} diff --git a/components/input/Group.tsx b/components/input/Group.tsx index 7e6f947d6..30b9e8b51 100644 --- a/components/input/Group.tsx +++ b/components/input/Group.tsx @@ -29,7 +29,6 @@ export default defineComponent({ }; }); return () => { - const {} = props; return ( props.value, () => { - if (props.value !== controlDefaultValue) { + if (props.value !== undefined) { + stateValue.value = props.value; + } + }, + ); + watch( + () => props.disabled, + () => { + if (props.value !== undefined) { stateValue.value = props.value; } }, @@ -181,7 +188,7 @@ export default defineComponent({ expose({ focus, blur, - inputRef, + input: inputRef, stateValue, setSelectionRange, select, @@ -217,7 +224,7 @@ export default defineComponent({ if (stateValue.value === value) { return; } - if (props.value === controlDefaultValue) { + if (props.value === undefined) { stateValue.value = value; } else { instance.update(); @@ -234,9 +241,10 @@ export default defineComponent({ }; const handleChange = (e: ChangeEvent) => { - const { value, composing, isComposing } = e.target as any; + const { value, composing } = e.target as any; // https://github.com/vueComponent/ant-design-vue/issues/2203 - if (((isComposing || composing) && props.lazy) || stateValue.value === value) return; + if ((((e as any).isComposing || composing) && props.lazy) || stateValue.value === value) + return; const newVal = e.target.value; resolveOnChange(inputRef.value, e, triggerChange); setValue(newVal, () => { @@ -270,6 +278,7 @@ export default defineComponent({ disabled, bordered = true, valueModifiers = {}, + htmlSize, } = props; const otherProps = omit(props as InputProps & { inputType: any; placeholder: string }, [ 'prefixCls', @@ -285,9 +294,11 @@ export default defineComponent({ 'size', 'inputType', 'bordered', + 'htmlSize', ]); const inputProps = { ...otherProps, + ...attrs, autocomplete: autocomplete.value, onChange: handleChange, onInput: handleChange, @@ -302,6 +313,7 @@ export default defineComponent({ ), ref: inputRef, key: 'ant-input', + size: htmlSize, }; if (valueModifiers.lazy) { delete inputProps.onInput; @@ -321,7 +333,7 @@ export default defineComponent({ inputType: 'input', value: fixControlledValue(stateValue.value), handleReset, - focused: focused.value, + focused: focused.value && props.disabled, }; return ( diff --git a/components/input/ResizableTextArea.tsx b/components/input/ResizableTextArea.tsx index a8f7be9c9..edd96084c 100644 --- a/components/input/ResizableTextArea.tsx +++ b/components/input/ResizableTextArea.tsx @@ -1,125 +1,100 @@ -import type { PropType, VNode } from 'vue'; -import { nextTick, defineComponent, withDirectives } from 'vue'; +import type { CSSProperties, VNode } from 'vue'; +import { + 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 BaseMixin from '../_util/BaseMixin'; -import inputProps from './inputProps'; -import PropTypes from '../_util/vue-types'; -import { getOptionProps } from '../_util/props-util'; 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; -export interface AutoSizeType { - minRows?: number; - maxRows?: number; -} - -const TextAreaProps = { - ...inputProps, - autosize: { type: [Boolean, Object] as PropType, default: undefined }, - autoSize: { type: [Boolean, Object] as PropType, default: undefined }, - onResize: PropTypes.func, -}; - const ResizableTextArea = defineComponent({ name: 'ResizableTextArea', mixins: [BaseMixin], inheritAttrs: false, - props: TextAreaProps, - setup() { - return { - nextFrameActionId: undefined, - textArea: null, - resizeFrameId: undefined, - }; - }, - data() { - return { - textareaStyles: {}, - resizeStatus: RESIZE_STATUS_NONE, - }; - }, - watch: { - value() { - nextTick(() => { - this.resizeTextarea(); - }); - }, - }, - mounted() { - this.resizeTextarea(); - }, - beforeUnmount() { - raf.cancel(this.nextFrameActionId); - raf.cancel(this.resizeFrameId); - }, - methods: { - saveTextArea(textArea: HTMLTextAreaElement) { - this.textArea = textArea; - }, - handleResize(size: { width: number; height: number }) { - const { resizeStatus } = this.$data; + 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); + }); - if (resizeStatus !== RESIZE_STATUS_NONE) { - return; - } - this.$emit('resize', size); - }, - resizeOnNextFrame() { - raf.cancel(this.nextFrameActionId); - this.nextFrameActionId = raf(this.resizeTextarea); - }, - - resizeTextarea() { - const autoSize = this.$props.autoSize || this.$props.autosize; - if (!autoSize || !this.textArea) { - return; - } - const { minRows, maxRows } = autoSize; - const textareaStyles = calculateNodeHeight(this.textArea, false, minRows, maxRows); - this.setState({ textareaStyles, resizeStatus: RESIZE_STATUS_RESIZING }, () => { - raf.cancel(this.resizeFrameId); - this.resizeFrameId = raf(() => { - this.setState({ resizeStatus: RESIZE_STATUS_RESIZED }, () => { - this.resizeFrameId = raf(() => { - this.setState({ resizeStatus: RESIZE_STATUS_NONE }); - this.fixFirefoxAutoScroll(); - }); - }); - }); - }); - }, // https://github.com/ant-design/ant-design/issues/21870 - fixFirefoxAutoScroll() { + const fixFirefoxAutoScroll = () => { try { - if (document.activeElement === this.textArea) { - const currentStart = this.textArea.selectionStart; - const currentEnd = this.textArea.selectionEnd; - this.textArea.setSelectionRange(currentStart, currentEnd); + 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 } - }, + }; - renderTextArea() { - const props: any = { ...getOptionProps(this), ...this.$attrs }; - const { prefixCls, autoSize, autosize, disabled, class: className } = props; - const { textareaStyles, resizeStatus } = this.$data; - warning( - autosize === undefined, - 'Input.TextArea', - 'autosize is deprecated, please use autoSize instead.', - ); + 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', @@ -130,23 +105,19 @@ const ResizableTextArea = defineComponent({ 'type', 'lazy', ]); - const cls = classNames(prefixCls, className, { + const cls = classNames(prefixCls, attrs.class, { [`${prefixCls}-disabled`]: disabled, }); - // Fix https://github.com/ant-design/ant-design/issues/6776 - // Make sure it could be reset when using form.getFieldDecorator - if ('value' in otherProps) { - otherProps.value = otherProps.value || ''; - } const style = { - ...props.style, - ...textareaStyles, - ...(resizeStatus === RESIZE_STATUS_RESIZING + ...(attrs.style as CSSProperties), + ...textareaStyles.value, + ...(resizeStatus.value === RESIZE_STATUS_RESIZING ? { overflowX: 'hidden', overflowY: 'hidden' } : null), }; const textareaProps: any = { ...otherProps, + ...attrs, style, class: cls, }; @@ -154,17 +125,32 @@ const ResizableTextArea = defineComponent({ delete textareaProps.autofocus; } return ( - - {withDirectives((