jsx import { jsx } from "@emotion/react" ; import React , { useCallback , useEffect , useMemo , useRef } from "react" ; import { RowBetween } from "../Flex/Row" ; import { style } from "./style" ; import { Column , ColumnItem } from "./Column" ; import useUpdate from "../Effect/useUpdate" ; export interface CascaderOption { label : React . ReactNode ; value : string | number ; children ? : CascaderOption [ ] ; } type defaultValue = ( string | number ) [ ] ; export interface WrapperProps { options : CascaderOption [ ] ; defaultValue : defaultValue ; onSelect : ( selectValue : defaultValue ) => void ; renderCancel ? : React . ReactNode | ( ( ) => React . ReactNode ) ; renderConfirm ? : React . ReactNode | ( ( ) => React . ReactNode ) ; renderTitle ? : React . ReactNode | ( ( ) => React . ReactNode ) ; onConfirm ? : ( selectValue : defaultValue ) => void ; onCancel ? : ( ) => void ; } export function Wrapper ( props ) { const { options , defaultValue , onSelect , renderTitle , renderCancel , renderConfirm } = props ; const update = useUpdate ( ) ; let selectedValueArray = useRef < defaultValue > ( Array . isArray ( defaultValue ) ? defaultValue : [ ] ) ; const currentColumns = useRef < ColumnItem [ ] [ ] > ( [ ] ) ; const { maxDepth , subOptionsRecord } = useMemo ( ( ) => { let subOptionsRecord = { } ; let maxDepth = 1 ; const traverse = ( options , currentDepth ) => { options . forEach ( ( option ) => { if ( Array . isArray ( option . children ) && option . children . length >= 1 ) { subOptionsRecord [ option . value ] = option . children ; maxDepth = Math . max ( currentDepth + 1 , maxDepth ) ; traverse ( option . children , currentDepth + 1 ) ; } } ) ; } ; traverse ( options , 1 ) ; return { maxDepth , subOptionsRecord } ; } , [ options ] ) ; const getInitColumns = useCallback ( ( options , selectedValue ) => { let initColumns = [ options . map ( ( item ) => ( { value : item . value , label : item . label } ) ) ] ; let currentSubOption = options ; for ( let i = 0 ; i < maxDepth - 1 ; i ++ ) { let currentValue = selectedValue [ i ] ; let nextIndex = currentSubOption . findIndex ( ( item ) => item . value === currentValue ) ; let nextSubOption = currentSubOption [ nextIndex === - 1 ? 0 : nextIndex ] ?. children || [ ] ; initColumns . push ( nextSubOption . map ( ( item ) => ( { value : item . value , label : item . label , } ) ) ) ; currentSubOption = nextSubOption ; } return initColumns ; } , [ ] ) ; useEffect ( ( ) => { selectedValueArray . current = Array . isArray ( defaultValue ) ? defaultValue : [ ] ; currentColumns . current = getInitColumns ( options , selectedValueArray . current ) ; update ( ) ; } , [ options , defaultValue ] ) ; const updateColumnsAndSelectedValues = useCallback ( ( value , index ) => { let usedTree = subOptionsRecord [ value ] ; let updateValueArray = selectedValueArray . current . slice ( 0 , index ) ; updateValueArray . push ( value ) ; let updateCurrentColumns = currentColumns . current . slice ( 0 , index + 1 ) ; for ( let i = 0 ; i < maxDepth - index - 1 ; i ++ ) { if ( ! usedTree ) { updateCurrentColumns . push ( [ ] ) ; continue ; } updateValueArray . push ( usedTree [ 0 ] . value ) ; updateCurrentColumns . push ( usedTree . map ( ( item ) => ( { value : item . value , label : item . label } ) ) ) ; usedTree = subOptionsRecord [ usedTree [ 0 ] . value ] ; } selectedValueArray . current = updateValueArray ; onSelect ?. ( updateValueArray ) ; currentColumns . current = updateCurrentColumns ; update ( ) ; } , [ ] ) ; const showRenderContent = ( render , defaultContent ) => { return typeof render === "function" ? render ( ) ?? defaultContent : render ?? defaultContent ; } ; return ( < div css = { [ style . container ] } > < RowBetween css = { style . header } > < div css = { style . title } > { showRenderContent ( renderTitle , "" ) } < /div> < div css = { [ style . btn , style . btnCancel ] } onClick = { ( ) => { props . onCancel ?. ( ) ; } } > { showRenderContent ( renderCancel , "取消" ) } < /div> < div css = { [ style . btn , style . btnConfirm ] } onClick = { ( ) => { props . onConfirm ?. ( selectedValueArray . current ) ; } } > { showRenderContent ( renderConfirm , "确认" ) } < /div> < /RowBetween> < RowBetween > { currentColumns . current . map ( ( column , index ) => { return ( < Column updateColumns = { ( value ) => { updateColumnsAndSelectedValues ( value , index ) ; } } columnOptions = { column } defaultValue = { selectedValueArray . current [ index ] } key = { index } / > ) ; } ) } < /RowBetween> < /div>); }