/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-nested-ternary */ import { Emitter } from '@difizen/mana-common'; import clsx from 'classnames'; import * as React from 'react'; import { defaultPrefixCls } from '../../constant'; import type { IResizeHandleDelegate } from '../../resize/index'; import { ResizeFlexMode } from '../../resize/index'; import { Layout } from '../layout'; import { SplitPanelContext } from './context'; import { Pane } from './pane'; import './styles/index.less'; export interface SplitPanelProps { prefixCls?: string; className?: string; direction?: Layout.direction; id: string; // setAbsoluteSize 时保证相邻节点总宽度不变 resizeKeep?: boolean; // used in accordion // dynamicTarget?: boolean; // 控制使用传入尺寸之和作为总尺寸或使用dom尺寸 useDomSize?: boolean; children?: React.ReactNode | React.ReactNode[]; onResize?: (slot?: string) => void; onSizeChanged?: (size: { slot: string; size: number }[]) => void; } type ChildType = React.ReactNode & { props: Record; }; const getProp = (child: ChildType, prop: string) => { return child && child.props && child.props[prop]; }; export type SplitPanel = React.ForwardRefExoticComponent< SplitPanelProps & React.RefAttributes > & { Pane: typeof Pane }; export const SplitPanel = React.forwardRef(function SplitPanel( { prefixCls = defaultPrefixCls, id, className, children = [], direction = 'left-to-right', resizeKeep = true, useDomSize, onResize, onSizeChanged, ...restProps }: SplitPanelProps, ref: React.ForwardedRef, ) { const baseCls = `${prefixCls}-splitpanel`; const ResizeHandle = Layout.getResizeHandle(direction); // convert children to list const childList = React.Children.toArray(children) as ChildType[]; const totalFlexNum = childList.reduce( (accumulator, item) => accumulator + (getProp(item, 'flex') !== undefined ? item.props.flex : 1), 0, ); const elements: React.ReactNode[] = []; const resizeDelegates = React.useRef([]); // const eventBus = useInjectable(IEventBus); const resizeEventEmmiter = new Emitter<{ slot: string }>(); // const rootRef = React.useRef(); // // const splitPanelService = useInjectable(SplitPanelManager).getService(id); // splitPanelService.panels = []; const maxLockState = React.useRef(childList.map(() => false)); const hideState = React.useRef(childList.map(() => false)); const resizeLockState = React.useRef( maxLockState.current.slice(0, childList.length - 1), ); const [locks, setLocks] = React.useState(resizeLockState.current); const [hides, setHides] = React.useState(hideState.current); const [maxLocks, setMaxLocks] = React.useState(maxLockState.current); // 获取setSize的handle,对于最右端或最底部的视图,取上一个位置的handle const setSizeHandle = (index: number) => { return (size?: number, isLatter?: boolean) => { const targetIndex = isLatter ? index - 1 : index; const delegete = resizeDelegates.current[targetIndex]; if (delegete) { delegete.setAbsoluteSize( size !== undefined ? size : getProp(childList[index], 'defaultSize'), isLatter, resizeKeep, ); } }; }; const setRelativeSizeHandle = (index: number) => { return (prev: number, next: number, isLatter?: boolean) => { const targetIndex = isLatter ? index - 1 : index; const delegete = resizeDelegates.current[targetIndex]; if (delegete) { delegete.setRelativeSize(prev, next); } }; }; const getSizeHandle = (index: number) => { return (isLatter?: boolean) => { const targetIndex = isLatter ? index - 1 : index; const delegete = resizeDelegates.current[targetIndex]; if (delegete) { return delegete.getAbsoluteSize(isLatter); } return 0; }; }; const getRelativeSizeHandle = (index: number) => { return (isLatter?: boolean) => { const targetIndex = isLatter ? index - 1 : index; const delegete = resizeDelegates.current[targetIndex]; if (delegete) { return delegete.getRelativeSize(); } return [0, 0]; }; }; const lockResizeHandle = (index: number) => { return (lock: boolean | undefined, isLatter?: boolean) => { const targetIndex = isLatter ? index - 1 : index; const newResizeState = resizeLockState.current.map((state, idx) => // eslint-disable-next-line no-nested-ternary idx === targetIndex ? (lock !== undefined ? lock : !state) : state, ); resizeLockState.current = newResizeState; setLocks(newResizeState); }; }; const setMaxSizeHandle = (index: number) => { return (lock: boolean | undefined, _isLatter?: boolean) => { const newMaxState = maxLockState.current.map((state, idx) => idx === index ? (lock !== undefined ? lock : !state) : state, ); maxLockState.current = newMaxState; setMaxLocks(newMaxState); }; }; const fireResizeEvent = (slot?: string) => { if (onResize) { onResize(slot); } if (onSizeChanged) { const sizeArr = childList.map((child, idx) => { const size = getSizeHandle(idx)(idx === childList.length - 1); return { slot: getProp(child, 'slot') as string, size }; }); onSizeChanged(sizeArr); } if (slot) { resizeEventEmmiter.fire({ slot }); } }; const hidePanelHandle = (index: number) => { return (show?: boolean) => { const newHideState = hideState.current.map((state, idx) => idx === index ? (show !== undefined ? !show : !state) : state, ); hideState.current = newHideState; const location = getProp(childList[index], 'slot') || getProp(childList[index], 'id'); if (location) { fireResizeEvent(location); } setHides(newHideState); }; }; childList.forEach((element, index) => { if (index !== 0) { const targetElement = index === 1 ? childList[index - 1] : childList[index]; let flexMode: ResizeFlexMode | undefined; if (getProp(element, 'flexGrow')) { flexMode = ResizeFlexMode.Prev; } else if (getProp(childList[index - 1], 'flexGrow')) { flexMode = ResizeFlexMode.Next; } elements.push( { const prevLocation = getProp(childList[index - 1], 'slot') || getProp(childList[index - 1], 'id'); const nextLocation = getProp(childList[index], 'slot') || getProp(childList[index], 'id'); fireResizeEvent(prevLocation!); fireResizeEvent(nextLocation!); }} noColor={true} // findNextElement={ // dynamicTarget // ? (resizeDirection: boolean) => // splitPanelService.getFirstResizablePanel(index - 1, resizeDirection) // : undefined // } // findPrevElement={ // dynamicTarget // ? (resizeDirection: boolean) => // splitPanelService.getFirstResizablePanel(index - 1, resizeDirection, true) // : undefined // } key={`split-handle-${index}`} delegate={(delegate) => { resizeDelegates.current.push(delegate); }} flexMode={flexMode} direction={direction} />, ); } elements.push(
{ // if (ele && splitPanelService.panels.indexOf(ele) === -1) { // splitPanelService.panels.push(ele); // } // }} id={getProp(element, 'id') /* @deprecated: query by data-view-id */} className={getProp(element, 'className')} style={{ // 手风琴场景,固定尺寸和flex尺寸混合布局;需要在resize flex模式下禁用 ...(element.props.flex && !element.props.savedSize && !childList.find((item) => item!.props.flexGrow) ? { flex: element.props.flex } : { [Layout.getSizeProperty(direction)]: getElementSize(element) }), // 相对尺寸带来的问题,必须限制最小最大尺寸 [Layout.getMinSizeProperty(direction)]: getProp(element, 'minSize') ? `${element.props.minSize}px` : '-1px', [Layout.getMaxSizeProperty(direction)]: maxLocks[index] && getProp(element, 'minSize') ? `${element.props.minSize}px` : 'unset', // resize flex模式下应用flexGrow ...(getProp(element, 'flexGrow') !== undefined ? { flexGrow: element.props.flexGrow } : {}), display: hides[index] ? 'none' : 'block', }} > {element}
, ); }); function getElementSize(element: any) { if (element.props.savedSize) { return `${element.props.savedSize}px`; } if (element.props.defaultSize !== undefined) { return `${element.props.defaultSize}px`; } if (element.props.flex) { return `${(element.props.flex / totalFlexNum) * 100}%`; } return `${(1 / totalFlexNum) * 100}%`; } React.useEffect(() => { // if (rootRef.current) { // splitPanelService.rootNode = rootRef.current; // } const disposer = resizeEventEmmiter.event((e) => { if (e.slot === id) { childList.forEach((c) => { fireResizeEvent(getProp(c, 'slot') || getProp(c, 'id')); }); } }); return () => { disposer.dispose(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return (
{elements}
); }) as SplitPanel; SplitPanel.Pane = Pane;