import React, { useEffect, useRef, useState } from 'react' import { isValid } from '@designable/shared' import cls from 'classnames' import { IconWidget, TextWidget } from '../widgets' import { usePrefix } from '../hooks' export interface ICompositePanelProps { direction?: 'left' | 'right' showNavTitle?: boolean defaultOpen?: boolean defaultPinning?: boolean defaultActiveKey?: number activeKey?: number | string onChange?: (activeKey: number | string) => void } export interface ICompositePanelItemProps { shape?: 'tab' | 'button' | 'link' title?: React.ReactNode icon?: React.ReactNode key?: number | string href?: string onClick?: (e: React.MouseEvent) => void extra?: React.ReactNode } const parseItems = ( children: React.ReactNode ): React.PropsWithChildren[] => { const items = [] React.Children.forEach(children, (child, index) => { if (child?.['type'] === CompositePanel.Item) { items.push({ key: child['key'] ?? index, ...child['props'] }) } }) return items } const findItem = ( items: React.PropsWithChildren[], key: string | number ) => { for (let index = 0; index < items.length; index++) { const item = items[index] if (key === index) return item if (key === item.key) return item } } const getDefaultKey = (children: React.ReactNode) => { const items = parseItems(children) return items?.[0].key } export const CompositePanel: React.FC & { Item?: React.FC } = (props) => { const prefix = usePrefix('composite-panel') const [activeKey, setActiveKey] = useState( props.defaultActiveKey ?? getDefaultKey(props.children) ) const activeKeyRef = useRef(null) const [pinning, setPinning] = useState(props.defaultPinning ?? false) const [visible, setVisible] = useState(props.defaultOpen ?? true) const items = parseItems(props.children) const currentItem = findItem(items, activeKey) const content = currentItem?.children activeKeyRef.current = activeKey useEffect(() => { if (isValid(props.activeKey)) { if (props.activeKey !== activeKeyRef.current) { setActiveKey(props.activeKey) } } }, [props.activeKey]) const renderContent = () => { if (!content || !visible) return return (
{currentItem.title}
{currentItem.extra}
{!pinning && ( { setPinning(!pinning) }} /> )} {pinning && ( { setPinning(!pinning) }} /> )} { setVisible(false) }} />
{content}
) } return (
{items.map((item, index) => { const takeTab = () => { if (item.href) { return {item.icon} } return ( {item.title}, placement: props.direction === 'right' ? 'left' : 'right', } } infer={item.icon} /> ) } const shape = item.shape ?? 'tab' const Comp = shape === 'link' ? 'a' : 'div' return ( { if (shape === 'tab') { if (activeKey === item.key) { setVisible(!visible) } else { setVisible(true) } if (!props?.activeKey || !props?.onChange) setActiveKey(item.key) } item.onClick?.(e) props.onChange?.(item.key) }} > {takeTab()} {props.showNavTitle && item.title ? (
{item.title}
) : null}
) })}
{renderContent()}
) } CompositePanel.Item = () => { return }