react-select私有化定制成Antd Select

27 篇文章 1 订阅

Component

import React, {useMemo} from 'react';
import {Form} from 'antd-mobile';
import ReactSelect, {components} from 'react-select';
import AsyncSelect from 'react-select/async';
import contentCanvasStyle from '../../../ContentCanvas.less';
import style from './style.less';

const CommonSelect = props => {
    const {loadOptions, isMulti, isSearchable, maxCount, attribute = {}, id, children, dragProps = {}, onEvent, ...rest} = props;
    const {options = []} = attribute;
    const required = !!attribute.required;
    const rules = required ? [{required, message: '请选择选项!'}] : [];
    return (
        <div {...dragProps} {...rest} className={`${style.common_select} ${contentCanvasStyle.form_control_item_wrap}`}>
            <Form.Item name={id} label={attribute.name} rules={rules}>
                <MySelect loadOptions={loadOptions} isMulti={isMulti} maxCount={maxCount} options={options} />
            </Form.Item>
            {children}
        </div>
    );
};

function NoOptionsMessage(props) {
    return (
        <div className={style.no_options_message}>
            <span className={style.content}>没有选项了</span>
        </div>
    );
}

function Option(props) {
    const {isSelected} = props;
    return (
        <span className={style.select_option}>
            <components.Option {...props} className={style.select_content} title="1111" />
            {!!isSelected && <span className={style.selected} />}
        </span>
    );
}

const MultiValue = props => {
    const {selectProps, index} = props;
    const {value} = selectProps || {};

    if (index == 0) {
        return (
            <components.MultiValue {...props}>
                <span className={style.ell_multi} title={props.data.label}>
                    {props.data.label}
                </span>
            </components.MultiValue>
        );
    } else if (index == 1) {
        // 超过两个值的则直接显示+N
        const overLength = value.length - 1;
        const _value = JSON.parse(JSON.stringify(value));
        _value.splice(0, 1);
        let title;
        _value.forEach(el => {
            if (title) {
                title = `${title},${el.label}`;
            } else {
                title = el.label;
            }
        });

        return (
            <components.MultiValue {...props}>
                <span title={title} className={style.ell_multi}>
                    +{overLength}
                </span>
            </components.MultiValue>
        );
    } else {
        // 其余都不显示
        return null;
    }
};

function SelectionPrompt(props) {
    return <span style={{color: '#cccccc'}}>请选择</span>;
}

function OptionLabel(props) {
    const {option} = props;
    return (
        <div className={style.ell} title={option.label}>
            {option.label}
        </div>
    );
}

const MySelect = ({onChange, value, options = [], maxCount = 1000, loadOptions, isMulti, isSearchable, isClearable}) => {
    const customStyle = useMemo(
        () => ({
            menuPortal: styles => ({...styles, zIndex: 9999}),
            menu: styles => ({...styles}),
            control: styles => ({...styles}),
            option: (styles, {data, isDisabled, isFocused, isSelected}) => {
                let _styles = {...styles};
                if (isSelected) {
                    _styles = {
                        ...styles,
                        backgroundColor: 'transparent',
                        color: 'inherit'
                    };
                }

                if (isFocused) {
                    _styles.backgroundColor = '#DEEBFF';
                }
                return _styles;
            },
            multiValue: styles => ({...styles}),
            multiValueLabel: styles => ({...styles}),
            multiValueRemove: styles => ({...styles})
        }),
        []
    );

    const selectOptions = useMemo(
        () =>
            options.map((v, i) => {
                if (v.label) {
                    return v;
                } else {
                    return {
                        label: v,
                        value: v
                    };
                }
            }),
        [options]
    );

    const handleChange = (newValue, actionMeta) => {
        if (isMulti && newValue && typeof newValue == 'string') {
            onChange(newValue.slice(0, maxCount), actionMeta);
        } else {
            onChange(newValue, actionMeta);
        }
    };

    const TheSelect = loadOptions ? AsyncSelect : ReactSelect;
    const components = {NoOptionsMessage, Option};
    if (isMulti) {
        components.MultiValue = MultiValue;
        if (value && value.length > 0) {
            components.inputValue = value.map(option => option.label).join(', '); // ??
        }
    }

    return (
        <TheSelect
            placeholder={<SelectionPrompt />}
            components={components}
            getOptionLabel={option => <OptionLabel option={option} />}
            styles={customStyle}
            className={style.origin_select}
            isMulti={isMulti}
            isSearchable={isSearchable}
            isClearable={isClearable}
            // 是否要隐藏选中的选项Options
            hideSelectedOptions={false}
            // 选中后是否要关闭菜单
            closeMenuOnSelect={false}
            menuPosition="fixed"
            options={selectOptions}
            cacheOptions
            defaultOptions
            loadOptions={loadOptions}
            onChange={handleChange}
            value={value}
            menuPortalTarget={document.body}
        />
    );
};

export default CommonSelect;



less

.origin_select {
    width: 100%;
    padding-right: 3px; // 防止选中框被遮挡
    font-size: 14px;

    & > div {
        border-color: rgb(238 232 232);
    }
}

.common_select {
    * {
        max-width: 100%;
    }

    [role='button'] {
        flex: none;
    }
}

.no_options_message {
    display: 'flex';
    justify-content: 'center';

    .content {
        text-align: center;
        color: #ccc;
        font-size: 16px;
        padding: 5px;
    }
}

.select_option {
    display: inline-flex;
    width: 100%;

    .select_content {
        width: 100%;
        text-overflow: ellipsis;
        overflow: hidden;
        word-break: break-all;
        white-space: nowrap;
    }

    .selected {
        display: inline-block;
        position: absolute;
        flex: none;
        width: 20px;
        height: 20px;
        right: 10px;
        align-self: center;
    }

    .selected::after {
        content: '';
        position: absolute;
        top: 2px;
        left: 6px;
        width: 6px;
        height: 12px;
        border: solid black;
        border-width: 0 1px 1px 0;
        transform: rotate(45deg);
    }
}

.ell {
    width: 100%;
    word-break: break-all;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ell_multi {
    max-width: 85px;
    display: inline-block;
    word-break: break-all;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值