import React, { useEffect, useMemo, useRef, useState } from "react"; import { CascaderData, CascaderBoxProps, CascaderMultipleProps, } from "../CascaderProps"; import { EmptyTip } from "../../tips"; import { CheckTree } from "../../checktree"; import { getOptions } from "../CascaderBox"; import { List } from "../../list"; import { Checkbox } from "../../checkbox"; import { CascaderLoading } from "../CascaderLoading"; import { useTranslation } from "../../i18n"; import { useConfig } from "../../util"; import { noop } from "../../_util/noop"; // CheckTree 需要数值形式多段值转为字符串值,使用该字符串拼接 export const separator = "_#$SEP$_"; export const defaultAllValue = "__CascaderAllValue__"; export function getRelations( data: CascaderData, all: CascaderMultipleProps["all"] ) { const relations = {}; const disabledNames = []; const traverse = (data: CascaderData, parentValue: string) => { (data.options || []).forEach(option => { const value = parentValue === null ? option.value : `${parentValue}${separator}${option.value}`; if (parentValue !== null) { relations[value] = parentValue; } else if (all) { relations[value] = (all as any)?.value || defaultAllValue; } if (option.disabled) { disabledNames.push(value); } if (option.child) { traverse(option.child as CascaderData, value); } }); }; traverse(data, null); return { relations, disabledNames }; } export function CascaderMultiMenuLists({ data, curValueList, onChange, onLoad, onSelect, expandTrigger, scheduleUpdate, all, tips, bottomTips, onScrollBottom = noop, }: CascaderBoxProps) { const enterTimerRef = useRef(null); const t = useTranslation(); const { classPrefix } = useConfig(); const allValue = (all as any)?.value || defaultAllValue; // 内部状态 const [internalValue, setInternalValue] = useState([]); const { relations, disabledNames } = useMemo(() => getRelations(data, all), [ data, all, ]); const lists = useMemo(() => { const lists = [data]; internalValue.forEach(value => { const option = lists[lists.length - 1].options.find( option => option.value === value ); if (option && option.child) { lists.push(option.child as CascaderData); } }); return lists; }, [data, internalValue]); useEffect(() => { if (scheduleUpdate) { scheduleUpdate(); } }, [lists, scheduleUpdate]); function handleSelect(index, value, { event, hasChild }) { const newValue = [...internalValue.slice(0, index), value]; setInternalValue(newValue); if (!hasChild) { onSelect(newValue, { event, options: getOptions(data, newValue) }); } } return ( list.join(separator))} onChange={(names, { event }) => { const value = names.map(name => name.split(separator)); onChange(value, { event, options: value.map(i => getOptions(data, i)), }); }} > {lists.map(({ options }, index) => ( onScrollBottom(internalValue.slice(0, index))} > {options ? ( <> {tips && ( {tips(internalValue.slice(0, index))} )} {index === 0 && all && ( {(all as any)?.label || t.selectAllText} )} {options.map(({ value, label, child, disabled }) => { const optionValue = [...internalValue.slice(0, index), value]; let triggerProps = {}; if (child) { triggerProps = expandTrigger === "hover" && child ? { onMouseEnter: (event: React.MouseEvent) => { enterTimerRef.current = setTimeout(() => { handleSelect(index, value, { event, hasChild: true, }); }, 150); }, onMouseLeave: () => { clearTimeout(enterTimerRef.current); }, } : { onClick: (event: React.MouseEvent) => { handleSelect(index, value, { event, hasChild: Boolean(child), }); }, }; } const itemProps = { ...triggerProps, key: [...internalValue.slice(0, index), value].join("/"), disabled, selected: false, }; if (child) { return (
{label || value}
} /> ); } return ( {label || value} ); })} {options.length === 0 && ( )} {bottomTips && ( {bottomTips(internalValue.slice(0, index))} )} ) : ( onLoad(internalValue, getOptions(data, internalValue)) } /> )}
))}
); } CascaderMultiMenuLists.displayName = "CascaderMultiMenuLists";