'use client'; import React, { useMemo, useRef, useState } from 'react'; import { BusterResizeableGridRow } from './interfaces'; import { BusterResizeColumns } from './BusterResizeColumns'; import { BusterNewItemDropzone } from './_BusterBusterNewItemDropzone'; import { MIN_ROW_HEIGHT, TOP_SASH_ID, NEW_ROW_ID, MAX_ROW_HEIGHT } from './config'; import clamp from 'lodash/clamp'; import { useDebounceFn, useMemoizedFn, useUpdateLayoutEffect } from 'ahooks'; import { useDroppable } from '@dnd-kit/core'; import { cn } from '@/lib/classMerge'; export const BusterResizeRows: React.FC<{ rows: BusterResizeableGridRow[]; className: string; allowEdit?: boolean; onRowLayoutChange: (rows: BusterResizeableGridRow[]) => void; }> = ({ allowEdit = true, rows, className, onRowLayoutChange }) => { const ref = useRef(null); const [isDraggingResizeId, setIsDraggingResizeId] = useState(null); const [sizes, setSizes] = useState(rows.map((r) => r.rowHeight ?? MIN_ROW_HEIGHT)); const { run: handleRowLayoutChangeDebounced } = useDebounceFn( useMemoizedFn((sizes: number[]) => { const newRows = rows.map((r, index) => ({ ...r, rowHeight: sizes[index] })); onRowLayoutChange(newRows); }), { wait: 375 } ); const handleResize = useMemoizedFn((index: number, size: number) => { const newSizes = [...sizes]; newSizes[index] = size; setSizes(newSizes); handleRowLayoutChangeDebounced(newSizes); }); const onRowLayoutChangePreflight = useMemoizedFn((columnSizes: number[], rowId: string) => { const newRows: BusterResizeableGridRow[] = rows.map((r) => { if (r.id === rowId) { return { ...r, columnSizes }; } return r; }); onRowLayoutChange(newRows); }); useUpdateLayoutEffect(() => { setSizes(rows.map((r) => r.rowHeight ?? MIN_ROW_HEIGHT)); }, [rows.length]); return (
{rows.map((row, index) => (
))} {allowEdit && }
); }; const ResizeRowHandle: React.FC<{ id: string; index?: number; sizes: number[]; setIsDraggingResizeId: (index: number | null) => void; onResize: (index: number, size: number) => void; allowEdit: boolean; active: boolean; top?: boolean; //if true we will not use dragging, just dropzone hideDropzone?: boolean; }> = React.memo( ({ hideDropzone, top, id, active, allowEdit, setIsDraggingResizeId, index, sizes, onResize }) => { const { setNodeRef, isOver, over } = useDroppable({ id: `${NEW_ROW_ID}_${id}}`, disabled: !allowEdit, data: { id } }); const showDropzone = !!over?.id && !hideDropzone; const isDropzoneActive = showDropzone && isOver; const handler = useMemoizedFn((mouseDownEvent: React.MouseEvent) => { const startPosition = mouseDownEvent.pageY; const style = document.createElement('style'); style.innerHTML = `* { cursor: row-resize; }`; document.head.appendChild(style); setIsDraggingResizeId(index!); function onMouseMove(mouseMoveEvent: MouseEvent) { const newSize = sizes[index!] + (mouseMoveEvent.pageY - startPosition); const clampedSize = clamp(newSize, MIN_ROW_HEIGHT, MAX_ROW_HEIGHT); onResize(index!, clampedSize); } function onMouseUp() { document.body.removeEventListener('mousemove', onMouseMove); style.remove(); setIsDraggingResizeId(null); } document.body.addEventListener('mousemove', onMouseMove); document.body.addEventListener('mouseup', onMouseUp, { once: true }); }); const onMouseDown = top ? undefined : handler; const memoizedStyle = useMemo(() => { return { zIndex: 1, bottom: !top ? -4 : -4, transform: !top ? 'translateY(100%)' : 'translateY(100%)' }; }, [top]); const showActive = (active || isDropzoneActive) && allowEdit; return (
); } ); ResizeRowHandle.displayName = 'ResizeRowHandle';