从0搭建vue3组件库: Input组件( 三 )

相关样式部分
.k-input__suffix,.k-input__prefix {position: absolute;right: 10px;height: 100%;top: 0;display: flex;align-items: center;cursor: pointer;color: #c0c4cc;font-size: 15px;}.no-cursor {cursor: default;}.k-input--prefix.k-input__inner {padding-left: 30px;}.k-input__prefix {position: absolute;width: 20px;cursor: default;left: 10px;}app.vue中使用效果如下
<template><div class="input-demo"><Input v-model="tel" suffixIcon="edit" placeholder="请输入内容" /><Input v-model="tel" prefixIcon="edit" placeholder="请输入内容" /></div></template><script lang="ts" setup>import { Input } from "kitty-ui";import { ref } from "vue";const tel = ref("");</script><style lang="less">.input-demo {width: 200px;}</style>

从0搭建vue3组件库: Input组件

文章插图
文本域将type属性的值指定为textarea即可展示文本域模式 。它绑定的事件以及属性和input基本一样
<template><div class="k-textarea" v-if="attrs.type === 'textarea'"><textareaclass="k-textarea__inner":style="textareaStyle"v-bind="attrs"ref="textarea":value="https://www.huyubaike.com/biancheng/inputProps.modelValue"@input="changeInputVal"/></div><divv-elseclass="k-input"@mouseenter="isEnter = true"@mouseleave="isEnter = false":class="styleClass">...</div></template>样式基本也就是focus,hover改变 border 颜色
.k-textarea {width: 100%;.k-textarea__inner {display: block;padding: 5px 15px;line-height: 1.5;box-sizing: border-box;width: 100%;font-size: inherit;color: #606266;background-color: #fff;background-image: none;border: 1px solid #dcdfe6;border-radius: 4px;&::placeholder {color: #c2c2ca;}&:hover {border: 1px solid #c0c4cc;}&:focus {outline: none;border: 1px solid #409eff;}}}
从0搭建vue3组件库: Input组件

文章插图
可自适应高度文本域组件可以通过接收autosize属性来开启自适应高度,同时autosize也可以传对象形式来指定最小和最大行高
type AutosizeObj = {minRows?: numbermaxRows?: number}type InputProps = {autosize?: boolean | AutosizeObj}具体实现原理是通过监听输入框值的变化来调整textarea的样式,其中用到了一些原生的方法譬如window.getComputedStyle(获取原生css对象),getPropertyValue(获取css属性值)等,所以原生js忘记的可以复习一下
...const textareaStyle = ref<any>()const textarea = shallowRef<HTMLTextAreaElement>()watch(() => inputProps.modelValue, () => {if (attrs.type === 'textarea' && inputProps.autosize) {const minRows = isObject(inputProps.autosize) ? (inputProps.autosize as AutosizeObj).minRows : undefinedconst maxRows = isObject(inputProps.autosize) ? (inputProps.autosize as AutosizeObj).maxRows : undefinednextTick(() => {textareaStyle.value = https://www.huyubaike.com/biancheng/calcTextareaHeight(textarea.value!, minRows, maxRows)})}}, { immediate: true })其中calcTextareaHeight
const isNumber = (val: any): boolean => {return typeof val === 'number'}//隐藏的元素let hiddenTextarea: HTMLTextAreaElement | undefined = undefined//隐藏元素样式const HIDDEN_STYLE = `height:0 !important;visibility:hidden !important;overflow:hidden !important;position:absolute !important;z-index:-1000 !important;top:0 !important;right:0 !important;`const CONTEXT_STYLE = ['letter-spacing','line-height','padding-top','padding-bottom','font-family','font-weight','font-size','text-rendering','text-transform','width','text-indent','padding-left','padding-right','border-width','box-sizing',]type NodeStyle = {contextStyle: stringboxSizing: stringpaddingSize: numberborderSize: number}type TextAreaHeight = {height: stringminHeight?: string}function calculateNodeStyling(targetElement: Element): NodeStyle {//获取实际textarea样式返回并赋值给隐藏的textareaconst style = window.getComputedStyle(targetElement)const boxSizing = style.getPropertyValue('box-sizing')const paddingSize =Number.parseFloat(style.getPropertyValue('padding-bottom')) +Number.parseFloat(style.getPropertyValue('padding-top'))const borderSize =Number.parseFloat(style.getPropertyValue('border-bottom-width')) +Number.parseFloat(style.getPropertyValue('border-top-width'))const contextStyle = CONTEXT_STYLE.map((name) => `${name}:${style.getPropertyValue(name)}`).join(';')return { contextStyle, paddingSize, borderSize, boxSizing }}export function calcTextareaHeight(targetElement: HTMLTextAreaElement,minRows = 1,maxRows?: number): TextAreaHeight {if (!hiddenTextarea) {//创建隐藏的textareahiddenTextarea = document.createElement('textarea')document.body.appendChild(hiddenTextarea)}//给隐藏的teatarea赋予实际textarea的样式以及值(value)const { paddingSize, borderSize, boxSizing, contextStyle } =calculateNodeStyling(targetElement)hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`)hiddenTextarea.value = https://www.huyubaike.com/biancheng/targetElement.value || targetElement.placeholder ||''//隐藏textarea整个高度,包括内边距padding,borderlet height = hiddenTextarea.scrollHeightconst result = {} as TextAreaHeight//判断boxSizing,返回实际高度if (boxSizing === 'border-box') {height = height + borderSize} else if (boxSizing === 'content-box') {height = height - paddingSize}hiddenTextarea.valuehttps://www.huyubaike.com/biancheng/= ''//计算单行高度const singleRowHeight = hiddenTextarea.scrollHeight - paddingSizeif (isNumber(minRows)) {let minHeight = singleRowHeight * minRowsif (boxSizing === 'border-box') {minHeight = minHeight + paddingSize + borderSize}height = Math.max(minHeight, height)result.minHeight = `${minHeight}px`}if (isNumber(maxRows)) {let maxHeight = singleRowHeight * maxRows!if (boxSizing === 'border-box') {maxHeight = maxHeight + paddingSize + borderSize}height = Math.min(maxHeight, height)}result.height = `${height}px`hiddenTextarea.parentNode?.removeChild(hiddenTextarea)hiddenTextarea = undefinedreturn result}

推荐阅读