浏览代码

Merge branch 'next' of https://github.com/jdf2e/nutui into next

richard1015 4 年之前
父节点
当前提交
8cd739ec84

+ 10 - 0
src/config.json

@@ -487,6 +487,16 @@
           "sort": 6,
           "sort": 6,
           "show": true,
           "show": true,
           "author": "Drjingfubo"
           "author": "Drjingfubo"
+        },
+        {
+          "version": "3.0.0",
+          "name": "TextArea",
+          "type": "component",
+          "cName": "文本域",
+          "desc": "文本输入",
+          "sort": 7,
+          "show": true,
+          "author": "gx"
         }
         }
       ]
       ]
     },
     },

+ 2 - 2
src/packages/infiniteloading/doc.md

@@ -178,9 +178,9 @@ setup() {
 | container-id          | 在 useWindow 属性为 false 的时候,自定义设置节点ID                        | String | `''`            |
 | container-id          | 在 useWindow 属性为 false 的时候,自定义设置节点ID                        | String | `''`            |
 | load-more-txt          | “没有更多数”据展示文案                        | String | `'哎呀,这里是底部了啦'`            |
 | load-more-txt          | “没有更多数”据展示文案                        | String | `'哎呀,这里是底部了啦'`            |
 | is-open-refresh        | 是否开启下拉刷新                         | Boolean | `false`                |
 | is-open-refresh        | 是否开启下拉刷新                         | Boolean | `false`                |
-| pull-icon        | 下拉刷新[图标名称](#/icon)                        | String | `https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png`                |
+| pull-icon        | 下拉刷新[图标名称](#/icon)                        | String | <img src="https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png" width=40/>                |
 | pull-txt        | 下拉刷新提示文案                         | String | `松手刷新`                |
 | pull-txt        | 下拉刷新提示文案                         | String | `松手刷新`                |
-| load-icon        | 上拉加载[图标名称](#/icon)                       | Boolean | `https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png`                |
+| load-icon        | 上拉加载[图标名称](#/icon)                       | Boolean | <img src="https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png" width=40 />                |
 | load-txt        | 上拉加载提示文案                         | String | `加载中...`                |
 | load-txt        | 上拉加载提示文案                         | String | `加载中...`                |
 
 
 ### Events
 ### Events

+ 1 - 28
src/packages/input/demo.vue

@@ -57,34 +57,6 @@
       placeholder="支持小数点的输入"
       placeholder="支持小数点的输入"
       label="数字:"
       label="数字:"
     />
     />
-    <h2>文本域</h2>
-    <nut-input
-      v-model:value="state.val7"
-      @change="change"
-      :autosize="true"
-      type="textarea"
-      placeholder="文本域"
-      label="留言:"
-    />
-    <nut-input
-      v-model:value="state.val7"
-      @change="change"
-      rows="5"
-      type="textarea"
-      placeholder="设置输入五行"
-      label="留言:"
-    />
-    <h2>显示字数统计</h2>
-    <nut-input
-      v-model:value="state.val8"
-      @change="change"
-      rows="5"
-      :limitShow="true"
-      maxLength="20"
-      type="textarea"
-      placeholder="设置输入五行"
-      label="留言:"
-    />
   </div>
   </div>
 </template>
 </template>
 
 
@@ -120,6 +92,7 @@ export default createDemo({
     const clear = (num: string | number) => {
     const clear = (num: string | number) => {
       console.log('clear:', num);
       console.log('clear:', num);
     };
     };
+
     return {
     return {
       state,
       state,
       change,
       change,

+ 17 - 19
src/packages/input/doc.md

@@ -20,8 +20,20 @@ app.use(input);
 双向绑定
 双向绑定
 
 
 ```html
 ```html
-<nut-input v-model:value="state.val1" @change="change" label="标题:" />
-
+<nut-input
+      v-model:value="state.val1"
+      @change="change"
+      @focus="focus"
+      @blur="blur"
+      label="文本"
+    />
+<nut-input placeholder="请输入文本"
+      @change="change"
+      v-model:value="state.val0"
+      :requireShow="true"
+      label="文本"
+      @clear="clear"
+    />
 ```
 ```
 
 
 ### 禁用和只读
 ### 禁用和只读
@@ -44,19 +56,7 @@ app.use(input);
 <nut-input v-model:value="state.val5" @change="change" type="digit" label="整数:" />
 <nut-input v-model:value="state.val5" @change="change" type="digit" label="整数:" />
 <nut-input v-model:value="state.val6" @change="change" type="digit" placeholder="支持小数点的输入" label="数字:"/>
 <nut-input v-model:value="state.val6" @change="change" type="digit" placeholder="支持小数点的输入" label="数字:"/>
 ```
 ```
-### 文本域
-
-```html
- <nut-input v-model:value="state.val7" @change="change" autosize="true" type="textarea" placeholder="文本域" label="留言:"/>
-<nut-input v-model:value="state.val7" @change="change" rows="5" type="textarea" placeholder="设置输入五行"  label="留言:"/>
-
-```
-### 文本域字数统计
-
-```html
- <nut-input v-model:value="state.val8" @change="change" rows="5" limitShow="true" maxLength="20" type="textarea" placeholder="设置输入五行" label="留言:"/>
 
 
-```
 
 
 
 
 | 参数         | 说明                             | 类型   | 默认值           |
 | 参数         | 说明                             | 类型   | 默认值           |
@@ -64,15 +64,13 @@ app.use(input);
 | type         | 类型,可选值为 `text` `textarea` `number`  等 | String |`text`         |
 | type         | 类型,可选值为 `text` `textarea` `number`  等 | String |`text`         |
 | value      | 输入值,双向绑定 | String |  -     |
 | value      | 输入值,双向绑定 | String |  -     |
 | placeholder         | 为空时占位符 | String |       -       |
 | placeholder         | 为空时占位符 | String |       -       |
-| placeholder-style | placeholder 样式     | String | - |
 | label          | 	左侧文案                       | string | -             |
 | label          | 	左侧文案                       | string | -             |
+| requireShow          |左侧*号是否展示                       | boolean | `false`           |
 | disabled          | 	是否禁用                       | boolean | `false`              |
 | disabled          | 	是否禁用                       | boolean | `false`              |
 | readonly          | 是否只读                        | boolean | `false`               |
 | readonly          | 是否只读                        | boolean | `false`               |
-| clear-btn       | 是否带清除按钮(icon)                        | boolean | `true`               |
-| required          | 是否带必填的*号,且blur事件做非空校验                       | boolean | `false`               |
 | maxlength          | 限制最长输入字符                   | string/number | -               |
 | maxlength          | 限制最长输入字符                   | string/number | -               |
-| rows          | textarea时高度                   | string/number | 2             |
-| limit-show          | textarea时是否展示输入字符。须设置maxlength                        | boolean | `false`               |
+| disableClear          | 禁止展示清除icon                   | boolean | false             |
+| textAlign          | 文本位置                   | string | `left`             |
 | change          | 输入内容时触发                        | function | -               |
 | change          | 输入内容时触发                        | function | -               |
 | focus          | 聚焦时触发                        | function | -               |
 | focus          | 聚焦时触发                        | function | -               |
 | blur          | 失焦时触发                        | function | -               |
 | blur          | 失焦时触发                        | function | -               |

+ 0 - 23
src/packages/input/index.scss

@@ -28,29 +28,6 @@
     position: absolute;
     position: absolute;
     right: 15px;
     right: 15px;
   }
   }
-  .nut-text {
-    flex: 1;
-    padding: 0 10px;
-    .nut-text-limit {
-      float: right;
-      color: rgba(153, 153, 153, 1);
-    }
-    .nut-text-core {
-      outline: none;
-      display: block;
-      box-sizing: border-box;
-      width: 100%;
-      min-width: 0;
-      margin: 0;
-      padding: 0;
-      color: #323233;
-      line-height: inherit;
-      text-align: left;
-      background-color: transparent;
-      border: 0;
-      resize: none;
-    }
-  }
 }
 }
 .nut-input-disabled {
 .nut-input-disabled {
   color: #c8c9cc !important;
   color: #c8c9cc !important;

+ 28 - 78
src/packages/input/index.vue

@@ -1,32 +1,10 @@
 <template>
 <template>
-  <view :class="['nut-input', { 'nut-input-disabled': disabled }]">
+  <view :class="classes">
     <view class="nut-input-label">
     <view class="nut-input-label">
       <view class="nut-input-require" v-if="requireShow">*</view>
       <view class="nut-input-require" v-if="requireShow">*</view>
       <view v-if="label" class="label-string">{{ label }}</view>
       <view v-if="label" class="label-string">{{ label }}</view>
     </view>
     </view>
-
-    <view v-if="type === 'textarea'" class="nut-text">
-      <textarea
-        :style="styles"
-        :rows="rows"
-        @input="valueChange"
-        v-model="state.curretvalue"
-        class="nut-text-core"
-        :maxlength="maxLength"
-        :placeholder="placeholder"
-        :disabled="disabled"
-        :readonly="readonly"
-      >
-      </textarea>
-      <span class="nut-text-limit" v-if="limitShow">
-        <span :class="[{ 'nut-field-over': state.textNum > maxLength }]">{{
-          state.textNum
-        }}</span>
-        <span>/{{ maxLength }}</span>
-      </span>
-    </view>
     <input
     <input
-      v-else
       class="input-text"
       class="input-text"
       :style="styles"
       :style="styles"
       :type="type"
       :type="type"
@@ -43,7 +21,7 @@
       @click="handleClear"
       @click="handleClear"
       class="nut-textinput-clear"
       class="nut-textinput-clear"
       v-if="!disableClear && !readonly"
       v-if="!disableClear && !readonly"
-      v-show="type !== 'textarea' && active"
+      v-show="active && state.curretvalue.length > 0"
     >
     >
       <nut-icon name="close-little" size="12px"></nut-icon>
       <nut-icon name="close-little" size="12px"></nut-icon>
     </view>
     </view>
@@ -54,43 +32,30 @@ import { ref, toRefs, reactive, computed } from 'vue';
 import { createComponent } from '@/utils/create';
 import { createComponent } from '@/utils/create';
 import { formatNumber } from './util';
 import { formatNumber } from './util';
 
 
-const { create } = createComponent('input');
-
+const { componentName, create } = createComponent('input');
+interface Events {
+  eventName: 'change' | 'focus' | 'blur' | 'clear' | 'update:value';
+  params: (string | number | Event)[];
+}
 export default create({
 export default create({
   props: {
   props: {
     type: {
     type: {
       type: String,
       type: String,
       default: 'text'
       default: 'text'
     },
     },
-    textAlign: {
-      type: String,
-      default: 'left'
-    },
-    limitShow: {
-      type: Boolean,
-      default: false
-    },
-    maxLength: {
-      type: String,
+    value: {
+      type: [String, Number],
       default: ''
       default: ''
     },
     },
-    requireShow: {
-      type: Boolean,
-      default: false
-    },
-    rows: {
+    placeholder: {
       type: String,
       type: String,
-      default: ''
+      default: '请输入信息'
     },
     },
     label: {
     label: {
       type: String,
       type: String,
       default: ''
       default: ''
     },
     },
-    placeholder: {
-      type: String,
-      default: '请输入信息'
-    },
-    readonly: {
+    requireShow: {
       type: Boolean,
       type: Boolean,
       default: false
       default: false
     },
     },
@@ -98,12 +63,16 @@ export default create({
       type: Boolean,
       type: Boolean,
       default: false
       default: false
     },
     },
-    autosize: {
+    readonly: {
       type: Boolean,
       type: Boolean,
       default: false
       default: false
     },
     },
-    value: {
-      type: [String, Number],
+    textAlign: {
+      type: String,
+      default: 'left'
+    },
+    maxLength: {
+      type: String,
       default: ''
       default: ''
     },
     },
     disableClear: {
     disableClear: {
@@ -115,25 +84,13 @@ export default create({
   emits: ['change', 'update:value', 'blur', 'focus', 'clear', 'error'],
   emits: ['change', 'update:value', 'blur', 'focus', 'clear', 'error'],
 
 
   setup(props, { emit }) {
   setup(props, { emit }) {
-    interface Events {
-      eventName:
-        | 'change'
-        | 'focus'
-        | 'blur'
-        | 'clear'
-        | 'update:value'
-        | 'error';
-      params: (string | number | Event)[];
-    }
-
     const {
     const {
       label,
       label,
       placeholder,
       placeholder,
       disabled,
       disabled,
       readonly,
       readonly,
       requireShow,
       requireShow,
-      maxLength,
-      rows
+      maxLength
     } = props;
     } = props;
     const { value } = toRefs(props);
     const { value } = toRefs(props);
     const active = ref(false);
     const active = ref(false);
@@ -141,14 +98,15 @@ export default create({
       curretvalue: value,
       curretvalue: value,
       textNum: String(value.value).length
       textNum: String(value.value).length
     });
     });
+    const classes = computed(() => {
+      return {
+        [componentName]: true,
+        'nut-input-disabled': disabled
+      };
+    });
     const styles = computed(() => {
     const styles = computed(() => {
-      const rize =
-        props.type == 'textarea'
-          ? `'resize':${props.autosize ? 'none' : 'horizontal'}`
-          : '';
       return {
       return {
-        'text-align': props.textAlign,
-        rize
+        'text-align': props.textAlign
       };
       };
     });
     });
     const emitChange = (envs: Array<Events>) => {
     const emitChange = (envs: Array<Events>) => {
@@ -162,12 +120,6 @@ export default create({
 
 
       if (maxLength && val.length > Number(maxLength)) {
       if (maxLength && val.length > Number(maxLength)) {
         val = val.slice(0, Number(maxLength));
         val = val.slice(0, Number(maxLength));
-        emitChange([
-          {
-            eventName: 'error',
-            params: [val]
-          }
-        ]);
       }
       }
       if (props.type == 'digit') {
       if (props.type == 'digit') {
         val = formatNumber(val, true);
         val = formatNumber(val, true);
@@ -176,8 +128,6 @@ export default create({
         val = formatNumber(val, false);
         val = formatNumber(val, false);
       }
       }
       state.textNum = val.length;
       state.textNum = val.length;
-      // input.value = val;
-      //state.curretvalue = val;
       emitChange([
       emitChange([
         {
         {
           eventName: 'update:value',
           eventName: 'update:value',
@@ -243,7 +193,6 @@ export default create({
       placeholder,
       placeholder,
       label,
       label,
       disabled,
       disabled,
-      rows,
       state,
       state,
       styles,
       styles,
       active,
       active,
@@ -252,6 +201,7 @@ export default create({
       valueFocus,
       valueFocus,
       valueBlur,
       valueBlur,
       handleClear,
       handleClear,
+      classes,
       emitChange
       emitChange
     };
     };
   }
   }

+ 93 - 70
src/packages/picker/Column.vue

@@ -12,17 +12,23 @@
       <view-block
       <view-block
         class="nut-picker__item"
         class="nut-picker__item"
         :key="index"
         :key="index"
-        v-for="(item, index) in state.options"
+        v-for="(item, index) in options"
         >{{ dataType === 'cascade' ? item.text : item }}</view-block
         >{{ dataType === 'cascade' ? item.text : item }}</view-block
       >
       >
     </view-block>
     </view-block>
   </view-block>
   </view-block>
 </template>
 </template>
 <script lang="ts">
 <script lang="ts">
-import { reactive, ref, watch, computed } from 'vue';
+import { reactive, ref, watch, computed, toRefs, onMounted } from 'vue';
 import { createComponent } from '@/utils/create';
 import { createComponent } from '@/utils/create';
 import { useTouch } from '@/utils/useTouch';
 import { useTouch } from '@/utils/useTouch';
 import { commonProps } from './commonProps';
 import { commonProps } from './commonProps';
+import {
+  PickerObjOpt,
+  PickerOption,
+  PickerObjectColumn,
+  PickerObjectColumns
+} from './types';
 const MOMENTUM_LIMIT_DISTANCE = 15;
 const MOMENTUM_LIMIT_DISTANCE = 15;
 const MOMENTUM_LIMIT_TIME = 300;
 const MOMENTUM_LIMIT_TIME = 300;
 const DEFAULT_DURATION = 200;
 const DEFAULT_DURATION = 200;
@@ -34,7 +40,6 @@ function stopPropagation(event: Event) {
   event.stopPropagation();
   event.stopPropagation();
 }
 }
 function preventDefault(event: Event, isStopPropagation?: boolean) {
 function preventDefault(event: Event, isStopPropagation?: boolean) {
-  /* istanbul ignore else */
   if (typeof event.cancelable !== 'boolean' || event.cancelable) {
   if (typeof event.cancelable !== 'boolean' || event.cancelable) {
     event.preventDefault();
     event.preventDefault();
   }
   }
@@ -44,7 +49,7 @@ function preventDefault(event: Event, isStopPropagation?: boolean) {
   }
   }
 }
 }
 
 
-function getElementTranslateY(element) {
+function getElementTranslateY(element: Element) {
   const style = window.getComputedStyle(element);
   const style = window.getComputedStyle(element);
   const transform = style.transform || style.webkitTransform;
   const transform = style.transform || style.webkitTransform;
   const translateY = transform.slice(7, transform.length - 1).split(', ')[5];
   const translateY = transform.slice(7, transform.length - 1).split(', ')[5];
@@ -54,7 +59,7 @@ export function isObject(val: unknown): val is Record<any, any> {
   return val !== null && typeof val === 'object';
   return val !== null && typeof val === 'object';
 }
 }
 
 
-function isOptionDisabled(option) {
+function isOptionDisabled(option: PickerObjectColumn) {
   return isObject(option) && option.disabled;
   return isObject(option) && option.disabled;
 }
 }
 
 
@@ -66,46 +71,57 @@ export default create({
 
 
   emits: ['click', 'change'],
   emits: ['click', 'change'],
   setup(props, { emit }) {
   setup(props, { emit }) {
-    let moving;
-    let startOffset, touchStartTime, momentumOffset, transitionEndTrigger;
+    const wrapper = ref();
+
     const state = reactive({
     const state = reactive({
       index: props.defaultIndex,
       index: props.defaultIndex,
       offset: 0,
       offset: 0,
       duration: 0,
       duration: 0,
-      options: props.listData
+      options: props.listData as PickerObjectColumn[],
+      moving: false,
+      startOffset: 0,
+      touchStartTime: 0,
+      momentumOffset: 0,
+      transitionEndTrigger: null as null | Function
     });
     });
-    watch(
-      () => props.listData,
-      val => {
-        if (val) {
-          state.options = val;
-        }
-      }
-    );
 
 
-    const wrapper = ref();
     const touch = useTouch();
     const touch = useTouch();
-    const count = () => state.options.length;
-    const _show = ref(false);
-    const getIndexByOffset = offset =>
-      range(Math.round(-offset / props.itemHeight), 0, count() - 1);
 
 
-    const baseOffset = () =>
-      (props.itemHeight * (props.visibleItemCount - 1)) / 2;
+    const wrapperStyle = computed(() => ({
+      transform: `translate3d(0, ${state.offset + baseOffset()}px, 0)`,
+      transitionDuration: `${state.duration}ms`,
+      transitionProperty: state.duration ? 'all' : 'none'
+    }));
+
+    const handleClick = (event: Event) => {
+      emit('click', event);
+    };
+
+    const getIndexByOffset = (offset: number) => {
+      return range(
+        Math.round(-offset / +props.itemHeight),
+        0,
+        state.options.length - 1
+      );
+    };
+
+    const baseOffset = () => {
+      return (+props.itemHeight * (+props.visibleItemCount - 1)) / 2;
+    };
 
 
     const stopMomentum = () => {
     const stopMomentum = () => {
-      moving = false;
+      state.moving = false;
       state.duration = 0;
       state.duration = 0;
-      if (transitionEndTrigger) {
-        transitionEndTrigger();
-        transitionEndTrigger = null;
+      if (state.transitionEndTrigger) {
+        state.transitionEndTrigger();
+        state.transitionEndTrigger = null;
       }
       }
     };
     };
 
 
-    const adjustIndex = index => {
-      index = range(index, 0, count());
+    const adjustIndex = (index: number) => {
+      index = range(index, 0, state.options.length);
 
 
-      for (let i = index; i < count(); i++) {
+      for (let i = index; i < state.options.length; i++) {
         if (!isOptionDisabled(state.options[i])) return i;
         if (!isOptionDisabled(state.options[i])) return i;
       }
       }
       for (let i = index - 1; i >= 0; i--) {
       for (let i = index - 1; i >= 0; i--) {
@@ -113,10 +129,10 @@ export default create({
       }
       }
     };
     };
 
 
-    const setIndex = (index, emitChange = false) => {
+    const setIndex = (index: number, emitChange = false) => {
       index = adjustIndex(index) || 0;
       index = adjustIndex(index) || 0;
 
 
-      const offset = -index * props.itemHeight;
+      const offset = -index * +props.itemHeight;
       const trigger = () => {
       const trigger = () => {
         if (index !== state.index) {
         if (index !== state.index) {
           state.index = index;
           state.index = index;
@@ -127,22 +143,16 @@ export default create({
         }
         }
       };
       };
 
 
-      if (moving && offset !== state.offset) {
-        transitionEndTrigger = trigger;
+      if (state.moving && offset !== state.offset) {
+        state.transitionEndTrigger = trigger;
       } else {
       } else {
         trigger();
         trigger();
       }
       }
 
 
       state.offset = offset;
       state.offset = offset;
     };
     };
-    watch(
-      () => props.defaultIndex,
-      val => {
-        setIndex(val);
-      }
-    );
-    setIndex(props.defaultIndex);
-    const momentum = (distance, duration) => {
+
+    const momentum = (distance: number, duration: number) => {
       const speed = Math.abs(distance / duration);
       const speed = Math.abs(distance / duration);
 
 
       distance = state.offset + (speed / 0.003) * (distance < 0 ? -1 : 1);
       distance = state.offset + (speed / 0.003) * (distance < 0 ? -1 : 1);
@@ -151,57 +161,58 @@ export default create({
 
 
       setIndex(index, true);
       setIndex(index, true);
     };
     };
-    const onTouchStart = event => {
+
+    const onTouchStart = (event: Event) => {
       if (props.readonly) {
       if (props.readonly) {
         return;
         return;
       }
       }
       touch.start(event);
       touch.start(event);
 
 
-      if (moving) {
+      if (state.moving) {
         const translateY = getElementTranslateY(wrapper.value);
         const translateY = getElementTranslateY(wrapper.value);
         state.offset = Math.min(0, translateY - baseOffset());
         state.offset = Math.min(0, translateY - baseOffset());
-        startOffset = state.offset;
+        state.startOffset = state.offset;
       } else {
       } else {
-        startOffset = state.offset;
+        state.startOffset = state.offset;
       }
       }
 
 
       state.duration = 0;
       state.duration = 0;
-      touchStartTime = Date.now();
-      momentumOffset = startOffset;
-      transitionEndTrigger = null;
+      state.touchStartTime = Date.now();
+      state.momentumOffset = state.startOffset;
+      state.transitionEndTrigger = null;
     };
     };
-    const onTouchMove = event => {
+    const onTouchMove = (event: Event) => {
       if (props.readonly) {
       if (props.readonly) {
         return;
         return;
       }
       }
-      moving = true;
+      state.moving = true;
       touch.move(event);
       touch.move(event);
 
 
       if (touch.isVertical()) {
       if (touch.isVertical()) {
-        moving = true;
+        state.moving = true;
         preventDefault(event, true);
         preventDefault(event, true);
       }
       }
 
 
-      const moveOffset = startOffset + touch.deltaY.value;
+      const moveOffset = state.startOffset + touch.deltaY.value;
       if (moveOffset > props.itemHeight) {
       if (moveOffset > props.itemHeight) {
-        state.offset = props.itemHeight;
+        state.offset = props.itemHeight as number;
       } else {
       } else {
-        state.offset = startOffset + touch.deltaY.value;
+        state.offset = state.startOffset + touch.deltaY.value;
       }
       }
 
 
       const now = Date.now();
       const now = Date.now();
 
 
-      if (now - touchStartTime > MOMENTUM_LIMIT_TIME) {
-        touchStartTime = now;
-        momentumOffset = state.offset;
+      if (now - state.touchStartTime > MOMENTUM_LIMIT_TIME) {
+        state.touchStartTime = now;
+        state.momentumOffset = state.offset;
       }
       }
     };
     };
     const onTouchEnd = () => {
     const onTouchEnd = () => {
       const index = getIndexByOffset(state.offset);
       const index = getIndexByOffset(state.offset);
       state.duration = DEFAULT_DURATION;
       state.duration = DEFAULT_DURATION;
       setIndex(index, true);
       setIndex(index, true);
-      const distance = state.offset - momentumOffset;
-      const duration = Date.now() - touchStartTime;
+      const distance = state.offset - state.momentumOffset;
+      const duration = Date.now() - state.touchStartTime;
 
 
       const allowMomentum =
       const allowMomentum =
         duration < MOMENTUM_LIMIT_TIME &&
         duration < MOMENTUM_LIMIT_TIME &&
@@ -212,25 +223,37 @@ export default create({
         return;
         return;
       }
       }
     };
     };
-    const handleClick = (event: Event) => {
-      emit('click', event);
-    };
-    const wrapperStyle = computed(() => ({
-      transform: `translate3d(0, ${state.offset + baseOffset()}px, 0)`,
-      transitionDuration: `${state.duration}ms`,
-      transitionProperty: state.duration ? 'all' : 'none'
-    }));
+
+    onMounted(() => {
+      setIndex(+props.defaultIndex);
+    });
+
+    watch(
+      () => props.listData,
+      val => {
+        if (val) {
+          state.options = val as PickerObjectColumn[];
+        }
+      }
+    );
+
+    watch(
+      () => props.defaultIndex,
+      val => {
+        setIndex(+val);
+      }
+    );
 
 
     return {
     return {
+      ...toRefs(state),
       wrapper,
       wrapper,
       onTouchStart,
       onTouchStart,
       onTouchMove,
       onTouchMove,
       onTouchEnd,
       onTouchEnd,
       wrapperStyle,
       wrapperStyle,
-      state,
       stopMomentum,
       stopMomentum,
       columns: state.options,
       columns: state.options,
-      height: Number(props.visibleItemCount) * props.itemHeight
+      height: Number(props.visibleItemCount) * +props.itemHeight
     };
     };
   }
   }
 });
 });

+ 5 - 3
src/packages/picker/commonProps.ts

@@ -1,14 +1,16 @@
 export const commonProps = {
 export const commonProps = {
   listData: {
   listData: {
     type: Array,
     type: Array,
-    default: []
+    default: () => {
+      return [];
+    }
   },
   },
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
     default: false
     default: false
   },
   },
   visibleItemCount: {
   visibleItemCount: {
-    type: [Number],
+    type: [Number, String],
     default: 7
     default: 7
   },
   },
   defaultIndex: {
   defaultIndex: {
@@ -16,7 +18,7 @@ export const commonProps = {
     default: 0
     default: 0
   },
   },
   itemHeight: {
   itemHeight: {
-    type: [Number],
+    type: [Number, String],
     default: 35
     default: 35
   }
   }
 };
 };

+ 6 - 6
src/packages/picker/demo.vue

@@ -95,8 +95,8 @@ export default createDemo({
       }`
       }`
     );
     );
     const desc3 = ref(
     const desc3 = ref(
-      `${listData3[0].text} 
-      ${listData3[0].children[0].text} 
+      `${listData3[0].text}
+      ${listData3[0].children[0].text}
       ${listData3[0].children[0].children[0].text}`
       ${listData3[0].children[0].children[0].text}`
     );
     );
     const descList = [desc, desc2, desc3];
     const descList = [desc, desc2, desc3];
@@ -110,16 +110,16 @@ export default createDemo({
       desc,
       desc,
       desc2,
       desc2,
       desc3,
       desc3,
-      open: index => {
+      open: (index: number) => {
         showList[index - 1].value = true;
         showList[index - 1].value = true;
       },
       },
-      confirm: res => {
+      confirm: (res: any) => {
         desc.value = res;
         desc.value = res;
       },
       },
-      confirm2: res => {
+      confirm2: (res: any) => {
         desc2.value = res.join(' ');
         desc2.value = res.join(' ');
       },
       },
-      confirm3: res => {
+      confirm3: (res: any) => {
         desc3.value = res.join(' ');
         desc3.value = res.join(' ');
       }
       }
     };
     };

+ 1 - 1
src/packages/picker/doc.md

@@ -1,4 +1,4 @@
-#  picker组件
+#  Picker组件
 
 
 ### 介绍
 ### 介绍
     
     

+ 148 - 111
src/packages/picker/index.vue

@@ -1,5 +1,5 @@
 <template>
 <template>
-  <view-block class="nut-picker">
+  <view-block :class="classes">
     <nut-popup
     <nut-popup
       position="bottom"
       position="bottom"
       :style="{ height: height + 56 + 'px' }"
       :style="{ height: height + 56 + 'px' }"
@@ -7,9 +7,9 @@
       @close="close"
       @close="close"
     >
     >
       <view-block class="nut-picker__bar">
       <view-block class="nut-picker__bar">
-        <view-block class="nut-picker__left" @click="close()"> 取消</view-block>
+        <view-block class="nut-picker__left" @click="close">取消</view-block>
         <view-block> {{ title }}</view-block>
         <view-block> {{ title }}</view-block>
-        <view-block @click="confirm()"> 确定</view-block>
+        <view-block @click="confirm()">确定</view-block>
       </view-block>
       </view-block>
 
 
       <view-block class="nut-picker__column">
       <view-block class="nut-picker__column">
@@ -45,12 +45,18 @@
   </view-block>
   </view-block>
 </template>
 </template>
 <script lang="ts">
 <script lang="ts">
-import { reactive, ref, watch, computed, toRaw } from 'vue';
+import { reactive, watch, computed, toRaw, toRefs } from 'vue';
 import { createComponent } from '@/utils/create';
 import { createComponent } from '@/utils/create';
 import column from './Column.vue';
 import column from './Column.vue';
 import popup from '@/packages/popup/index.vue';
 import popup from '@/packages/popup/index.vue';
 import { commonProps } from './commonProps';
 import { commonProps } from './commonProps';
-const { create } = createComponent('picker');
+import {
+  PickerObjOpt,
+  PickerOption,
+  PickerObjectColumn,
+  PickerObjectColumns
+} from './types';
+const { create, componentName } = createComponent('picker');
 
 
 export default create({
 export default create({
   children: [column, popup],
   children: [column, popup],
@@ -65,61 +71,82 @@ export default create({
     },
     },
     ...commonProps
     ...commonProps
   },
   },
-  components: { column },
   emits: ['close', 'change', 'confirm', 'update:isVisible'],
   emits: ['close', 'change', 'confirm', 'update:isVisible'],
 
 
   setup(props, { emit }) {
   setup(props, { emit }) {
-    const show = ref(false);
-    const defaultIndex = ref(props.defaultIndex);
-    const formattedColumns: any = ref(props.listData);
-    //临时变量,当点击确定时候赋值
-    let _defaultIndex = props.defaultIndex;
     const childrenKey = 'children';
     const childrenKey = 'children';
     const valuesKey = 'values';
     const valuesKey = 'values';
+    const state = reactive({
+      show: false,
+      formattedColumns: props.listData as PickerObjectColumn[],
+      defaultIndex: props.defaultIndex as number
+    });
+    //临时变量,当点击确定时候赋值
+    let _defaultIndex = props.defaultIndex;
     let defaultIndexList: number[] = [];
     let defaultIndexList: number[] = [];
 
 
-    watch(
-      () => props.isVisible,
-      val => {
-        show.value = val;
-      }
-    );
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true
+      };
+    });
 
 
-    watch(
-      () => props.listData,
-      val => {
-        formattedColumns.value = val;
-      }
-    );
+    const top = computed(() => {
+      return (Number(+props.visibleItemCount - 1) / 2) * +props.itemHeight;
+    });
 
 
-    const addDefaultIndexList = listData => {
-      defaultIndexList = [];
-      listData.forEach(res => {
-        defaultIndexList.push(res.defaultIndex);
-      });
-    };
-    const dataType = computed(() => {
-      const firstColumn = formattedColumns.value[0] || {};
+    const height = computed(() => {
+      return Number(props.visibleItemCount) * +props.itemHeight;
+    });
 
 
+    const dataType = computed(() => {
+      const firstColumn = state.formattedColumns[0] as PickerObjectColumn;
       if (typeof firstColumn === 'object') {
       if (typeof firstColumn === 'object') {
-        if (firstColumn?.[childrenKey]) {
+        if (firstColumn[childrenKey]) {
           return 'cascade';
           return 'cascade';
         } else if (firstColumn?.[valuesKey]) {
         } else if (firstColumn?.[valuesKey]) {
-          addDefaultIndexList(props.listData);
-          //多列
+          addDefaultIndexList(props.listData as PickerObjectColumn[]);
           return 'multipleColumns';
           return 'multipleColumns';
         }
         }
       }
       }
       return 'text';
       return 'text';
     });
     });
-    const formatCascade = (listData, defaultIndex) => {
-      const formatted: any[] = [];
-      let children = listData;
+
+    const columnList = computed(() => {
+      if (dataType.value === 'text') {
+        return [
+          { values: state.formattedColumns, defaultIndex: state.defaultIndex }
+        ];
+      } else if (dataType.value === 'multipleColumns') {
+        return state.formattedColumns;
+      } else if (dataType.value === 'cascade') {
+        return formatCascade(
+          state.formattedColumns as PickerObjectColumn[],
+          state.defaultIndex
+        );
+      }
+      return state.formattedColumns;
+    });
+
+    const addDefaultIndexList = (listData: PickerObjectColumn[]) => {
+      defaultIndexList = [];
+      listData.forEach(res => {
+        defaultIndexList.push(res.defaultIndex as number);
+      });
+    };
+
+    const formatCascade = (
+      listData: PickerObjectColumn[],
+      defaultIndex: number
+    ) => {
+      const formatted: PickerObjectColumn[] = [];
+      let children = listData as PickerObjectColumns;
       children.defaultIndex = defaultIndex;
       children.defaultIndex = defaultIndex;
       while (children) {
       while (children) {
         formatted.push({
         formatted.push({
           values: children,
           values: children,
-          defaultIndex: children.defaultIndex
+          defaultIndex: defaultIndex
         });
         });
         children = children?.[children.defaultIndex || 0].children;
         children = children?.[children.defaultIndex || 0].children;
       }
       }
@@ -127,94 +154,104 @@ export default create({
       return formatted;
       return formatted;
     };
     };
 
 
-    const columnList = computed(() => {
-      if (dataType.value === 'text') {
-        return [
-          { values: formattedColumns.value, defaultIndex: defaultIndex.value }
-        ];
-      } else if (dataType.value === 'multipleColumns') {
-        return formattedColumns.value;
-      } else if (dataType.value === 'cascade') {
-        return formatCascade(formattedColumns.value, defaultIndex.value);
-      }
-      return formattedColumns.value;
-    });
-    const getCascadeData = (listData, defaultIndex) => {
-      let arr = listData;
+    const getCascadeData = (
+      listData: PickerObjectColumn[],
+      defaultIndex: number
+    ) => {
+      let arr = listData as PickerObjectColumns;
       arr.defaultIndex = defaultIndex;
       arr.defaultIndex = defaultIndex;
       const dataList: string[] = [];
       const dataList: string[] = [];
 
 
       while (arr) {
       while (arr) {
         const item = arr[arr.defaultIndex ?? 0];
         const item = arr[arr.defaultIndex ?? 0];
-        dataList.push(item.text);
+        dataList.push(item.text as string);
         arr = item.children;
         arr = item.children;
       }
       }
       return dataList;
       return dataList;
     };
     };
-    return {
-      show,
-      column,
-      title: props.title,
-      dataType,
-      columnList,
-      top: (Number(props.visibleItemCount - 1) / 2) * props.itemHeight,
-      height: Number(props.visibleItemCount) * props.itemHeight,
-      close: () => {
-        emit('close');
-        emit('update:isVisible', false);
-      },
-      changeHandler: (columnIndex, dataIndex) => {
-        if (dataType.value === 'cascade') {
-          let cursor: any = toRaw(formattedColumns.value);
-          //最外层使用props.defaultIndex作为初始index
-          if (columnIndex === 0) {
-            defaultIndex.value = dataIndex;
-          } else {
-            let i = 0;
-            while (cursor) {
-              if (i === columnIndex) {
-                cursor.defaultIndex = dataIndex;
-              } else if (i > columnIndex) {
-                cursor.defaultIndex = 0;
-              }
-              cursor = cursor[cursor.defaultIndex || 0].children;
-              i++;
+
+    const close = () => {
+      emit('close');
+      emit('update:isVisible', false);
+    };
+
+    const changeHandler = (columnIndex: number, dataIndex: number) => {
+      if (dataType.value === 'cascade') {
+        let cursor = toRaw(state.formattedColumns) as PickerObjectColumns;
+        if (columnIndex === 0) {
+          state.defaultIndex = dataIndex;
+        } else {
+          let i = 0;
+          while (cursor) {
+            if (i === columnIndex) {
+              cursor.defaultIndex = dataIndex;
+            } else if (i > columnIndex) {
+              cursor.defaultIndex = 0;
             }
             }
+            cursor = cursor[cursor.defaultIndex || 0].children;
+            i++;
           }
           }
-        } else if (dataType.value === 'text') {
-          _defaultIndex = dataIndex;
-        } else if (dataType.value === 'multipleColumns') {
-          defaultIndexList[columnIndex] = dataIndex;
-          const val = defaultIndexList.map(
-            (res, i) => toRaw(formattedColumns.value)[i].values[res]
-          );
-          console.log('val', defaultIndexList);
-          emit('change', val);
         }
         }
-      },
-
-      confirm: () => {
-        if (dataType.value === 'text') {
-          defaultIndex.value = _defaultIndex;
-          emit('confirm', formattedColumns.value[_defaultIndex]);
-        } else if (dataType.value === 'multipleColumns') {
-          for (let i = 0; i < defaultIndexList.length; i++) {
-            formattedColumns.value[i].defaultIndex = defaultIndexList[i];
-          }
-          const checkedArr = toRaw(formattedColumns.value).map(
-            (res: any) => res.values[res.defaultIndex]
-          );
-          console.log(formattedColumns.value);
-          emit('confirm', checkedArr);
-        } else if (dataType.value === 'cascade') {
-          emit(
-            'confirm',
-            getCascadeData(toRaw(formattedColumns.value), defaultIndex.value)
-          );
+      } else if (dataType.value === 'text') {
+        _defaultIndex = dataIndex;
+      } else if (dataType.value === 'multipleColumns') {
+        defaultIndexList[columnIndex] = dataIndex;
+        const val = defaultIndexList.map(
+          (res, i) =>
+            toRaw(state.formattedColumns as PickerObjectColumns)[i].values[res]
+        );
+        emit('change', val);
+      }
+    };
+
+    const confirm = () => {
+      if (dataType.value === 'text') {
+        state.defaultIndex = _defaultIndex as number;
+        emit('confirm', state.formattedColumns[_defaultIndex as number]);
+      } else if (dataType.value === 'multipleColumns') {
+        for (let i = 0; i < defaultIndexList.length; i++) {
+          state.formattedColumns[i].defaultIndex = defaultIndexList[i];
         }
         }
+        const checkedArr = toRaw(state.formattedColumns).map(
+          (res: PickerObjectColumn) =>
+            res.values && res.values[res.defaultIndex as number]
+        );
+        emit('confirm', checkedArr);
+      } else if (dataType.value === 'cascade') {
+        emit(
+          'confirm',
+          getCascadeData(toRaw(state.formattedColumns), state.defaultIndex)
+        );
+      }
+
+      emit('update:isVisible', false);
+    };
+
+    watch(
+      () => props.isVisible,
+      val => {
+        state.show = val;
+      }
+    );
 
 
-        emit('update:isVisible', false);
+    watch(
+      () => props.listData,
+      val => {
+        state.formattedColumns = val as PickerObjectColumns;
       }
       }
+    );
+
+    return {
+      classes,
+      ...toRefs(state),
+      column,
+      dataType,
+      columnList,
+      top,
+      height,
+      close,
+      changeHandler,
+      confirm
     };
     };
   }
   }
 });
 });

+ 15 - 0
src/packages/picker/types.ts

@@ -0,0 +1,15 @@
+export type PickerObjOpt = {
+  text?: string;
+  [key: string]: any;
+};
+
+export type PickerOption = string | PickerObjOpt;
+
+export type PickerObjectColumn = {
+  values?: PickerOption[];
+  defaultIndex?: number;
+  children?: PickerOption[];
+  [key: string]: any;
+};
+
+export type PickerObjectColumns = PickerObjectColumn & PickerObjOpt[];

+ 78 - 0
src/packages/textarea/demo.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="demo-nopading">
+    <h2>基础用法</h2>
+    <nut-textarea
+      v-model:value="state.val0"
+      @change="change"
+      rows="5"
+      placeholder="高度可拉伸"
+      :autosize="true"
+      label="留言:"
+    />
+    <h2>显示字数统计</h2>
+    <nut-textarea
+      v-model:value="state.val1"
+      @change="change"
+      rows="5"
+      :limitShow="true"
+      maxLength="20"
+      type="textarea"
+      placeholder="设置输入五行"
+      label="留言:"
+    />
+  </div>
+</template>
+
+<script lang="ts">
+import { reactive } from 'vue';
+import { createComponent } from '@/utils/create';
+const { createDemo } = createComponent('textarea');
+export default createDemo({
+  setup() {
+    const state = reactive({
+      val0: '',
+      val1: '初始数据'
+    });
+    setTimeout(function() {
+      state.val1 = '异步测试数据,2秒';
+    }, 2000);
+    const change = (num: string | number) => {
+      console.log('change: ', num);
+    };
+    const focus = (num: string | number) => {
+      console.log('focus:', num);
+    };
+    const blur = (num: string | number) => {
+      console.log('blur:', num);
+    };
+    const clear = (num: string | number) => {
+      console.log('clear:', num);
+    };
+
+    return {
+      state,
+      change,
+      blur,
+      clear,
+      focus
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.demo-nopading {
+  height: 100%;
+  background: #f7f8fa;
+  overflow-x: hidden;
+  overflow-y: auto;
+  padding: 0;
+  padding-top: 57px;
+  h2 {
+    padding-left: 25px;
+    margin-top: 25px;
+    margin-bottom: 10px;
+    color: #909ca4;
+  }
+}
+</style>

+ 69 - 0
src/packages/textarea/doc.md

@@ -0,0 +1,69 @@
+# Input 输入框组件
+
+### 介绍
+
+
+### 安装
+
+``` javascript
+import { createApp } from 'vue';
+import { input } from '@nutui/nutui';
+
+const app = createApp();
+app.use(input);
+
+```
+## 代码演示
+
+### 基础用法
+
+
+```html
+<nut-textarea
+      v-model:value="state.val0"
+      @change="change"
+      rows="5"
+       placeholder="高度可拉伸"
+      :autosize="true"
+      label="留言:"
+    />
+```
+
+### 显示字数统计
+
+
+```html
+ <nut-textarea
+       v-model:value="state.val1"
+      @change="change"
+      rows="5"
+      :limitShow="true"
+      maxLength="20"
+      type="textarea"
+      placeholder="设置输入五行"
+      label="留言:"
+    />
+```
+
+
+
+| 参数         | 说明                             | 类型   | 默认值           |
+|--------------|----------------------------------|--------|------------------|
+| value      | 输入值,双向绑定 | String |  -     |
+| placeholder         | 为空时占位符 | String |       -       |
+| label          | 	左侧文案                       | string | -             |
+| maxlength          | 限制最长输入字符                   | string/number | -               |
+| rows          | textarea时高度                   | string/number | 2             |
+| limit-show          | textarea时是否展示输入字符。须设置maxlength                        | boolean | `false`               |
+| change          | 输入内容时触发                        | function | -               |
+| focus          | 聚焦时触发                        | function | -               |
+| blur          | 失焦时触发                        | function | -               |
+| clear          | 点击清空时触发                        | function | -               |
+
+
+
+
+
+
+
+

+ 54 - 0
src/packages/textarea/index.scss

@@ -0,0 +1,54 @@
+.nut-textarea {
+  position: relative;
+  width: 100%;
+  padding: 10px 0px 10px 25px;
+  display: flex;
+  background: rgba(255, 255, 255, 1);
+  border-bottom: 1px solid rgba(234, 240, 251, 1);
+  font-size: 14px;
+  input {
+    width: 230px;
+    flex: 1;
+    padding: 0 10px;
+  }
+  .nut-input-label {
+    width: 80px;
+    overflow: hidden;
+    display: inline-block;
+    text-align: left;
+    .label-string {
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+  }
+  .nut-textinput-clear {
+    width: 16px;
+    height: 16px;
+    position: absolute;
+    right: 15px;
+  }
+  .nut-text {
+    flex: 1;
+    padding: 0 10px;
+    .nut-text-limit {
+      float: right;
+      color: rgba(153, 153, 153, 1);
+    }
+    .nut-text-core {
+      outline: none;
+      display: block;
+      box-sizing: border-box;
+      width: 100%;
+      min-width: 0;
+      margin: 0;
+      padding: 0;
+      color: #323233;
+      line-height: inherit;
+      text-align: left;
+      background-color: transparent;
+      border: 0;
+      resize: none;
+    }
+  }
+}

+ 192 - 0
src/packages/textarea/index.vue

@@ -0,0 +1,192 @@
+<template>
+  <view class="nut-textarea">
+    <view class="nut-input-label">
+      <view v-if="props.label" class="label-string">{{ props.label }}</view>
+    </view>
+    <view class="nut-text">
+      <textarea
+        :style="styles"
+        :rows="props.rows"
+        @input="valueChange"
+        v-model="state.curretvalue"
+        class="nut-text-core"
+        :maxlength="maxLength"
+        :placeholder="props.placeholder"
+        :disabled="props.disabled"
+        :readonly="props.readonly"
+      >
+      </textarea>
+      <view class="nut-text-limit" v-if="limitShow">
+        <view :class="[{ 'nut-field-over': state.textNum > maxLength }]">{{
+          state.textNum
+        }}</view>
+        <view>/{{ maxLength }}</view>
+      </view>
+    </view>
+  </view>
+</template>
+<script lang="ts">
+import { ref, toRefs, reactive, computed } from 'vue';
+import { createComponent } from '@/utils/create';
+
+const { componentName, create } = createComponent('textarea');
+interface Events {
+  eventName: 'change' | 'focus' | 'blur' | 'clear' | 'update:value';
+  params: (string | number | Event)[];
+}
+export default create({
+  props: {
+    textAlign: {
+      type: String,
+      default: 'left'
+    },
+    limitShow: {
+      type: Boolean,
+      default: false
+    },
+    maxLength: {
+      type: String,
+      default: ''
+    },
+    rows: {
+      type: String,
+      default: ''
+    },
+    label: {
+      type: String,
+      default: ''
+    },
+    placeholder: {
+      type: String,
+      default: '请输入信息'
+    },
+    readonly: {
+      type: Boolean,
+      default: false
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    autosize: {
+      type: Boolean,
+      default: false
+    },
+    value: {
+      type: [String, Number],
+      default: ''
+    }
+  },
+
+  emits: ['change', 'update:value', 'blur', 'focus', 'clear', 'error'],
+
+  setup(props, { emit }) {
+    const { maxLength } = props;
+    const { value } = toRefs(props);
+    const active = ref(false);
+    const state = reactive({
+      curretvalue: value,
+      textNum: String(value.value).length
+    });
+    const classes = computed(() => {
+      return {
+        [componentName]: true
+      };
+    });
+    const styles = computed(() => {
+      return {
+        'text-align': props.textAlign,
+        resize: props.autosize ? 'vertical' : 'none'
+      };
+    });
+    const emitChange = (envs: Array<Events>) => {
+      envs.forEach((item: Events) => {
+        return emit(item.eventName, ...item.params);
+      });
+    };
+    const valueChange = (e: Event) => {
+      const input = e.target as HTMLInputElement;
+      let val = input.value;
+
+      if (maxLength && val.length > Number(maxLength)) {
+        val = val.slice(0, Number(maxLength));
+      }
+      state.textNum = val.length;
+      emitChange([
+        {
+          eventName: 'update:value',
+          params: [val]
+        },
+        {
+          eventName: 'change',
+          params: [val]
+        }
+      ]);
+    };
+    const valueFocus = (e: Event) => {
+      active.value = true;
+      const input = e.target as HTMLInputElement;
+      let val = input.value;
+      val = String(val);
+      emitChange([
+        {
+          eventName: 'update:value',
+          params: [state.curretvalue]
+        },
+        {
+          eventName: 'focus',
+          params: [val]
+        }
+      ]);
+    };
+    const valueBlur = (e: Event) => {
+      setTimeout(() => {
+        active.value = false;
+      }, 400);
+      const input = e.target as HTMLInputElement;
+      let val = input.value;
+      val = String(val);
+      emitChange([
+        {
+          eventName: 'update:value',
+          params: [val]
+        },
+        {
+          eventName: 'blur',
+          params: [val]
+        }
+      ]);
+    };
+    const handleClear = () => {
+      const val = '';
+      emitChange([
+        {
+          eventName: 'update:value',
+          params: [val]
+        },
+        {
+          eventName: 'clear',
+          params: [val]
+        }
+      ]);
+    };
+    return {
+      props,
+      value,
+      state,
+      styles,
+      active,
+      maxLength,
+      valueChange,
+      valueFocus,
+      valueBlur,
+      handleClear,
+      emitChange
+    };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>