import React from 'react'; import {debounce} from 'lodash'; import type {SplitProps} from 'react-split'; import SplitPaneLib from 'react-split'; import {cn} from '../../utils/cn'; import {useSetting} from '../../utils/hooks/useSetting'; import './SplitPane.scss'; const b = cn('kv-split'); interface SplitPaneProps { children: React.ReactNode; defaultSizePaneKey: string; direction?: SplitProps['direction']; sizes?: SplitProps['sizes']; defaultSizes?: SplitProps['sizes']; initialSizes?: SplitProps['sizes']; collapsedSizes?: SplitProps['sizes']; minSize?: number[]; gutterSize?: SplitProps['gutterSize']; triggerCollapse?: boolean; triggerExpand?: boolean; onSplitStartDragAdditional?: VoidFunction; onSplitDragAdditional?: VoidFunction; } const minSizeDefaultInner = [0, 100]; const sizesDefaultInner = [50, 50]; const SAVE_DEBOUNCE_MS = 200; const getGutterStyle: NonNullable = (dimension, gutterSize) => ({ [dimension]: `${gutterSize}px`, display: gutterSize === 0 ? 'none' : '', }); function SplitPane(props: SplitPaneProps) { const [innerSizes, setInnerSizes] = React.useState(); const { collapsedSizes, triggerCollapse, triggerExpand, defaultSizes: defaultSizesProp, initialSizes, sizes: sizesProp, gutterSize = 8, } = props; const [savedSizesString, setSavedSizesString] = useSetting( props.defaultSizePaneKey, ); const saveSizesStringDebounced = React.useMemo(() => { return debounce((value: string) => setSavedSizesString(value), SAVE_DEBOUNCE_MS); }, [setSavedSizesString]); React.useEffect(() => { return () => { saveSizesStringDebounced.cancel(); }; }, [saveSizesStringDebounced]); const defaultSizePane = React.useMemo(() => { if (initialSizes) { return initialSizes; } const fallbackSizes = defaultSizesProp || sizesDefaultInner; if (!savedSizesString) { return fallbackSizes; } const parts = savedSizesString.split(','); const parsedSizes = parts.map((part) => { const trimmed = part.trim(); if (!trimmed) { return Number.NaN; } return Number(trimmed); }); const expectedLength = fallbackSizes.length; const isValid = parsedSizes.length === expectedLength && parsedSizes.every((v) => Number.isFinite(v)); return isValid ? parsedSizes : fallbackSizes; }, [defaultSizesProp, initialSizes, savedSizesString]); const setDefaultSizePane = React.useCallback( (sizes: number[]) => { saveSizesStringDebounced(sizes.join(',')); }, [saveSizesStringDebounced], ); const onDragHandler = (sizes: number[]) => { const {onSplitDragAdditional} = props; if (onSplitDragAdditional) { onSplitDragAdditional(); } setDefaultSizePane(sizes); }; const onDragStartHandler = () => { const {onSplitStartDragAdditional} = props; if (onSplitStartDragAdditional) { onSplitStartDragAdditional(); } setInnerSizes(undefined); }; React.useEffect(() => { if (triggerCollapse) { const newSizes = collapsedSizes || minSizeDefaultInner; setDefaultSizePane(newSizes); setInnerSizes(newSizes); } }, [collapsedSizes, triggerCollapse, setDefaultSizePane]); React.useEffect(() => { const newSizes = defaultSizesProp || sizesDefaultInner; if (triggerExpand) { setDefaultSizePane(newSizes); setInnerSizes(newSizes); } }, [defaultSizesProp, triggerExpand, setDefaultSizePane]); return ( {props.children} ); } export default SplitPane;