diff --git a/examples/input/demos/status.vue b/examples/input/demos/status.vue index 4faf01996..921b265e9 100644 --- a/examples/input/demos/status.vue +++ b/examples/input/demos/status.vue @@ -1,7 +1,13 @@ diff --git a/examples/input/input.md b/examples/input/input.md index cf254c59f..475302877 100644 --- a/examples/input/input.md +++ b/examples/input/input.md @@ -13,6 +13,7 @@ autocomplete | Boolean | false | 是否开启自动填充功能 | N autofocus | Boolean | false | 自动聚焦 | N clearable | Boolean | false | 是否可清空 | N disabled | Boolean | false | 是否禁用输入框 | N +format | Function | - | 【讨论中】指定输入框展示值的格式。TS 类型:`(value: number | number) => number | string` | N label | String / Slot / Function | - | 左侧文本。TS 类型:`string | TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N maxcharacter | Number | - | 用户最多可以输入的字符个数,一个中文汉字表示两个字符长度。`maxcharacter` 和 `maxlength` 二选一使用 | N maxlength | Number | - | 用户最多可以输入的文本长度。值小于等于 0 的时候,则不限制输入长度。`maxcharacter` 和 `maxlength` 二选一使用 | N @@ -38,7 +39,7 @@ onKeypress | Function | | TS 类型:`(value: InputValue, context: { e: Keyboa onKeyup | Function | | TS 类型:`(value: InputValue, context: { e: KeyboardEvent }) => void`
释放键盘时触发 | N onMouseenter | Function | | TS 类型:`(context: { e: MouseEvent }) => void`
进入输入框时触发 | N onMouseleave | Function | | TS 类型:`(context: { e: MouseEvent }) => void`
离开输入框时触发 | N -onPaste | Function | | TS 类型:`(context: { e: ClipboardEvent; pasteValue: string }) => void`
粘贴事件,`pasteValue` 表示粘贴板的内容 | N +onPaste | Function | | TS 类型:`(context: { e: ClipboardEvent; pasteValue: string }) => void`
粘贴事件,`pasteValue` 表示粘贴板的内容 | N ### Input Events @@ -54,4 +55,4 @@ keypress | `(value: InputValue, context: { e: KeyboardEvent })` | 按下字符 keyup | `(value: InputValue, context: { e: KeyboardEvent })` | 释放键盘时触发 mouseenter | `(context: { e: MouseEvent })` | 进入输入框时触发 mouseleave | `(context: { e: MouseEvent })` | 离开输入框时触发 -paste | `(context: { e: ClipboardEvent; pasteValue: string })` | 粘贴事件,`pasteValue` 表示粘贴板的内容 +paste | `(context: { e: ClipboardEvent; pasteValue: string })` | 粘贴事件,`pasteValue` 表示粘贴板的内容 diff --git a/src/input/input.tsx b/src/input/input.tsx index c9c65e619..73136b659 100644 --- a/src/input/input.tsx +++ b/src/input/input.tsx @@ -4,7 +4,7 @@ import { InputValue, TdInputProps } from './type'; import { getCharacterLength, omit } from '../utils/helper'; import getConfigReceiverMixins, { InputConfig } from '../config-provider/config-receiver'; import mixins from '../utils/mixins'; - +import { ClassName } from '../common'; import CLASSNAMES from '../utils/classnames'; import { emitEvent } from '../utils/event'; import { prefix } from '../config'; @@ -12,6 +12,8 @@ import props from './props'; import { renderTNodeJSX } from '../utils/render-tnode'; const name = `${prefix}-input`; +const INPUT_WRAP_CLASS = `${prefix}-input__wrap`; +const INPUT_TIPS_CLASS = `${prefix}-input__tips`; function getValidAttrs(obj: object): object { const newObj = {}; @@ -54,6 +56,20 @@ export default mixins(getConfigReceiverMixins('input type: this.renderType, }); }, + inputClasses(): ClassName { + return [ + name, + CLASSNAMES.SIZE[this.size] || '', + { + [CLASSNAMES.STATUS.disabled]: this.disabled, + [CLASSNAMES.STATUS.focused]: this.focused, + [`${prefix}-is-${this.status}`]: this.status, + [`${prefix}-is-disabled`]: this.disabled, + [`${prefix}-is-readonly`]: this.readonly, + [`${name}--focused`]: this.focused, + }, + ]; + }, }, watch: { autofocus: { @@ -67,83 +83,11 @@ export default mixins(getConfigReceiverMixins('input immediate: true, }, }, + created() { this.composing = false; }, - render(h: CreateElement): VNode { - const inputEvents = getValidAttrs({ - focus: this.emitFocus, - blur: this.emitBlur, - keydown: this.handleKeydown, - keyup: this.handleKeyUp, - keypress: this.handleKeypress, - // input的change事件是失去焦点或者keydown的时候执行。这与api定义的change不符,所以不做任何变化。 - // eslint-disable-next-line @typescript-eslint/no-empty-function - change: () => {}, - }); - - const wrapperAttrs = omit(this.$attrs, Object.keys(this.inputAttrs)); - const wrapperEvents = omit(this.$listeners, [...Object.keys(inputEvents), 'input']); - - const prefixIcon = this.renderIcon(h, this.prefixIcon, 'prefix-icon'); - let suffixIcon = this.renderIcon(h, this.suffixIcon, 'suffix-icon'); - - const label = renderTNodeJSX(this, 'label'); - const suffix = renderTNodeJSX(this, 'suffix'); - - const labelContent = label ?
{label}
: null; - const suffixContent = suffix ?
{suffix}
: null; - - if (this.showClear) { - suffixIcon = ; - } - - if (this.type === 'password') { - if (this.renderType === 'password') { - suffixIcon = ; - } else if (this.renderType === 'text') { - suffixIcon = ; - } - } - const classes = [ - name, - CLASSNAMES.SIZE[this.size] || '', - { - [CLASSNAMES.STATUS.disabled]: this.disabled, - [CLASSNAMES.STATUS.focused]: this.focused, - [`${prefix}-is-${this.status}`]: this.status, - [`${name}--prefix`]: prefixIcon || labelContent, - [`${name}--suffix`]: suffixIcon || suffixContent, - [`${name}--focused`]: this.focused, - }, - ]; - return ( -
this.mouseEvent(true)} - onMouseleave={() => this.mouseEvent(false)} - {...{ attrs: wrapperAttrs, on: wrapperEvents }} - > - {prefixIcon ? {prefixIcon} : null} - {labelContent} - - {suffixContent} - {suffixIcon ? ( - - {suffixIcon} - - ) : null} -
- ); - }, methods: { mouseEvent(v: boolean) { this.isHover = v; @@ -197,6 +141,12 @@ export default mixins(getConfigReceiverMixins('input if (this.disabled) return; emitEvent>(this, 'keypress', this.value, { e }); }, + onHandlePaste(e: ClipboardEvent) { + if (this.disabled) return; + // @ts-ignore + const clipData = e.clipboardData || window.clipboardData; + emitEvent>(this, 'paste', { e, pasteValue: clipData?.getData('text/plain') }); + }, emitPassword() { const { renderType } = this; const toggleType = renderType === 'password' ? 'text' : 'password'; @@ -231,5 +181,97 @@ export default mixins(getConfigReceiverMixins('input // 受控,重要,勿删 this.$nextTick(() => this.setInputValue(this.value)); }, + + onInputMouseenter(e: MouseEvent) { + this.mouseEvent(true); + this.onMouseenter?.({ e }); + }, + + onInputMouseleave(e: MouseEvent) { + this.mouseEvent(false); + this.onMouseleave?.({ e }); + }, + }, + + render(h: CreateElement): VNode { + const inputEvents = getValidAttrs({ + focus: this.emitFocus, + blur: this.emitBlur, + keydown: this.handleKeydown, + keyup: this.handleKeyUp, + keypress: this.handleKeypress, + paste: this.onHandlePaste, + // input的change事件是失去焦点或者keydown的时候执行。这与api定义的change不符,所以不做任何变化。 + // eslint-disable-next-line @typescript-eslint/no-empty-function + change: () => {}, + }); + + const wrapperAttrs = omit(this.$attrs, Object.keys(this.inputAttrs)); + const wrapperEvents = omit(this.$listeners, [...Object.keys(inputEvents), 'input', 'paste']); + + const prefixIcon = this.renderIcon(h, this.prefixIcon, 'prefix-icon'); + let suffixIcon = this.renderIcon(h, this.suffixIcon, 'suffix-icon'); + + const label = renderTNodeJSX(this, 'label'); + const suffix = renderTNodeJSX(this, 'suffix'); + + const labelContent = label ?
{label}
: null; + const suffixContent = suffix ?
{suffix}
: null; + + if (this.showClear) { + suffixIcon = ; + } + + if (this.type === 'password') { + if (this.renderType === 'password') { + suffixIcon = ; + } else if (this.renderType === 'text') { + suffixIcon = ; + } + } + + const classes = [ + this.inputClasses, + { + [`${name}--prefix`]: prefixIcon || labelContent, + [`${name}--suffix`]: suffixIcon || suffixContent, + }, + ]; + const inputNode = ( +
+ {prefixIcon ? {prefixIcon} : null} + {labelContent} + + {suffixContent} + {suffixIcon ? ( + + {suffixIcon} + + ) : null} +
+ ); + + const tips = renderTNodeJSX(this, 'tips'); + if (tips) { + return ( +
+ {inputNode} +
{tips}
+
+ ); + } + return inputNode; }, }); diff --git a/src/input/props.ts b/src/input/props.ts index d80c8b32b..9fbb72ac0 100644 --- a/src/input/props.ts +++ b/src/input/props.ts @@ -2,7 +2,6 @@ /** * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC - * updated at 2021-12-28 11:39:46 * */ import { TdInputProps } from './type'; @@ -17,6 +16,10 @@ export default { clearable: Boolean, /** 是否禁用输入框 */ disabled: Boolean, + /** 【讨论中】指定输入框展示值的格式 */ + format: { + type: Function as PropType, + }, /** 左侧文本 */ label: { type: [String, Function] as PropType, @@ -56,7 +59,6 @@ export default { /** 输入框状态 */ status: { type: String as PropType, - default: undefined as TdInputProps['status'], validator(val: TdInputProps['status']): boolean { return ['success', 'warning', 'error'].includes(val); }, @@ -69,6 +71,10 @@ export default { suffixIcon: { type: Function as PropType, }, + /** 输入框下方提示文本,会根据不同的 `status` 呈现不同的样式 */ + tips: { + type: [String, Function] as PropType, + }, /** 输入框类型 */ type: { type: String as PropType, @@ -101,4 +107,10 @@ export default { onKeypress: Function as PropType, /** 释放键盘时触发 */ onKeyup: Function as PropType, + /** 进入输入框时触发 */ + onMouseenter: Function as PropType, + /** 离开输入框时触发 */ + onMouseleave: Function as PropType, + /** 粘贴事件,`pasteValue` 表示粘贴板的内容 */ + onPaste: Function as PropType, }; diff --git a/src/input/type.ts b/src/input/type.ts index ba44232d2..e42ae409e 100644 --- a/src/input/type.ts +++ b/src/input/type.ts @@ -2,7 +2,6 @@ /** * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC - * updated at 2021-12-28 11:39:46 * */ import { TNode, SizeEnum } from '../common'; @@ -28,6 +27,10 @@ export interface TdInputProps { * @default false */ disabled?: boolean; + /** + * 【讨论中】指定输入框展示值的格式 + */ + format?: (value: number | number) => number | string; /** * 左侧文本 */ @@ -75,6 +78,10 @@ export interface TdInputProps { * 组件后置图标 */ suffixIcon?: TNode; + /** + * 输入框下方提示文本,会根据不同的 `status` 呈现不同的样式 + */ + tips?: string | TNode; /** * 输入框类型 * @default text @@ -120,6 +127,18 @@ export interface TdInputProps { * 释放键盘时触发 */ onKeyup?: (value: InputValue, context: { e: KeyboardEvent }) => void; + /** + * 进入输入框时触发 + */ + onMouseenter?: (context: { e: MouseEvent }) => void; + /** + * 离开输入框时触发 + */ + onMouseleave?: (context: { e: MouseEvent }) => void; + /** + * 粘贴事件,`pasteValue` 表示粘贴板的内容 + */ + onPaste?: (context: { e: ClipboardEvent; pasteValue: string }) => void; } export type InputValue = string | number; diff --git a/test/ssr/__snapshots__/ssr.test.js.snap b/test/ssr/__snapshots__/ssr.test.js.snap index 5ef08eaf1..2ba69167f 100644 --- a/test/ssr/__snapshots__/ssr.test.js.snap +++ b/test/ssr/__snapshots__/ssr.test.js.snap @@ -2943,7 +2943,7 @@ exports[`ssr snapshot test renders ./examples/config-provider/demos/date-picker.
-
+


@@ -2951,7 +2951,7 @@ exports[`ssr snapshot test renders ./examples/config-provider/demos/date-picker.
-
+


@@ -2959,7 +2959,7 @@ exports[`ssr snapshot test renders ./examples/config-provider/demos/date-picker.
-
+


@@ -2967,7 +2967,7 @@ exports[`ssr snapshot test renders ./examples/config-provider/demos/date-picker.
-
+


@@ -2975,7 +2975,7 @@ exports[`ssr snapshot test renders ./examples/config-provider/demos/date-picker.
-
+


@@ -2983,7 +2983,7 @@ exports[`ssr snapshot test renders ./examples/config-provider/demos/date-picker.
-
+


@@ -3145,7 +3145,7 @@ exports[`ssr snapshot test renders ./examples/config-provider/demos/others.vue c
-
+
select time



Fearure Tag Fearure Tag Fearure Tag Fearure Tag

@@ -3360,7 +3360,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/base.vue correct
-
+
@@ -3373,7 +3373,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/custom-icon.vue
-
+
@@ -3386,7 +3386,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/date-presets.vue
-
+
@@ -3400,7 +3400,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/date-presets-alt
-
+
@@ -3410,7 +3410,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/date-presets-alt
-
+
@@ -3424,7 +3424,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/date-presets-tim
-
+
@@ -3437,7 +3437,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/date-range.vue c
-
+


@@ -3445,7 +3445,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/date-range.vue c
-
+
@@ -3458,7 +3458,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/date-time.vue co
-
+
@@ -3472,7 +3472,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/disable-date.vue
-
+
@@ -3482,7 +3482,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/disable-date.vue
-
+
@@ -3492,7 +3492,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/disable-date.vue
-
+
@@ -3502,7 +3502,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/disable-date.vue
-
+
@@ -3512,7 +3512,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/disable-date.vue
-
+
@@ -3526,7 +3526,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/first-day-of-wee
-
+
@@ -3539,7 +3539,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/month.vue correc
-
+
@@ -3552,7 +3552,7 @@ exports[`ssr snapshot test renders ./examples/date-picker/demos/year.vue correct
-
+
@@ -4970,9 +4970,26 @@ exports[`ssr snapshot test renders ./examples/input/demos/size.vue correctly 1`] exports[`ssr snapshot test renders ./examples/input/demos/status.vue correctly 1`] = `
+
-
+

+
+
+
这是普通文本提示
+
+
+
+
校验通过文本提示
+
+
+
+
校验不通过文本提示
+
+
+
+
校验存在严重问题文本提示
+
`; @@ -8431,7 +8448,7 @@ exports[`ssr snapshot test renders ./examples/select/demos/creatable.vue correct
-
+
@@ -8517,7 +8534,7 @@ exports[`ssr snapshot test renders ./examples/select/demos/filterable.vue correc
-
+
@@ -8527,7 +8544,7 @@ exports[`ssr snapshot test renders ./examples/select/demos/filterable.vue correc
-
+
@@ -8720,7 +8737,7 @@ exports[`ssr snapshot test renders ./examples/select/demos/remote-search.vue cor
-
+
@@ -8730,7 +8747,7 @@ exports[`ssr snapshot test renders ./examples/select/demos/remote-search.vue cor
-
+
@@ -13312,14 +13329,14 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/base.vue correct
-
+
::
-
+
::
@@ -13332,7 +13349,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/clearable.vue co
-
+
::
@@ -13340,7 +13357,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/clearable.vue co
-
+
::
@@ -13352,7 +13369,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/disabled.vue cor
-
+
::
@@ -13364,7 +13381,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/format.vue corre
-
+
::
@@ -13377,7 +13394,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/hide-clear-butto
-
+
::
@@ -13385,7 +13402,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/hide-clear-butto
-
+
请选择时间
@@ -13397,7 +13414,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/hm.vue correctly
-
+
请选择时间
@@ -13409,7 +13426,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/hms.vue correctl
-
+
::
@@ -13422,7 +13439,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/keyboard.vue cor
-
+
::
@@ -13430,7 +13447,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/keyboard.vue cor
-
+
::
@@ -13442,7 +13459,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/range.vue correc
-
+
::-::
@@ -13454,7 +13471,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/show-steps.vue c
-
+
请选择时间
@@ -13466,7 +13483,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/step.vue correct
-
+
::
@@ -13478,7 +13495,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/twelve-hour.vue
-
+
::
@@ -13490,7 +13507,7 @@ exports[`ssr snapshot test renders ./examples/time-picker/demos/twelve-hour-meri
-
+
::
diff --git a/test/unit/date-picker/__snapshots__/demo.test.js.snap b/test/unit/date-picker/__snapshots__/demo.test.js.snap index 6b4b79d54..65a3562cd 100644 --- a/test/unit/date-picker/__snapshots__/demo.test.js.snap +++ b/test/unit/date-picker/__snapshots__/demo.test.js.snap @@ -720,7 +720,7 @@ exports[`DatePicker base demo works fine 1`] = ` >
-
+
@@ -179,7 +179,7 @@ exports[`DatePicker :range 1`] = `
-
+
@@ -248,7 +248,7 @@ exports[`DatePicker :value 1`] = `
-
+
diff --git a/test/unit/input/__snapshots__/demo.test.js.snap b/test/unit/input/__snapshots__/demo.test.js.snap index be77343ff..b05ba9379 100644 --- a/test/unit/input/__snapshots__/demo.test.js.snap +++ b/test/unit/input/__snapshots__/demo.test.js.snap @@ -433,6 +433,16 @@ exports[`Input status demo works fine 1`] = ` class="tdesign-demo-block-column" style="max-width: 500px;" > +
+ +
+
@@ -462,5 +472,83 @@ exports[`Input status demo works fine 1`] = ` type="text" />
+ +
+ +
+
+ +
+
+ 这是普通文本提示 +
+
+ +
+
+ +
+
+ 校验通过文本提示 +
+
+ +
+
+ +
+
+ 校验不通过文本提示 +
+
+ +
+
+ +
+
+ 校验存在严重问题文本提示 +
+
`; diff --git a/test/unit/input/__snapshots__/index.test.js.snap b/test/unit/input/__snapshots__/index.test.js.snap index 05565f439..ad51c0cf7 100644 --- a/test/unit/input/__snapshots__/index.test.js.snap +++ b/test/unit/input/__snapshots__/index.test.js.snap @@ -8,7 +8,7 @@ exports[`Input :props :disabled 1`] = `
`; -exports[`Input :props :readonly 1`] = `
`; +exports[`Input :props :readonly 1`] = `
`; exports[`Input :props :size 1`] = `
`; diff --git a/test/unit/select/__snapshots__/demo.test.js.snap b/test/unit/select/__snapshots__/demo.test.js.snap index 6bc244a59..c0fc75a87 100644 --- a/test/unit/select/__snapshots__/demo.test.js.snap +++ b/test/unit/select/__snapshots__/demo.test.js.snap @@ -988,7 +988,7 @@ exports[`Steps Steps creatable demo works fine 1`] = ` +0