select选择器组件(5)|青训营日记

112 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 12天

今天终于终于实现多选了!!虽然还很不完善~效果如下 image.png

  • 遇到的困难1:我将选中的选项封装成子组件,每次选中就将数组selectedItems[]push进当前选中的选项option,然后如何再次渲染到页面我花了点时间,我在当前子组件setselectedItems[]因为本身selectedItems[]是从父组件传过来显示在子组件上的,于是在子组件里改变选中的选项时需要调用父组件的函数改变selectedItems[]的值,然后再次传入子组件进行渲染,这个函数也需要传入子组件,才能被调用,也是运用了回调函数。
  • 遇到的困难2:我将Options?.splice(i,1);//删除已选中OptionsetOptions(Options);却发现无法渲染到页面上,原因是,每次setstate的时候是根据传入的地址是否改变来判断是否要更新页面,所以要写成 Options && setOptions([...Options]);这样的形式,然后就能渲染到页面上啦,点击一个就会减少一个选项 目录结构如下:

image.png

全部的代码如下: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>
    );
}