vue3新拟态组件库开发流程——input组件源码

有了开发button的经验,copy过来删删改改获得最基础的框架:

<script setup>
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import { useProp, useNeumorphism } from '../mixin/neumorphism'
const props = defineProps({
    ...useProp,
    ...{
      disabled: {
        type: Boolean,
        default: false
      }
    }
})
const { baseStyleObject } = useNeumorphism(props)
let styleObject =  computed(() => ({ 

}))
</script>

<template>
  <input 
    class="san-input"
    :style="{...baseStyleObject,...styleObject}"
    v-bind="$attrs"
    @input="$emit('input', $event.target.value)"
    @focus="$emit('focus', $event.target.value)"
    @blur="$emit('blur', $event.target.value)"
    @change="$emit('change', $event.target.value)"
  />
</template>

<script>
export default {
  name: 'sanorin-input',
}
</script>

<style scoped>
@import "../../style/index.css";

.san-input{
  width: 180px;
}
.san-input {
  outline: none;
  color: var(--text-color);
  padding: 10px;
  border: none;
  border-radius: 5px;
  background: linear-gradient(var(--linear-angle), var(--linear-left-color), var(--linear-right-color));
  box-shadow: inset var(--shadow-ah) var(--shadow-av) var(--shadow-blur) var(--left-color), inset var(--shadow-bh) var(--shadow-bv) var(--shadow-blur) var(--right-color);
}
</style>

往下写,先支持disabled,支持disable的时候出现一个问题,我先在测试页面写下测试示例<sanorin-input v-model="input" :disabled="true"></sanorin-input>这里已经成功禁用了输入,我想禁用输入的时候改变鼠标样式,就需要prop接收下来disabled然后绑定到样式上:class="[{'is-disabled': disabled}]" ,于是,attrsv-bind="$attrs"就接收不到diasable了,因此这里需要手动绑定下:disabled="disabled"

<script setup>
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import { useProp, useNeumorphism } from '../mixin/neumorphism'
const props = defineProps({
    ...useProp,
    ...{
      disabled: {
        type: Boolean,
        default: false
      }
    }
})
const { baseStyleObject } = useNeumorphism(props)
let styleObject =  computed(() => ({ 

}))
</script>

<template>
  <input 
    class="san-input"
    :class="[{'is-disabled': disabled}]" 
    :style="{...baseStyleObject,...styleObject}"
    v-bind="$attrs"
    @input="$emit('input', $event.target.value)"
    @focus="$emit('focus', $event.target.value)"
    @blur="$emit('blur', $event.target.value)"
    @change="$emit('change', $event.target.value)"
    :disabled="disabled"
  />
</template>

<script>
export default {
  name: 'sanorin-input',
}
</script>

<style scoped>
@import "../../style/index.css";

.san-input{
  width: 180px;
}
.san-input {
  outline: none;
  color: var(--text-color);
  padding: 10px;
  border: none;
  border-radius: 5px;
  background: linear-gradient(var(--linear-angle), var(--linear-left-color), var(--linear-right-color));
  box-shadow: inset var(--shadow-ah) var(--shadow-av) var(--shadow-blur) var(--left-color), inset var(--shadow-bh) var(--shadow-bv) var(--shadow-blur) var(--right-color);
}
</style>

接下来加入is-clearable的代码

<script setup>
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import { useProp, useNeumorphism } from '../mixin/neumorphism'
const emit = defineEmits(['focus','blur','clear','input','change','update:modelValue'])
const handleFocus = (evt) => { focused.value = true; emit('focus', evt) }
const handleBlur = (evt) => { focused.value = false; emit('blur', evt) }
const handleClear = () => { emit('input', ''); emit('change', ''); emit('clear') }
const props = defineProps({
    ...useProp,
    ...{
      modelValue: {
        type: [String, Number],
        default: ''
      },
      disabled: {
        type: Boolean,
        default: false
      },
      clearable: {
        type: Boolean,
        default: false
      }
    }
})
let hovering = ref(false)
let focused = ref(false)
let nativeInputValue = computed(() => props.value === null || props.value === undefined ? '' : String(props.value))
let showClear = computed(() => props.clearable && !props.disabled && (focused.value || hovering.value) && !!nativeInputValue)

const { baseStyleObject } = useNeumorphism(props)
let styleObject =  computed(() => ({ 

}))
</script>

<template>
  <div
    class="san-input neumorphism flex"
    :class="[{
        'is-disabled': disabled,
    }]"
    :style="{...baseStyleObject,...styleObject}"
    @mouseenter="hovering = true"
    @mouseleave="hovering = false">
    <input 
      :value="modelValue"
      v-bind="$attrs"
      :disabled="disabled"
      @input="emit('update:modelValue', $event.target.value)"
      @focus="handleFocus"
      @blur="handleBlur"
      @change="emit('change', $event.target.value)"
    />
    <!-- 后置内容 -->
    <span
      v-if="showClear">
      <span>
        <i v-if="showClear"
          class="iconfont san-qingchu"
          @mousedown.prevent
          @click="handleClear"
        ></i>
      </span>
    </span>
  </div>
  
</template>

<script>
export default {
  name: 'sanorin-input',
}
</script>

<style scoped>
@import "../../style/index.css";
@import "../../style/neumorphism.css";

.san-input{
  height: 22px;
  width: 250px;
  padding: 10px;
  border-radius: 5px;
}
.san-input > input{
  flex: 1;
  width: 220px;
  background: transparent;
  border: none;
  outline: none;
}

</style>

接下来加入密码框,要有查看密码的功能,认情况input的type是passport,点击查看按钮,type就变成text。

<script setup>
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import { useProp, useNeumorphism } from '../mixin/neumorphism'
const emit = defineEmits(['focus','blur','clear','change','update:modelValue'])
const handleFocus = (evt) => { focused.value = true; emit('focus', evt) }
const handleBlur = (evt) => { focused.value = false; emit('blur', evt) }
const props = defineProps({
    ...useProp,
    ...{
      modelValue: {
        type: [String, Number],
        default: ''
      },
      disabled: {
        type: Boolean,
        default: false
      },
      clearable: {
        type: Boolean,
        default: false
      },
      showPassword: {
        type: Boolean,
        default: false
      },
    }
})
let hovering = ref(false)
let focused = ref(false)
let nativeInputValue = computed(() => props.value === null || props.value === undefined ? '' : String(props.value))
// clearable
let showClear = computed(() => props.clearable && !props.disabled && (focused.value || hovering.value) && !!nativeInputValue)
const handleClear = () => { emit('update:modelValue', ''); emit('change', ''); emit('clear') }
// password
let passwordVisible = ref(false)
const handlePassword = () => { passwordVisible.value = !passwordVisible.value }

const { baseStyleObject } = useNeumorphism(props)
let styleObject =  computed(() => ({ 

}))
</script>

<template>
  <div
    class="san-input neumorphism flex"
    :class="[{
        'is-disabled': disabled,
    }]"
    :style="{...baseStyleObject,...styleObject}"
    @mouseenter="hovering = true"
    @mouseleave="hovering = false">
    <input 
      :value="modelValue"
      v-bind="$attrs"
      :disabled="disabled"
      :type="showPassword ? (passwordVisible ? 'text': 'password') : type"
      @input="emit('update:modelValue', $event.target.value)"
      @focus="handleFocus"
      @blur="handleBlur"
      @change="emit('change', $event.target.value)"
    />
    <!-- 后置内容 -->
    <span v-if="showClear">
      <i v-if="showClear"
        class="iconfont san-qingchu"
        @mousedown.prevent
        @click="handleClear"
      ></i>
    </span>
    <span v-if="showPassword">
      <i v-if="!passwordVisible"
        class="iconfont san-bukejian"
        @mousedown.prevent
        @click="handlePassword"
      ></i>
      <i v-if="passwordVisible"
        class="iconfont san-kejian"
        @mousedown.prevent
        @click="handlePassword"
      ></i>
    </span>
  </div>
  
</template>

<script>
export default {
  name: 'sanorin-input',
}
</script>

<style scoped>
@import "../../style/index.css";
@import "../../style/neumorphism.css";

.san-input{
  height: 22px;
  width: 250px;
  padding: 10px;
  border-radius: 5px;
}
.san-input > input{
  flex: 1;
  width: 220px;
  background: transparent;
  border: none;
  outline: none;
}

</style>

接下来加入三点
icon
尺寸
textarea(初步)

<script setup>
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import { useProp, useNeumorphism } from '../mixin/neumorphism'
const input = ref() // 与html中ref=""对应,定位dom元素
const emit = defineEmits(['focus','blur','clear','change','update:modelValue'])
const handleFocus = (evt) => { focused.value = true; emit('focus', evt) }
const handleBlur = (evt) => { focused.value = false; emit('blur', evt) }
const props = defineProps({
    ...useProp,
    ...{
      modelValue: {
        type: [String, Number],
        default: ''
      },
      disabled: {
        type: Boolean,
        default: false
      },
      clearable: {
        type: Boolean,
        default: false
      },
      showPassword: {
        type: Boolean,
        default: false
      },
      preIcon: {
          type: String,
          default: null,
      },
      postIcon: {
          type: String,
          default: null,
      },
      type: {
        type: String,
        default: 'text'
      },
      size: {
          type: Number,
          default: 40
      },
    }
})
let hovering = ref(false)
let focused = ref(false)
let nativeInputValue = computed(() => props.value === null || props.value === undefined ? '' : String(props.value))
// clearable
let showClear = computed(() => props.clearable && !props.disabled && (focused.value || hovering.value) && !!nativeInputValue)
const handleClear = () => { emit('update:modelValue', ''); emit('change', ''); emit('clear') }
// password
let passwordVisible = ref(false)
const handlePassword = () => { passwordVisible.value = !passwordVisible.value; focus() }
const focus = () => { input.value.focus(); setTimeout(() => { input.value.setSelectionRange(-1,-1) }) } // 点击密码后自动对焦并焦点至为最后一位

const { baseStyleObject } = useNeumorphism(props)
let styleObject =  computed(() => ({ 
  '--font-size': `${props.size-26}px`, '--line-height': `${props.size-20}px`,
}))
</script>

<template>
  <div
    class="san-input neumorphism flex"
    :class="[{
        'is-disabled': disabled,
    }]"
    :style="{...baseStyleObject,...styleObject}"
    @mouseenter="hovering = true"
    @mouseleave="hovering = false">
    <!-- 前置内容 -->
    <i v-if="preIcon" :class="[preIcon, 'iconfont']"></i>
    <!-- 主体内容 -->
    <input 
      v-if="type !== 'textarea'"
      ref="input"
      :value="modelValue"
      v-bind="$attrs"
      :disabled="disabled"
      :type="showPassword ? (passwordVisible ? 'text': 'password') : type"
      @input="emit('update:modelValue', $event.target.value)"
      @focus="handleFocus"
      @blur="handleBlur"
      @change="emit('change', $event.target.value)"
    />
    <textarea 
      v-else
      ref="textarea"
      :value="modelValue"
      v-bind="$attrs"
      :disabled="disabled"
      @input="emit('update:modelValue', $event.target.value)"
      @focus="handleFocus"
      @blur="handleBlur"
      @change="emit('change', $event.target.value)"
    />
    <!-- 后置内容 -->
    <i v-if="showClear" class="iconfont san-qingchu" @click="handleClear"></i>
    <template v-if="showPassword">
      <i v-if="!passwordVisible" class="iconfont san-bukejian" @click="handlePassword"></i>
      <i v-if="passwordVisible" class="iconfont san-kejian" @click="handlePassword"></i>
    </template>
    <i v-if="postIcon" :class="[postIcon, 'iconfont']"></i>
  </div>
  
</template>

<script>
export default {
  name: 'sanorin-input',
}
</script>

<style scoped>
@import "../../style/index.css";
@import "../../style/neumorphism.css";

.san-input{
  width: 250px;
  padding: 10px;
  border-radius: 5px;
}
.san-input > input{
  flex: 1;
  width: 220px;
  background: transparent;
  border: none;
  outline: none;
  padding: 0px;
  font-size: var(--font-size);
  line-height: var(--line-height);
}
.san-input > textarea{
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
}
</style>

接下里增加输入长度限制

<script setup>
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import { useProp, useNeumorphism } from '../mixin/neumorphism'
const input = ref() // 与html中ref=""对应,定位dom元素
const emit = defineEmits(['focus','blur','clear','change','update:modelValue'])
const handleFocus = (evt) => { focused.value = true; emit('focus', evt) }
const handleBlur = (evt) => { focused.value = false; emit('blur', evt) }
const props = defineProps({
    ...useProp,
    ...{
      modelValue: {
        type: [String, Number],
        default: ''
      },
      disabled: {
        type: Boolean,
        default: false
      },
      clearable: {
        type: Boolean,
        default: false
      },
      showPassword: {
        type: Boolean,
        default: false
      },
      preIcon: {
          type: String,
          default: null,
      },
      postIcon: {
          type: String,
          default: null,
      },
      type: {
        type: String,
        default: 'text'
      },
      size: {
          type: Number,
          default: 40
      }
    }
})
let hovering = ref(false)
let focused = ref(false)
let nativeInputValue = computed(() => props.value === null || props.value === undefined ? '' : String(props.value))
// clearable
let showClear = computed(() => props.clearable && !props.disabled && (focused.value || hovering.value) && !!nativeInputValue)
const handleClear = () => { emit('update:modelValue', ''); emit('change', ''); emit('clear') }
// password
let passwordVisible = ref(false)
const handlePassword = () => { passwordVisible.value = !passwordVisible.value; focus() }
const focus = () => { input.value.focus(); setTimeout(() => { input.value.setSelectionRange(-1,-1) }) } // 点击密码后自动对焦并焦点至为最后一位
// limit
const textLength = computed(() => String(props.modelValue).length)

const { baseStyleObject } = useNeumorphism(props)
let styleObject =  computed(() => ({ 
  '--font-size': `${props.size-26}px`, '--line-height': `${props.size-20}px`, '--limit-size': `${props.size-28}px`
}))
</script>

<template>
  <div
    class="san-input neumorphism flex"
    :class="[{
        'is-disabled': disabled,
    }]"
    :style="{...baseStyleObject,...styleObject}"
    @mouseenter="hovering = true"
    @mouseleave="hovering = false">
    <!-- 前置内容 -->
    <i v-if="preIcon" :class="[preIcon, 'iconfont']"></i>
    <!-- 主体内容 -->
    <input 
      v-if="type !== 'textarea'"
      ref="input"
      :value="modelValue"
      v-bind="$attrs"
      :disabled="disabled"
      :type="showPassword ? (passwordVisible ? 'text': 'password') : type"
      @input="emit('update:modelValue', $event.target.value)"
      @focus="handleFocus"
      @blur="handleBlur"
      @change="emit('change', $event.target.value)"
    />
    <textarea 
      v-else
      ref="textarea"
      :value="modelValue"
      v-bind="$attrs"
      :disabled="disabled"
      @input="emit('update:modelValue', $event.target.value)"
      @focus="handleFocus"
      @blur="handleBlur"
      @change="emit('change', $event.target.value)"
    />
    <!-- 后置内容 -->
    <span class="limit" v-if="$attrs.maxlength">{{textLength}}/{{$attrs.maxlength}}</span>
    <i v-if="showClear" class="iconfont san-qingchu" @click="handleClear"></i>
    <template v-if="showPassword">
      <i v-if="!passwordVisible" class="iconfont san-bukejian" @click="handlePassword"></i>
      <i v-if="passwordVisible" class="iconfont san-kejian" @click="handlePassword"></i>
    </template>
    <i v-if="postIcon" :class="[postIcon, 'iconfont']"></i>
  </div>
  
</template>

<script>
export default {
  name: 'sanorin-input',
}
</script>

<style scoped>
@import "../../style/index.css";
@import "../../style/neumorphism.css";

.san-input{
  width: 250px;
  padding: 10px;
  border-radius: 5px;
}
.san-input > input{
  flex: 1;
  width: 220px;
  background: transparent;
  border: none;
  outline: none;
  padding: 0px;
  font-size: var(--font-size);
  line-height: var(--line-height);
}
.limit{
  /* color: ; */
  font-size: var(--limit-size);
}
.san-input > textarea{
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
}
</style>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值