import { type ForwardedRef, forwardRef, Fragment, useCallback, useMemo, useState, } from 'react'; import styles from './styles/rt-splitter.module.css'; import cn from 'classnames'; import type { RtSplitterProps } from './rt.splitter.types'; import ResizeObserver, { type OnResize } from 'rc-resize-observer'; import useItems from './hooks/useItems'; import useSizes from './hooks/useSizes'; import useResizable from './hooks/useResizable'; import useResize from './hooks/useResize'; import useEvent from 'rc-util/lib/hooks/useEvent'; import { useRtBaseProps } from '@dpa/react-hoc'; import { RtSplitterPanel } from './components/rt-splitter-panel/rt-splitter-panel'; import { RtSplitterBar } from './components/rt-splitter-bar'; export const RtSplitter = forwardRef( (props: RtSplitterProps, ref: ForwardedRef) => { const { layout = 'horizontal', children, onResizeStart, onResize, onResizeEnd, dragLinePaddings = [0, 0], } = props; const [isRTL, setIsRTL] = useState(false); const handleRef = useCallback( (node: HTMLDivElement | null) => { if (typeof ref === 'function') { ref(node); } else if (ref) { ref.current = node; } if (node) { setIsRTL(node.getAttribute('dir') === 'rtl'); } }, [ref] ); const { ...baseProps } = useRtBaseProps(props, '0.2.2'); const isVertical = layout === 'vertical'; const reverse = !isVertical && isRTL; const items = useItems(children); const [isResizeStarted, setIsResizeStarted] = useState(false); const [containerSize, setContainerSize] = useState(); const onContainerResize: OnResize = (size) => { const { offsetWidth, offsetHeight } = size; const containerSize = isVertical ? offsetHeight : offsetWidth; if (containerSize === 0) { return; } setContainerSize(containerSize); }; const [ panelSizes, itemPxSizes, itemPtgSizes, itemPtgMinSizes, itemPtgMaxSizes, updateSizes, ] = useSizes(items, containerSize); const resizableInfos = useResizable(items, itemPxSizes, isRTL); const [onOffsetStart, onOffsetUpdate, onOffsetEnd, onCollapse] = useResize( items, resizableInfos, itemPtgSizes, containerSize, updateSizes, isRTL ); const onInternalResizeStart = useEvent((index: number) => { setIsResizeStarted(true); onOffsetStart(index); onResizeStart?.(itemPxSizes); }); const onInternalResizeUpdate = useEvent((index: number, offset: number) => { const nextSizes = onOffsetUpdate(index, offset); onResize?.(nextSizes); }); const onInternalResizeEnd = useEvent(() => { setIsResizeStarted(false); onOffsetEnd(); onResizeEnd?.(itemPxSizes); }); const onInternalCollapse = useEvent( (index: number, type: 'start' | 'end') => { const nextSizes = onCollapse(index, type); onResize?.(nextSizes); onResizeEnd?.(nextSizes); } ); const containerClassName = cn( styles['rt-splitter'], styles[`rt-splitter-${layout}`], { [styles[`rt-splitter-rtl-${layout}`]]: isRTL, }, { [styles['rt-splitter-resizing']]: isResizeStarted } ); const stackSizes = useMemo(() => { const mergedSizes: number[] = []; let stack = 0; for (let i = 0; i < items.length; i += 1) { stack += itemPtgSizes[i]; mergedSizes.push(stack); } return mergedSizes; }, [itemPtgSizes]); return (
{items.map((item, idx) => { const panel = ; let splitBar: React.ReactElement | null = null; const resizableInfo = resizableInfos[idx]; if (resizableInfo) { const areaMinStart = (stackSizes[idx - 1] || 0) + itemPtgMinSizes[idx]; const areaMinEnd = (stackSizes[idx + 1] || 100) - itemPtgMaxSizes[idx + 1]; const areaMaxStart = (stackSizes[idx - 1] || 0) + itemPtgMaxSizes[idx]; const areaMaxEnd = (stackSizes[idx + 1] || 100) - itemPtgMinSizes[idx + 1]; splitBar = ( { let offset = isVertical ? offsetY : offsetX; if (reverse) { offset = -offset; } onInternalResizeUpdate(index, offset); }} onOffsetEnd={onInternalResizeEnd} onCollapse={onInternalCollapse} containerSize={containerSize || 0} endSlot={item.endSlot} startSlot={item.startSlot} dragLinePaddings={dragLinePaddings} /> ); } return ( {panel} {splitBar} ); })}
); } ); RtSplitter.displayName = 'RtSplitter';