import React, { useEffect, useMemo, useState } from "react" import { Popover, Text, UnstyledButton, Input, Checkbox, useTree, getTreeExpandedState, RenderTreeNodePayload, Flex, TreeNodeData, Combobox, CloseButton, Stack, TextInput, InputProps, } from "@mantine/core" import { useDisclosure } from "@mantine/hooks" import { IconChevronUp, IconSearch } from "@tabler/icons-react" import { VirtualizedTree } from "./VirtualizedTree" import { filterTreeData_dfs, findLabel, getStateFromDataByName, } from "./libs/util" interface VirtualTreeSelectProps extends InputProps { data: TreeNodeData[] value: string onChange: (value: string) => void placeholder?: string setCurrCityCode: (code: string) => void } export function VirtualTreeSelect({ data, value, onChange, setCurrCityCode, ...props }: VirtualTreeSelectProps) { const [opened, { toggle }] = useDisclosure(false) const [searchName, setSearchName] = useState("") const filteredTreeData = useMemo(() => { return filterTreeData_dfs(data, searchName) }, [searchName, data]) const tree = useTree({ initialExpandedState: getTreeExpandedState(filteredTreeData, []), initialCheckedState: [], }) useEffect(() => { if (filteredTreeData?.length && searchName) { let state = getStateFromDataByName(filteredTreeData) tree.setExpandedState(state) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [filteredTreeData, searchName]) useEffect(() => { setCurrCityCode(tree.selectedState[0]) let label = findLabel(data, tree.selectedState[0]) onChange(label.path || "") }, [data, onChange, setCurrCityCode, tree.selectedState]) const ChevronIcon = ({ hasChildren, expanded, }: { hasChildren: boolean expanded: boolean }) => { if (hasChildren) { return ( ) } return null } const Leaf = React.memo( ({ node, expanded, elementProps, selected, tree, }: RenderTreeNodePayload) => { return ( {}} gap={12} px={16} justify={"space-between"} flex={1} align="stretch" bdrs={4} pl={(node.nodeProps?.level || 1) * 16} style={{ overflowX: "hidden", cursor: "auto", height: 36, }}> {}}> { if (!selected) { tree.select(node.value) } else { tree.select("") } }} /> {node.label} tree.toggleExpanded(node.value)}> ) } ) return ( } // style={{ width: 250 }} {...props}> {value || "请选择..."} { setSearchName(e.currentTarget.value) }} rightSection={ searchName ? ( { setSearchName("") }} style={{ display: searchName ? undefined : "none", cursor: "pointer", }} /> ) : ( ) } /> {filteredTreeData.length === 0 ? ( 暂无数据 ) : ( <> {/* 虚拟列表容器:必须有固定高度 */} } /> )} ) }