这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战
写在前面:
- 个人前端网站:zhangqiang.hk.cn
- 欢迎加入博主的前端学习qq交流群::706947563,专注前端开发,共同学习进步!
1 简要概括
本文旨在写一个跟ant design里面Select组件一样的组件,其带有搜索、多选功能;
技术栈:react+typescript+css (除此之外,不依赖其他任何插件比如jquery、或者他人封装好的select组件)。
在写本篇文章时,本组件还正在开发,我会实时更新开发进度,与代码。希望可以把这个做出来吧!
2 思路,来龙去脉
首先我们来看下antd的select
这个既有下拉框,又有搜索,还能多选。
我一开始想的是,用html的select标签来写,然后发现不行;接着想着用input标签,然后发现也不行;再然后试着用html5的
datalist来做,还是不行。
思来想去,看了antd的元素源码,
发现他这个大部分都是div+span
万剑归宗,就用div来写了!! 然后再加上香喷喷的css,隔壁的小孩都馋哭了... 咳!回归正传,目前确定的思路就是div+span+css了,代码已经开动。
3 开始 show code
- tsx
import React, { useState } from 'react';
// import style from './index.sass'
import {
OptionsType,
} from './data.d';
import './index.css';
const SelectForBase: React.FC<{}> = (props: any) => {
const [isOnFocus, setIsOnFocus] = useState(false);
const [optionsClickedList, setOptionsClickedList] = useState<OptionsType[]>([
{ label: 'qwer', value: '3' },
{ label: 'err', value: '4' },
]); // 当前选中的选项
const options: OptionsType[] = [
{ label: 'sdf', value: '1' },
{ label: 'as', value: '2' },
{ label: 'qwer', value: '3' },
{ label: 'err', value: '4' },
{ label: 'vss', value: '5' },
{ label: 'vscode', value: '6' },
];
const onFocus = () => {
// console.log('获取焦点');
setIsOnFocus(true);
}
const onBlur = () => {
// console.log('失去焦点');
setIsOnFocus(false);
}
// 删除选中的option
const areDeleteOption = (value: OptionsType) => {
console.log('value:', value);
let tempArr = optionsClickedList, index = -1;
for (let i = 0; i < tempArr.length; i++) {
if (tempArr[i].value === value.value) {
index = i;
console.log('index:', index);
break;
}
}
if (index !== -1) {
tempArr.splice(index, 1);
console.log(tempArr);
setOptionsClickedList([...tempArr]);
}
}
return (
<>
<div className='select-body'>
<div className={`select-header ${isOnFocus ? 'select-header-visited' : ''}`} suppressContentEditableWarning contentEditable="true" onFocus={onFocus} onBlur={onBlur}>
{
optionsClickedList.map((item: OptionsType, index: number) => {
return <span key={index} suppressContentEditableWarning contentEditable='false' >
<span className={`card`} >{item?.label}
<span onClick={(e) => { console.log(e); areDeleteOption(item); }} >x</span>
</span>
</span>
})
}
</div>
</div>
</>
)
}
export default SelectForBase;
- css
/* 下拉框整体样式 */
.select-body {
box-sizing: border-box;
position: relative;
display: inline-block;
width: 100%;
height: 60px;
margin: 0;
padding: 0;
color: #000000d9;
font-size: 14px;
}
/* 下拉框搜索框 */
.select-body .select-header {
width: 100%;
min-height: 30px;
position: relative;
border: 1px solid #dad8d8;
padding: 5px;
background-color: #fff;
transition: all .3s cubic-bezier(.645, .045, .355, 1);
cursor: text;
}
.select-body .select-header:hover {
border: 1px solid rgb(62, 165, 249);
}
[contenteditable]:focus {
outline: none;
}
.select-body .select-header-visited {
border: 1px solid rgb(62, 165, 249);
box-shadow: rgba(62, 165, 249, 0.5) 0px 0px 10px;
}
.select-body .select-header span.card {
display: inline-block;
padding: 2px 0px 2px 18px;
margin: auto 2px;
border: 1px solid #f0f0f0;
border-radius: 2px;
background-color: rgb(245, 245, 245);
pointer-events: none;
cursor: default;
}
.select-body .select-header span.card span {
display: inline-block;
margin-left: 2px;
padding: 0 5px;
color: rgba(0, 0, 0, 0.5);
cursor: pointer;
}
.select-body .select-header span.card span:hover {
color: rgba(0, 0, 0, 1);
}
/* 下拉框下拉列表 */
.select-body .select-content {}
- data.d.ts
export type OptionsType = {
label:string; // 显示的标签值
value:string; // key值
}
\