import { Func } from '@fuselab/ui-shared/lib/typeHelpers'; import * as React from 'react'; import * as _ from 'underscore'; /* tslint:enable:no-use-before-declare */ import classNames from './splittable.classNames'; export interface SplittableAttributes { orientation: 'horizontal' | 'vertical'; parts: Func[]; } export interface SplittableActions { onLayout(partitions: number[]); } export type SplittableProps = SplittableAttributes & SplittableActions; export function layoutStack(max: number, parts: number[], index: number, delta: number): number[] { const total = parts.reduce((s, c) => s + c, 0); const unassignedCount = parts.reduce((s, c) => c ? s : s + 1, 0); const unassignedAmount = Math.round(max - total) / unassignedCount; const cur = parts.map(x => x || unassignedAmount); if (delta) { const prev = cur[index]; const next = cur[index + 1]; const effectiveDelta = delta > 0 ? Math.min(next, delta) : Math.max(-prev, delta); cur.splice(index, 2, prev + effectiveDelta, next - effectiveDelta); } return cur; } export const Splittable = (props: SplittableProps) => { const [partitions, setState] = React.useState([]); const classes = classNames(props.orientation); const getSize = (index: number) => partitions[index] ? `${partitions[index]}px` : 'auto'; const getStyle = (index: number) => ({ [props.orientation === 'vertical' ? 'width' : 'height']: getSize(index) }); const rootRef = React.createRef(); const dividerRefs = _.range(0, props.parts.length).map(x => React.createRef()); const onDragOver = (e: React.DragEvent) => { e.preventDefault(); }; // render a part with part container const renderPartOnly = (part: Func, index: number) => ([(
{part()}
)]); React.useEffect(() => { const root = rootRef.current; if (!partitions.length) { const parts = _.range(0, props.parts.length).map(x => 0); setState(layoutStack(root.clientWidth, parts, 0, 0)); } }); // render a part with optional splitter const renderPart = (part: Func, index: number) => { let prevPosition = 0; if (index === 0) { return renderPartOnly(part, index); } const onStart = (e: React.DragEvent) => { e.dataTransfer.setData('text/plain', `${partitions[index - 1]}`); prevPosition = props.orientation === 'vertical' ? e.screenX : e.screenY; }; const onDrag = (e: React.DragEvent) => { const cur = props.orientation === 'vertical' ? e.screenX : e.screenY; const delta = cur - prevPosition; const root = rootRef.current; if (delta !== 0) { prevPosition = cur; const next = layoutStack(root.clientWidth, partitions, index - 1, delta); setState(next); } }; return [(
), ...renderPartOnly(part, index) ]; }; return (
{props.parts.map(renderPart)}
); };