这是我参与「第五届青训营 」伴学笔记创作活动的第 12天
今天终于终于实现多选了!!虽然还很不完善~效果如下
- 遇到的困难1:我将选中的选项封装成子组件,每次选中就将数组
selectedItems[]push进当前选中的选项option,然后如何再次渲染到页面我花了点时间,我在当前子组件setselectedItems[]因为本身selectedItems[]是从父组件传过来显示在子组件上的,于是在子组件里改变选中的选项时需要调用父组件的函数改变selectedItems[]的值,然后再次传入子组件进行渲染,这个函数也需要传入子组件,才能被调用,也是运用了回调函数。 - 遇到的困难2:我将
Options?.splice(i,1);//删除已选中Option后setOptions(Options);却发现无法渲染到页面上,原因是,每次setstate的时候是根据传入的地址是否改变来判断是否要更新页面,所以要写成Options && setOptions([...Options]);这样的形式,然后就能渲染到页面上啦,点击一个就会减少一个选项 目录结构如下:
全部的代码如下:select.tsx
import { type } from "@testing-library/user-event/dist/type";
import classNames from "classnames";
import { setDefaultResultOrder } from "dns";
import React,{useEffect, useMemo, useState} from "react"
import { consumers } from "stream";
import SelectedItems from "./selectedItems";
import Option from "./selectedItems";
import "./style/select.scss"
// import {Option} from "./selectOption"
export type SelectSize = "lg" | "sm";
export type Mode = "mutiple";
export type Option = {
label: string | any;
value: string | number | any;
}
interface SelectProps{
placeholder?: string;
options?: Option[];
disabled?: true | false;
size ?: SelectSize;
mode ?: Mode
};
const Select:React.FC<SelectProps> = (props) => {
const{
placeholder,
options,
disabled,
size,
mode
}=props;
const [showOptions,setshowOptions] = useState(false); //是否打开下拉框
const [Iconstyles,setIcon] = useState("ai-downangle "+`ai-downangle-${size}`);//下拉框icon改变
const selectedItem : Option = {label:"default",value:placeholder}; //点击select框
const selectedItems : Option[] = [{label:"default",value:placeholder}];//多选
const [useselectedItem,setuseSelectedItem] = useState(selectedItem); //更换选择的条目
const [useselectedItems,setuseSelectedItems] = useState(selectedItems);
const [IsX,setIsX] = useState("") //是否有删除标志
const [Options,setOptions] = useState(options);
const [isSelect,setisSelect] = useState("true");
let disabledselect : string;
if(disabled == true){
disabledselect = 'true'
}else{
disabledselect = 'false'
}
const selectClick = () => {
if(mode && useselectedItems.length == 1){
useselectedItems.forEach(function(a){
if(a.label == "default") setuseSelectedItems([]);
})
setisSelect("false");
}
if (disabled == false){
setshowOptions(!showOptions);}
if (size != "lg"){
if(Iconstyles == "ai-upangle") setIcon("ai-downangle");
else setIcon("ai-upangle");
}else{
if(Iconstyles == "ai-upangle ai-upangle-lg"){
setIcon("ai-downangle ai-downangle-lg");
}else{
setIcon("ai-upangle ai-upangle-lg");
}
}
}
//点击选项
const optionClick = (option:Option,i:number)=>{
if(mode != "mutiple"){
setuseSelectedItem(option);
setshowOptions(!showOptions);
setIcon("ai-downangle")
setIsX(" x");
}else{
useselectedItems.push(option);
Options?.splice(i,1);//删除已选中Option
setuseSelectedItems(useselectedItems);
Options && setOptions([...Options]);
}
}
//点击删除
const Delete = ()=>{
setuseSelectedItem({label:"default",value:placeholder});
setIsX("");
}
//减少已选择
const ChangeSelectedItems = (selectedItems : Option[],addoption:Option) => {
setuseSelectedItems(selectedItems);
console.log(useselectedItems);
console.log(addoption);
if(addoption != undefined) Options && Options.push(addoption);
if (selectedItems.length == 0) setuseSelectedItems([{label:"default",value:placeholder}]);
// // Options && setOptions([...Options]);
console.log(Options);
setshowOptions(true);
}
const selectclasses = classNames("ai-select", {
[`ai-select-${disabledselect}`]: disabledselect, //是否可以使用
[`ai-select-${size}`]: size, //大小
[`ai-select-${isSelect}`]: isSelect, //上面的框里是否有字符串
});
const optionclasses = classNames("ai-option", {
[`ai-select-option-${size}`]: size, //大小
});
const optionsclasses = classNames("ai-options", {
[`ai-options-${size}`]: size, //大小
});
const mouseclasses = classNames("ai-mouse", {
[`ai-mouse-${size}`]: size, //大小
});
return (
<div id="root" >
<div className = {selectclasses} onClick={selectClick}>
{
mode &&(
<>
<SelectedItems changefunction={ChangeSelectedItems} selectedItems={useselectedItems}></SelectedItems></>
)
}
{
!mode && (<><span id = "ai-select-single">{useselectedItem.value}
<span onClick={Delete} id = "ai-select-singleX">{IsX}</span>
</span><span className={Iconstyles}></span></>)
}
</div>
{
showOptions && (
<div className={optionsclasses}>
{
Options?.map((Option,i) =>(
<div key = {Option.label} className = {optionclasses}
onClick = {()=>optionClick(Option,i)} >
<span className = {mouseclasses} >{Option.value}</span>
</div>
))
}
</div>
)
}
</div>
)
};
Select.defaultProps = {
disabled: false,
};
export default Select;
selectedItems.tsx
import { Mode, Option } from "./select";
import "./style/select.scss"
interface selectedItemsProps{
changefunction?:Function,
selectedItems?:Option[],
mode?:Mode
};
const SelectedItems:React.FC<selectedItemsProps> = (props) => {
const {
changefunction,
selectedItems,
mode
} = props;
const SelectedItems = selectedItems;
const Delete = (addoption:Option,index:number) => {
if(addoption.label != "default"){
if(SelectedItems != undefined){
SelectedItems.splice(index,1);
//console.log(SelectedItems);
}
console.log(addoption);
changefunction && changefunction(SelectedItems,addoption);
}
}
return (
<>
{
selectedItems &&
selectedItems.map((selectedItem,i) => (
<span key={selectedItem.label} onClick={()=>{Delete(selectedItem,i)}} className="ai-multiple-select">
{selectedItem.value}
<span id="ai-x"> x</span>
</span>
)
)
}
</>
)
};
export default SelectedItems;
select.scss
@import "../../theme/themes/default/index.scss";
@import "./mixin";
@import "./basic.scss";
#root{
width: 250px;
}
//基础样式
.ai-select{
font-size: $font-size-base;
display:flex;
padding:4px 8px;
border-color:$border-color ;
border:1.5px solid;
width: 200px;
//justify-content: space-between;
background-color:#ffffff00;
border-radius: $border-radius;
margin: auto;
box-shadow: $box-shadow;
line-height: $line-height-base;
}
#ai-select-single{
margin-top: auto;
margin-bottom: auto;
color: $black;
}
.ai-select-false{
height: 32px;
}
.ai-select-true{
cursor: not-allowed;
color: $menu-item-disabled-color;
// box-shadow: 0px 0px 3px 1px rgb(172, 172, 175) inset;
}
.ai-select-false{
cursor: pointer;
}
.ai-options{
width: 220px;
border: 1px solid #aec0bf;
border-radius: 5px;
margin: auto;
box-shadow:$box-shadow
}
.ai-options-sm{
width: 215px;
}
.ai-options-lg{
width: 235px;
}
.ai-option{
padding:4px 8px;
width: 200px;
font-size: 14px;
cursor: pointer;
font-size: $font-size-base;
margin: auto;
}
.ai-mouse:hover{
background-color: $gray-600;
}
.ai-downangle{
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-bottom: 8px solid transparent;
border-top: 8px solid transparent;
position: relative;
top: 12px;
}
.ai-downangle-lg{
top: 16px;
}
.ai-upangle{
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
position: relative;
bottom: -2px;
margin-right: 2px;
}
.ai-upangle-lg{
top: 8px;
}
.ai-mouse {
display: flex;
align-items: center;
height: 30px;
}
.ai-mouse-sm{
height: 25px;
}
.ai-mouse-lg{
height: 35px;
}
button{
border: #ffffff 0.5px;
background-color: #ffffff;
position: relative;
}
.ai-select-option-lg , .ai-select-lg{
@include select-size(
$line-height-sm,
$ai-select-padding-x-lg,
$ai-select-padding-y-lg,
$font-size-lg,
$border-radius-lg
)
}
.ai-select-sm{
@include select-size(
$line-height-sm,
$ai-select-padding-x-sm,
$ai-select-padding-y-sm,
$font-size-sm,
$border-radius-sm,
)
}
.ai-select-options-sm {
width: 200px;
}
.ai-select-option-sm {
width: 200px;
@include select-size(
$line-height-sm,
$ai-select-padding-x-sm,
$ai-select-padding-y-sm,
$font-size-sm,
$border-radius-sm,
)
}
#ai-x {
color:$gray-600,
}
#ai-select-singleX {
color:$gray-600;
font-size:medium;
}
.ai-multiple-select {
font-size: small;
border: $gray-600 solid 1px;
border-radius:$border-radius-sm ;
height: fit-content;
margin-top: auto;
margin-bottom: auto;
}
basic.scss
$ai-select-padding-x-lg: 16px;
$ai-select-padding-y-lg: 4px;
$ai-select-padding-x-sm: 8px;
$ai-select-padding-y-sm: 4px;
_mixin.scss
@mixin select-size($line-height, $padding-x,$padding-y, $font-size, $border-raduis) {
line-height: $line-height;
padding: $padding-y $padding-x;
font-size: $font-size;
border-radius: $border-raduis;
}
selectDemo.tsx
import Select,{Option} from "../select";
import React from "react";
const options : Option[] = [
{
label:"audi",
value:"123audi"
},
{
label:"motor",
value:"moter456"
},
{
label:"bicycle",
value:"789bicycle"
},
]
const options2 : Option[] = [
{
label:"orange",
value:"orange"
},
{
label:"banana",
value:"banana"
},
{
label:"apple",
value:"apple"
},
]
export default function App() {
return (
<div>
<Select placeholder={"choose a fruit"} options = {options2} mode="mutiple"></Select>
{/* <Select placeholder={"请选择一个对象"} options = {options}></Select>
<Select placeholder={"choose a fruit"} options = {options2} mode="mutiple"></Select>
<Select placeholder={"choose a fruit"} options = {options2} disabled></Select>
<Select placeholder={"choose a fruit"} options = {options2} size = "lg"></Select>
<Select placeholder={"choose a fruit"} options = {options2} size = "sm"></Select> */}
</div>
);
}
import Select,{Option} from "../select";
import React from "react";
const options : Option[] = [
{
label:"audi",
value:"123audi"
},
{
label:"motor",
value:"moter456"
},
{
label:"bicycle",
value:"789bicycle"
},
]
const options2 : Option[] = [
{
label:"orange",
value:"orange"
},
{
label:"banana",
value:"banana"
},
{
label:"apple",
value:"apple"
},
]
export default function App() {
return (
<div>
<Select placeholder={"choose a fruit"} options = {options2} mode="mutiple"></Select>
{/* <Select placeholder={"请选择一个对象"} options = {options}></Select>
<Select placeholder={"choose a fruit"} options = {options2} mode="mutiple"></Select>
<Select placeholder={"choose a fruit"} options = {options2} disabled></Select>
<Select placeholder={"choose a fruit"} options = {options2} size = "lg"></Select>
<Select placeholder={"choose a fruit"} options = {options2} size = "sm"></Select> */}
</div>
);
}