diff --git a/packages/varlet-vue2-ui/src/countdown/Countdown.vue b/packages/varlet-vue2-ui/src/countdown/Countdown.vue index 975d9e0..7935d3a 100644 --- a/packages/varlet-vue2-ui/src/countdown/Countdown.vue +++ b/packages/varlet-vue2-ui/src/countdown/Countdown.vue @@ -6,12 +6,11 @@ - diff --git a/packages/varlet-vue2-ui/src/input.zip b/packages/varlet-vue2-ui/src/input.zip new file mode 100644 index 0000000..0332ee0 Binary files /dev/null and b/packages/varlet-vue2-ui/src/input.zip differ diff --git a/packages/varlet-vue2-ui/src/input/Input.vue b/packages/varlet-vue2-ui/src/input/Input.vue new file mode 100644 index 0000000..3f34421 --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/Input.vue @@ -0,0 +1,277 @@ + + + + + diff --git a/packages/varlet-vue2-ui/src/input/__tests__/__snapshots__/index.spec.js.snap b/packages/varlet-vue2-ui/src/input/__tests__/__snapshots__/index.spec.js.snap new file mode 100644 index 0000000..eb36a7a --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/__tests__/__snapshots__/index.spec.js.snap @@ -0,0 +1,346 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test input clear 1`] = ` +"
+
+
+
+
+
+
+
+
+
+ + + +
" +`; + +exports[`test input example 1`] = ` +"
+
基本使用
+
+
+
+
+
+
+ +
+
+
+
+
+ + + +
+
朴素模式
+
+
+
+
+
+
+ +
+
+ + + + +
+
文本域
+
+
+
+
+
+
+ +
+
+
+
+
+ + + +
+
最大长度
+
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
0 / 10
+
+
+
+
禁用
+
+
+
+
+
+
+ +
+
+
+
+
+ + + +
+
只读
+
+
+
+
+
+
+ +
+
+
+
+
+ + + +
+
可清除
+
+
+
+
+
+
+
+
+
+
+ + + +
+
显示图标
+
+
+
+
+
+
+
+
+
+
+ + + +
+
字段校验
+
+
+
+
+
+
+ +
+
+
+
+
+ + + +
+
+
" +`; + +exports[`test input focus & blur 1`] = ` +"
+
+
+
+
+
+ +
+
+
+
+
+ + + +
" +`; + +exports[`test input focus & blur 2`] = ` +"
+
+
+
+
+
+ +
+
+
+
+
+ + + +
" +`; + +exports[`test input hint to be false 1`] = ` +"
+
+
+
+
+
+ +
+
+
+
+
+ + + +
" +`; + +exports[`test input maxlength 1`] = ` +"
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
4/100
+
+
+
" +`; + +exports[`test input validation 1`] = ` +"
+
+
+
+
+
+ +
+
+
+
+
+ +
+
长度必须大于3
+
+
+
+
" +`; + +exports[`test input validation 2`] = ` +"
+
+
+
+
+
+ +
+
+
+
+
+ + + +
" +`; + +exports[`test input validation 3`] = ` +"
+
+
+
+
+
+ +
+
+
+
+
+ + + +
" +`; diff --git a/packages/varlet-vue2-ui/src/input/__tests__/index.spec.js b/packages/varlet-vue2-ui/src/input/__tests__/index.spec.js new file mode 100644 index 0000000..4c2af6f --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/__tests__/index.spec.js @@ -0,0 +1,219 @@ +import example from '../example' +import Input from '..' +import VarInput from '../Input' +import { mount } from '@vue/test-utils' +import Vue from 'vue' +import { delay } from '../../utils/jest' + +test('test input example', () => { + const wrapper = mount(example) + expect(wrapper.html()).toMatchSnapshot() + wrapper.destroy() +}) + +test('test input plugin', () => { + Vue.use(Input) + expect(Vue.component(Input.name)).toBeTruthy() +}) + +test('test input focus & blur', async () => { + const onFocus = jest.fn() + const onBlur = jest.fn() + + const wrapper = mount(VarInput, { + listeners: { + focus: onFocus, + blur: onBlur, + }, + }) + + wrapper.vm.focus() + await wrapper.find('.var-input__input').trigger('focus') + expect(wrapper.html()).toMatchSnapshot() + + wrapper.vm.blur() + await wrapper.find('.var-input__input').trigger('blur') + expect(wrapper.html()).toMatchSnapshot() + + wrapper.destroy() +}) + +test('test input onInput & onChange & onClick', async () => { + const onInput = jest.fn((value) => wrapper.setProps({ value })) + const onChange = jest.fn() + const onClick = jest.fn() + + const wrapper = mount(VarInput, { + propsData: { + value: '', + }, + listeners: { + input: onInput, + change: onChange, + click: onClick, + }, + }) + + await wrapper.trigger('click') + expect(onClick).toHaveBeenCalledTimes(1) + + wrapper.find('.var-input__input').setValue('t') + + await wrapper.find('.var-input__input').trigger('input') + expect(onInput).lastCalledWith('t', new Event('input')) + expect(wrapper.props('value')).toBe('t') + + await wrapper.find('.var-input__input').trigger('change') + expect(onChange).lastCalledWith('t', new Event('input')) + + wrapper.destroy() +}) + +test('test input maxlength', () => { + const wrapper = mount(VarInput, { + propsData: { + value: 'text', + maxlength: 100, + }, + }) + + expect(wrapper.find('.var-form-details__length').text()).toBe('4/100') + expect(wrapper.html()).toMatchSnapshot() +}) + +test('test input hint to be false', () => { + const wrapper = mount(VarInput, { + propsData: { + value: 'text', + hint: false, + }, + }) + + expect(wrapper.html()).toMatchSnapshot() + + wrapper.destroy() +}) + +test('test input clear', async () => { + const onInput = jest.fn((value) => wrapper.setProps({ value })) + const onClear = jest.fn() + + const wrapper = mount(VarInput, { + propsData: { + value: 'text', + clearable: true, + }, + listeners: { + clear: onClear, + input: onInput, + }, + }) + + expect(wrapper.html()).toMatchSnapshot() + + await wrapper.find('.var-input__clear-icon').trigger('click') + expect(onClear).lastCalledWith('') + expect(wrapper.props('value')).toBe('') + + wrapper.destroy() +}) + +const triggerEvents = async (wrapper) => { + await wrapper.find('.var-input__input').trigger('input') + await wrapper.find('.var-input__input').trigger('change') + await wrapper.find('.var-input__clear-icon').trigger('click') + await wrapper.trigger('click') +} + +test('test input disabled', async () => { + const onClear = jest.fn() + const onClick = jest.fn() + const onInput = jest.fn() + const onChange = jest.fn() + + const wrapper = mount(VarInput, { + propsData: { + value: 'hello', + clearable: true, + disabled: true, + }, + listeners: { + input: onInput, + clear: onClear, + click: onClick, + change: onChange, + }, + }) + + await triggerEvents(wrapper) + + expect(onInput).toHaveBeenCalledTimes(0) + expect(onClear).toHaveBeenCalledTimes(0) + expect(onClick).toHaveBeenCalledTimes(0) + expect(onChange).toHaveBeenCalledTimes(0) + + wrapper.destroy() +}) + +test('test input readonly', async () => { + const onClear = jest.fn() + const onClick = jest.fn() + const onInput = jest.fn() + const onChange = jest.fn() + + const wrapper = mount(VarInput, { + propsData: { + value: 'hello', + clearable: true, + readonly: true, + }, + listeners: { + input: onInput, + clear: onClear, + click: onClick, + chnage: onChange, + }, + }) + + await triggerEvents(wrapper) + + expect(onInput).toHaveBeenCalledTimes(0) + expect(onClear).toHaveBeenCalledTimes(0) + expect(onClick).toHaveBeenCalledTimes(2) + expect(onChange).toHaveBeenCalledTimes(0) + + wrapper.destroy() +}) + +test('test input validation', async () => { + const onInput = jest.fn((value) => wrapper.setProps({ value })) + + const wrapper = mount(VarInput, { + propsData: { + value: '', + rules: [(v) => v.length > 3 || '长度必须大于3'], + }, + listeners: { + input: onInput, + }, + }) + + wrapper.find('.var-input__input').setValue('1') + await wrapper.find('.var-input__input').trigger('input') + await delay(16) + expect(wrapper.find('.var-form-details__message').text()).toBe('长度必须大于3') + expect(wrapper.html()).toMatchSnapshot() + + wrapper.vm.reset() + await delay(16) + expect(wrapper.props('value')).toBe('') + expect(wrapper.html()).toMatchSnapshot() + + wrapper.find('.var-input__input').setValue('1234') + await wrapper.find('.var-input__input').trigger('input') + await delay(16) + expect(wrapper.find('.var-form-details__message').exists()).toBeFalsy() + expect(wrapper.html()).toMatchSnapshot() + + wrapper.destroy() +}) diff --git a/packages/varlet-vue2-ui/src/input/docs/en-US.md b/packages/varlet-vue2-ui/src/input/docs/en-US.md new file mode 100644 index 0000000..b2c2a65 --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/docs/en-US.md @@ -0,0 +1,172 @@ +# Input + +### Install + +```js +import Vue from 'vue' +import { Input } from '@varlet/ui' + +Vue.use(Input) +``` + +### Basic Usage + +The behavior of the input box is consistent with the basic content, and the user can always get a string that conforms to the `type rule when inputting + +```js +export default { + data: () =>({ + value: '' + }), +} +``` + +```html + +``` + +### Plain Mode + +If you only need the basic functionality of the component, you can remove some styles through attributes. + +```html + +``` + +### Textarea + +```html + +``` + +### Maxlength + +```html + +``` + +### Disabled + +```html + +``` + +### Readonly + +```html + +``` + +### Clearable + +```html + +``` + +### Display Icon +```js +import Vue from 'vue' +import { Icon } from '@varlet/ui' + +Vue.use(Icon) +``` + +```html + + + + +``` + +### Validate + +The values are validated by passing in an array of validators,If the validator returns `true`, the validation passes. +Other values are converted to text as a user prompt. + +```html + +``` + +## API + +### Props + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| `v-model` | The value of the binding | _string_ | `-` | +| `placeholder` | placeholder | _string_ | `-` | +| `type` | Input type, The optional value is `text` `password` `number` | _string_ | `text` | +| `maxlength` | Maxlength | _string \| number_ | `-` | +| `textarea` | Is it a textarea | _boolean_ | `false` | +| `rows` | Number of lines to display in the textarea | _string \| number_ | `8` | +| `line` | Whether to display a dividing line | _boolean_ | `true` | +| `hint` | Whether to use placeholder as hint | _boolean_ | `true` | +| `text-color` | Text color | _string_ | `-` | +| `focus-color` | The primary color in focus | _string_ | `-` | +| `blur-color` | The primary color in blur | _string_ | `-` | +| `readonly` | Whether the readonly | _boolean_ | `false` | +| `disabled` | Whether the disabled | _boolean_ | `false` | +| `clearable` | Whether the clearable | _boolean_ | `false` | +| `resize` | Whether textarea can be dragged to resize | _boolean_ | `false` | +| `validate-trigger` | Timing to trigger validation, The optional value is `onFocus` `onBlur` `onChange` `onClick` `onClear` `onInput` | _ValidateTriggers[]_ | `['onInput', 'onClear']` | +| `rules` | The validation rules, Returns `true` to indicate that the validation passed,The remaining values are converted to text as user prompts | _Array<(v: string) => any>_ | `-` | + +### Methods + +| Method | Description | Arguments | Return | +| --- | --- | --- | --- | +| `focus` | Focus | `-` | `-` | +| `blur` | Blur | `-` | `-` | +| `validate` | Trigger validate | `-` | `valid: Promise` | +| `resetValidation` | Clearing validate messages | `-` | `-` | +| `reset` | Clear the value of the binding and validate messages | `-` | `-` | + +### Events + +| Event | Description | Arguments | +| --- | --- | --- | +| `focus` | Trigger while focusing | `event: Event` | +| `blur` | Triggered when out of focus | `event: Event` | +| `click` | Triggered on Click | `event: Event` | +| `clear` | Triggered on Clearance | `value: string` | +| `input` | Trigger on input | `value: string`, `event: Event` | +| `change` | Trigger on change | `value: string`, `event: Event` | + +### Slots + +| Slot | Description | Arguments | +| --- | --- | --- | +| `prepend-icon` | Prepend Icon | `-` | +| `append-icon` | Append Icon | `-` | + +### Style Variables +Here are the CSS variables used by the component, Styles can be customized using [StyleProvider](#/en-US/style-provider) + +| Variable | Default | +| --- | --- | +| `--input-input-text-color` | `#555` | +| `--input-error-color` | `var(--color-danger)` | +| `--input-blur-color` | `#888` | +| `--input-focus-color` | `var(--color-primary)` | +| `--input-placeholder-size` | `16px` | +| `--input-textarea-height` | `auto` | +| `--input-textarea-padding-top` | `8px` | +| `--input-icon-padding` | `16px 0 0` | +| `--input-icon-size` | `20px` | +| `--input-input-text-color` | `#555` | +| `--input-line-size` | `1px` | +| `--input-line-spread-size` | `2px` | +| `--input-disabled-color` | `var(--color-text-disabled)` | \ No newline at end of file diff --git a/packages/varlet-vue2-ui/src/input/docs/zh-CN.md b/packages/varlet-vue2-ui/src/input/docs/zh-CN.md new file mode 100644 index 0000000..3bb2185 --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/docs/zh-CN.md @@ -0,0 +1,172 @@ +# 输入框 + +### 引入 + +```js +import Vue from 'vue' +import { Input } from '@varlet/ui' + +Vue.use(Input) +``` + +### 基本使用 + +输入框的行为和基本原生一致,用户输入时始终获得一个符合 `type` 规则的字符串 + +```js +export default { + data: () =>({ + value: '' + }), +} +``` + +```html + +``` + +### 朴素模式 + +如果只需要组件的基本功能,可以通过属性去除部分样式。 + +```html + +``` + +### 文本域 + +```html + +``` + +### 最大长度 + +```html + +``` + +### 禁用 + +```html + +``` + +### 只读 + +```html + +``` + +### 可清除 + +```html + +``` + +### 显示图标 +```js +import Vue from 'vue' +import { Icon } from '@varlet/ui' + +Vue.use(Icon) +``` + +```html + + + + +``` + +### 字段校验 + +通过传入一个校验器数组可以对值进行校验,校验器返回 `true` 则为校验通过。 +以外的值将转换为文本作为用户提示。 + +```html + +``` + +## API + +### 属性 + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| `v-model` | 绑定的值 | _string_ | `-` | +| `placeholder` | 占位符 | _string_ | `-` | +| `type` | 输入框类型, 可选值为 `text` `password` `number` | _string_ | `text` | +| `maxlength` | 最大长度 | _string \| number_ | `-` | +| `textarea` | 是否是文本域 | _boolean_ | `false` | +| `rows` | 文本域的显示行数 | _string \| number_ | `8` | +| `line` | 是否显示分割线 | _boolean_ | `true` | +| `hint` | 是否使用占位符作为提示 | _boolean_ | `true` | +| `text-color` | 文字颜色 | _string_ | `-` | +| `focus-color` | 聚焦时的主要颜色 | _string_ | `-` | +| `blur-color` | 失焦时的主要颜色 | _string_ | `-` | +| `readonly` | 是否只读 | _boolean_ | `false` | +| `disabled` | 是否禁用 | _boolean_ | `false` | +| `clearable` | 是否可清除 | _boolean_ | `false` | +| `resize` | 文本域是否可以拖动调整尺寸 | _boolean_ | `false` | +| `validate-trigger` | 触发验证的时机,可选值为 `onFocus` `onBlur` `onChange` `onClick` `onClear` `onInput` | _ValidateTriggers[]_ | `['onInput', 'onClear']` | +| `rules` | 验证规则,返回 `true` 表示验证通过,其余的值则转换为文本作为用户提示 | _Array<(v: string) => any>_ | `-` | + +### 方法 + +| 方法名 | 说明 | 参数 | 返回值 | +| --- | --- | --- | --- | +| `focus` | 聚焦 | `-` | `-` | +| `blur` | 失焦 | `-` | `-` | +| `validate` | 触发校验 | `-` | `valid: Promise` | +| `resetValidation` | 清空校验信息 | `-` | `-` | +| `reset` | 清空绑定的值和校验信息 | `-` | `-` | + +### 事件 + +| 事件名 | 说明 | 参数 | +| --- | --- | --- | +| `focus` | 聚焦时触发 | `event: Event` | +| `blur` | 失焦时触发 | `event: Event` | +| `click` | 点击时触发 | `event: Event` | +| `clear` | 清除时触发 | `value: string` | +| `input` | 输入时触发 | `value: string`, `event: Event` | +| `change` | 更新时触发 | `value: string`, `event: Event` | + +### 插槽 + +| 插槽名 | 说明 | 参数 | +| --- | --- | --- | +| `prepend-icon` | 前置图标 | `-` | +| `append-icon` | 后置图标 | `-` | + +### 样式变量 +以下为组件使用的 css 变量,可以使用 [StyleProvider 组件](#/zh-CN/style-provider)进行样式定制 + +| 变量名 | 默认值 | +| --- | --- | +| `--input-input-text-color` | `#555` | +| `--input-error-color` | `var(--color-danger)` | +| `--input-blur-color` | `#888` | +| `--input-focus-color` | `var(--color-primary)` | +| `--input-placeholder-size` | `16px` | +| `--input-textarea-height` | `auto` | +| `--input-textarea-padding-top` | `8px` | +| `--input-icon-padding` | `16px 0 0` | +| `--input-icon-size` | `20px` | +| `--input-input-text-color` | `#555` | +| `--input-line-size` | `1px` | +| `--input-line-spread-size` | `2px` | +| `--input-disabled-color` | `var(--color-text-disabled)` | diff --git a/packages/varlet-vue2-ui/src/input/example/index.vue b/packages/varlet-vue2-ui/src/input/example/index.vue new file mode 100644 index 0000000..fcd5978 --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/example/index.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/packages/varlet-vue2-ui/src/input/example/locale/en-US.ts b/packages/varlet-vue2-ui/src/input/example/locale/en-US.ts new file mode 100644 index 0000000..9a08b12 --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/example/locale/en-US.ts @@ -0,0 +1,14 @@ +export default { + basicUsage: 'Basic Usage', + plainMode: 'Plain Mode', + textarea: 'Textarea', + maxlength: 'Maxlength', + disabled: 'Disabled', + readonly: 'Readonly', + clearable: 'Clearable', + displayIcon: 'Display Icon', + validate: 'Validate', + placeholder: 'Please enter text', + maxMessage: 'Text length must be greater than 6', + clearableText: 'Clearable Text', +} diff --git a/packages/varlet-vue2-ui/src/input/example/locale/index.ts b/packages/varlet-vue2-ui/src/input/example/locale/index.ts new file mode 100644 index 0000000..d2e375e --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/example/locale/index.ts @@ -0,0 +1,23 @@ +// lib +import _zhCN from '../../../locale/zh-CN' +import _enCN from '../../../locale/en-US' +// mobile example doc +import zhCN from './zh-CN' +import enUS from './en-US' +import { useLocale, add as _add, use as _use } from '../../../locale' + +const { add, use: exampleUse, pack, packs, merge } = useLocale() + +const use = (lang: string) => { + _use(lang) + exampleUse(lang) +} + +export { add, pack, packs, merge, use } + +// lib +_add('zh-CN', _zhCN) +_add('en-US', _enCN) +// mobile example doc +add('zh-CN', zhCN as any) +add('en-US', enUS as any) diff --git a/packages/varlet-vue2-ui/src/input/example/locale/zh-CN.ts b/packages/varlet-vue2-ui/src/input/example/locale/zh-CN.ts new file mode 100644 index 0000000..4975e83 --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/example/locale/zh-CN.ts @@ -0,0 +1,14 @@ +export default { + basicUsage: '基本使用', + plainMode: '朴素模式', + textarea: '文本域', + maxlength: '最大长度', + disabled: '禁用', + readonly: '只读', + clearable: '可清除', + displayIcon: '显示图标', + validate: '字段校验', + placeholder: '请输入文本', + maxMessage: '文本长度必须大于6', + clearableText: '可清除文本', +} diff --git a/packages/varlet-vue2-ui/src/input/index.ts b/packages/varlet-vue2-ui/src/input/index.ts new file mode 100644 index 0000000..e815931 --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/index.ts @@ -0,0 +1,10 @@ +import type { VueConstructor } from 'vue' +import Input from './Input.vue' + +Input.install = function (app: VueConstructor) { + app.component(Input.name, Input) +} + +export const _InputComponent = Input + +export default Input diff --git a/packages/varlet-vue2-ui/src/input/input.less b/packages/varlet-vue2-ui/src/input/input.less new file mode 100644 index 0000000..15d1408 --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/input.less @@ -0,0 +1,187 @@ +@input-input-text-color: #555; +@input-error-color: var(--color-danger); +@input-blur-color: #888; +@input-focus-color: var(--color-primary); +@input-placeholder-size: 16px; +@input-textarea-height: auto; +@input-textarea-padding-top: 8px; +@input-icon-padding: 16px 0 0; +@input-icon-size: 20px; +@input-line-size: 1px; +@input-line-spread-size: 2px; +@input-disabled-color: var(--color-text-disabled); + +:root { + --input-input-text-color: @input-input-text-color; + --input-error-color: @input-error-color; + --input-blur-color: @input-blur-color; + --input-focus-color: @input-focus-color; + --input-placeholder-size: @input-placeholder-size; + --input-textarea-height: @input-textarea-height; + --input-textarea-padding-top: @input-textarea-padding-top; + --input-icon-padding: @input-icon-padding; + --input-icon-size: @input-icon-size; + --input-line-size: @input-line-size; + --input-line-spread-size: @input-line-spread-size; + --input-disabled-color: @input-disabled-color; +} + +.var { + &-input-footer-margin-enter-from, + &-input-footer-margin-leave-to { + opacity: 0; + margin-top: 2px !important; + } + + &-input-footer-margin-enter-active, + &-input-footer-margin-leave-active { + transition: 0.2s all var(--cubic-bezier); + } +} + +.var-input { + width: 100%; + color: var(--input-input-text-color); + + &__controller { + width: 100%; + display: flex; + position: relative; + } + + &__wrap { + position: relative; + flex-grow: 1; + display: flex; + flex-direction: column; + padding-top: var(--input-placeholder-size); + } + + &__icon { + display: flex; + align-items: center; + padding: var(--input-icon-padding); + font-size: 20px; + } + + &__placeholder { + position: absolute; + top: 50%; + left: 0; + width: 100%; + transform-origin: left; + transition-property: top, transform, width; + transition-duration: 0.3s; + transform: translate(0, calc(-50% + var(--input-placeholder-size) / 2)) scale(1); + font-size: var(--input-placeholder-size); + color: var(--input-blur-color); + } + + &__textarea-placeholder { + position: absolute; + top: 0; + left: 0; + width: 100%; + transform-origin: left; + transition-property: transform, width; + transition-duration: 0.3s; + transform: translate(0, calc(var(--input-textarea-padding-top) + var(--input-placeholder-size))) scale(1); + font-size: var(--input-placeholder-size); + color: var(--input-blur-color); + } + + &__autocomplete { + width: 0; + height: 0; + padding: 0; + border: none; + outline: none; + font-size: 0; + } + + &__input { + width: 100%; + height: 32px; + padding: 0; + outline: none; + border: none; + background: transparent; + color: var(--input-input-text-color); + caret-color: var(--input-focus-color); + font: inherit; + } + + &__line { + width: 100%; + height: var(--input-line-size); + background: var(--input-blur-color); + } + + &__dot { + width: 100%; + height: var(--input-line-spread-size); + background: var(--input-focus-color); + transform: scaleX(0); + transform-origin: center; + transition: transform 0.3s var(--cubic-bezier); + } + + &__clear-icon[var-input-cover] { + display: flex; + margin-left: 4px; + } + + &--textarea { + padding-top: var(--input-textarea-padding-top); + height: var(--input-textarea-height); + } + + &--placeholder-hint { + top: 0; + width: 133.33%; + transform: translate(0, 0) scale(0.75); + } + + &--placeholder-non-hint { + top: 50%; + transform: translate(0, -50%) scale(1); + } + + &--non-hint { + padding-top: 0; + } + + &--placeholder-hidden { + visibility: hidden; + } + + &--focus { + color: var(--input-focus-color); + } + + &--spread { + transform: scaleX(1); + } + + &--disabled { + -webkit-text-fill-color: var(--input-disabled-color); + opacity: 1; + color: var(--input-disabled-color); + } + + &--error { + color: var(--input-error-color); + } + + &--line-disabled { + background: var(--input-disabled-color); + } + + &--line-error { + background: var(--input-error-color); + } + + &--caret-error { + caret-color: var(--input-error-color); + } +} diff --git a/packages/varlet-vue2-ui/src/input/props.ts b/packages/varlet-vue2-ui/src/input/props.ts new file mode 100644 index 0000000..9c78645 --- /dev/null +++ b/packages/varlet-vue2-ui/src/input/props.ts @@ -0,0 +1,72 @@ +import type { PropType } from 'vue' + +export function typeValidator(type: string) { + return ['text', 'password', 'number'].includes(type) +} + +export type ValidateTriggers = 'onFocus' | 'onBlur' | 'onChange' | 'onClick' | 'onClear' | 'onInput' + +export const props = { + value: { + type: String, + }, + type: { + type: String as PropType<'text' | 'password' | 'number'>, + default: 'text', + validator: typeValidator, + }, + textarea: { + type: Boolean, + default: false, + }, + rows: { + type: [String, Number], + default: 8, + }, + placeholder: { + type: String, + }, + line: { + type: Boolean, + default: true, + }, + hint: { + type: Boolean, + default: true, + }, + textColor: { + type: String, + }, + focusColor: { + type: String, + }, + blurColor: { + type: String, + }, + maxlength: { + type: [String, Number], + }, + disabled: { + type: Boolean, + default: false, + }, + readonly: { + type: Boolean, + default: false, + }, + clearable: { + type: Boolean, + default: false, + }, + resize: { + type: Boolean, + default: false, + }, + validateTrigger: { + type: Array as PropType, + default: () => ['onInput', 'onClear'], + }, + rules: { + type: Array as PropType any>>, + }, +} diff --git a/packages/varlet-vue2-ui/types/index.d.ts b/packages/varlet-vue2-ui/types/index.d.ts index ad4daf7..2d7fcc7 100644 --- a/packages/varlet-vue2-ui/types/index.d.ts +++ b/packages/varlet-vue2-ui/types/index.d.ts @@ -24,5 +24,6 @@ export * from './snackbar' export * from './sticky' export * from './loading' export * from './menu' +export * from './input' export * from './varComponent' export * from './varDirective' diff --git a/packages/varlet-vue2-ui/types/input.d.ts b/packages/varlet-vue2-ui/types/input.d.ts new file mode 100644 index 0000000..d164be4 --- /dev/null +++ b/packages/varlet-vue2-ui/types/input.d.ts @@ -0,0 +1,44 @@ +import { VarComponent } from './varComponent' + +export type InputValidateTriggers = 'onFocus' | 'onBlur' | 'onChange' | 'onClick' | 'onClear' | 'onInput' + +export interface InputProps { + value?: string + type?: 'text' | 'password' | 'number' + textarea?: boolean + rows?: string | number + placeholder?: string + hint?: boolean + textColor?: string + focusColor?: string + blurColor?: string + maxlength?: string | number + disabled?: boolean + readonly?: boolean + clearable?: boolean + resize?: boolean + validateTrigger?: InputValidateTriggers[] + rules?: Array<(v: string) => any> + onFocus?: (e: Event) => void + onBlur?: (e: Event) => void + onClick?: (e: Event) => void + onClear?: (value: string) => void + onInput?: (value: string, e: Event) => void + onChange?: (value: string, e: Event) => void +} + +export class Input extends VarComponent { + $props: InputProps + + focus(): void + + blur(): void + + validate(): Promise + + resetValidation(): void + + reset(): void +} + +export class _InputComponent extends Input {}