/* eslint-disable react/no-array-index-key */ import React, { useState } from 'react'; import classNames from 'classnames'; import ResizeObserver from 'rc-resize-observer'; import useEvent from 'rc-util/lib/hooks/useEvent'; import type { GetProp } from '../_util/type'; import { devUseWarning } from '../_util/warning'; import { ConfigContext } from '../config-provider'; import useCSSVarCls from '../config-provider/hooks/useCSSVarCls'; import useItems from './hooks/useItems'; import useResizable from './hooks/useResizable'; import useResize from './hooks/useResize'; import useSizes from './hooks/useSizes'; import type { SplitterProps } from './interface'; import { InternalPanel } from './Panel'; import SplitBar from './SplitBar'; import useStyle from './style'; const Splitter: React.FC> = (props) => { const { prefixCls: customizePrefixCls, className, style, layout = 'horizontal', children, rootClassName, onResizeStart, onResize, onResizeEnd, } = props; const { getPrefixCls, direction, splitter } = React.useContext(ConfigContext); const prefixCls = getPrefixCls('splitter', customizePrefixCls); const rootCls = useCSSVarCls(prefixCls); const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls); // ======================== Direct ======================== const isVertical = layout === 'vertical'; const isRTL = direction === 'rtl'; const reverse = !isVertical && isRTL; // ====================== Items Data ====================== const items = useItems(children); // >>> Warning for uncontrolled if (process.env.NODE_ENV !== 'production') { const warning = devUseWarning('Splitter'); let existSize = false; let existUndefinedSize = false; items.forEach((item) => { if (item.size !== undefined) { existSize = true; } else { existUndefinedSize = true; } }); if (existSize && existUndefinedSize && !onResize) { warning( false, 'usage', 'When part of `Splitter.Panel` has `size`, `onResize` is required or change `size` to `defaultSize`.', ); } } // ====================== Container ======================= const [containerSize, setContainerSize] = useState(); const onContainerResize: GetProp = (size) => { const { offsetWidth, offsetHeight } = size; const containerSize = isVertical ? offsetHeight : offsetWidth; // Skip when container has no size, Such as nested in a hidden tab panel // to fix: https://github.com/ant-design/ant-design/issues/51106 if (containerSize === 0) { return; } setContainerSize(containerSize); }; // ========================= Size ========================= const [panelSizes, itemPxSizes, itemPtgSizes, itemPtgMinSizes, itemPtgMaxSizes, updateSizes] = useSizes(items, containerSize); // ====================== Resizable ======================= const resizableInfos = useResizable(items, itemPxSizes); const [onOffsetStart, onOffsetUpdate, onOffsetEnd, onCollapse, movingIndex] = useResize( items, resizableInfos, itemPtgSizes, containerSize, updateSizes, ); // ======================== Events ======================== const onInternalResizeStart = useEvent((index: number) => { onOffsetStart(index); onResizeStart?.(itemPxSizes); }); const onInternalResizeUpdate = useEvent((index: number, offset: number) => { const nextSizes = onOffsetUpdate(index, offset); onResize?.(nextSizes); }); const onInternalResizeEnd = useEvent(() => { onOffsetEnd(); onResizeEnd?.(itemPxSizes); }); const onInternalCollapse = useEvent((index: number, type: 'start' | 'end') => { const nextSizes = onCollapse(index, type); onResize?.(nextSizes); onResizeEnd?.(nextSizes); }); // ======================== Styles ======================== const containerClassName = classNames( prefixCls, className, `${prefixCls}-${layout}`, { [`${prefixCls}-rtl`]: isRTL, }, rootClassName, splitter?.className, cssVarCls, rootCls, hashId, ); // ======================== Render ======================== const maskCls = `${prefixCls}-mask`; const stackSizes = React.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]); const mergedStyle: React.CSSProperties = { ...splitter?.style, ...style }; return wrapCSSVar(
{items.map((item, idx) => { // Panel const panel = ; // Split Bar let splitBar: React.ReactElement | null = null; const resizableInfo = resizableInfos[idx]; if (resizableInfo) { const ariaMinStart = (stackSizes[idx - 1] || 0) + itemPtgMinSizes[idx]; const ariaMinEnd = (stackSizes[idx + 1] || 100) - itemPtgMaxSizes[idx + 1]; const ariaMaxStart = (stackSizes[idx - 1] || 0) + itemPtgMaxSizes[idx]; const ariaMaxEnd = (stackSizes[idx + 1] || 100) - itemPtgMinSizes[idx + 1]; splitBar = ( { let offset = isVertical ? offsetY : offsetX; if (reverse) { offset = -offset; } onInternalResizeUpdate(index, offset); }} onOffsetEnd={onInternalResizeEnd} onCollapse={onInternalCollapse} /> ); } return ( {panel} {splitBar} ); })} {/* Fake mask for cursor */} {typeof movingIndex === 'number' && (
)}
, ); }; if (process.env.NODE_ENV !== 'production') { Splitter.displayName = 'Splitter'; } export default Splitter;