From 31b3116e52a10fedc12e76af71e284a5f753abc3 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Fri, 8 Aug 2025 10:07:48 -0600 Subject: [PATCH] minor update to dashboard grid and memoizing --- .../ui/grid/BusterResizeColumns.tsx | 19 +-- .../components/ui/grid/BusterResizeRows.tsx | 27 ++-- .../ui/grid/BusterResizeableGrid.tsx | 143 ++++++++++-------- .../ui/layouts/PreventNavigation.tsx | 76 +++++----- 4 files changed, 131 insertions(+), 134 deletions(-) diff --git a/apps/web/src/components/ui/grid/BusterResizeColumns.tsx b/apps/web/src/components/ui/grid/BusterResizeColumns.tsx index 9994e0252..84fb2ba2f 100644 --- a/apps/web/src/components/ui/grid/BusterResizeColumns.tsx +++ b/apps/web/src/components/ui/grid/BusterResizeColumns.tsx @@ -2,7 +2,8 @@ import { SortableContext, useSortable } from '@dnd-kit/sortable'; import React, { useEffect, useMemo, useState } from 'react'; -import { useMemoizedFn, useMount, useMouse } from '@/hooks'; +import { useMouse } from '@/hooks/useMouse'; +import { useMemoizedFn } from '@/hooks/useMemoizedFn'; import { cn } from '@/lib/classMerge'; import { BusterSortableItemDragContainer } from './_BusterSortableItemDragContainer'; import type { ResizeableGridDragItem } from './interfaces'; @@ -33,8 +34,8 @@ export const BusterResizeColumns: React.FC = ({ const mouse = useMouse({ moveThrottleMs: 50, disabled: readOnly || !over }); const [stagedLayoutColumns, setStagedLayoutColumns] = useState(() => columnSizes || []); - const canResize = useMemo(() => items.length > 1 && items.length < 4, [items.length]); - const isDropzoneActives = useMemo(() => !!over?.id && canResize, [over?.id, canResize]); + const canResize = items.length > 1 && items.length < 4; + const isDropzoneActives = !!over?.id && canResize; const insertPosition = useMemoizedFn((itemId: string, _index: number, mouseLeft: number) => { const movedIndex = _index; @@ -90,14 +91,6 @@ export const BusterResizeColumns: React.FC = ({ onRowLayoutChange(newColumnSpans, rowId); }); - const onDragEnd = useMemoizedFn(() => { - //Optional: Add any additional drag end logic - }); - - const onDragStart = useMemoizedFn(() => { - // Optional: Add any additional drag start logic - }); - useEffect(() => { if (!isEqual(stagedLayoutColumns, columnSizes)) { setStagedLayoutColumns(columnSizes || []); @@ -113,8 +106,8 @@ export const BusterResizeColumns: React.FC = ({ {items.map((item, index) => (
(rows.map((r) => r.rowHeight ?? MIN_ROW_HEIGHT)); const { run: handleRowLayoutChangeDebounced } = useDebounceFn( - useMemoizedFn((sizes: number[]) => { + (sizes: number[]) => { const newRows = rows.map((r, index) => ({ ...r, rowHeight: sizes[index] })); onRowLayoutChange(newRows); - }), + }, { wait: 375 } ); @@ -124,7 +125,7 @@ const ResizeRowHandle: React.FC<{ const showDropzone = !!over?.id && !hideDropzone; const isDropzoneActive = showDropzone && isOver; - const handler = useMemoizedFn((mouseDownEvent: React.MouseEvent) => { + const handler = (mouseDownEvent: React.MouseEvent) => { if (typeof index !== 'number') return; const startPosition = mouseDownEvent.pageY; @@ -147,17 +148,15 @@ const ResizeRowHandle: React.FC<{ 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 style: React.CSSProperties = { + zIndex: 1, + bottom: !top ? -4 : -4, + transform: !top ? 'translateY(100%)' : 'translateY(100%)' + }; const showActive = (active || isDropzoneActive) && !readOnly; @@ -172,7 +171,7 @@ const ResizeRowHandle: React.FC<{ 'h-1 w-full rounded-sm transition-colors duration-200 ease-in-out select-none', !top && 'dragger absolute' )} - style={memoizedStyle} + style={style} onMouseDown={onMouseDown} />
{ - if (activeId && rows.some((row) => row.id === activeId)) { - return closestCenter({ - ...args, - droppableContainers: args.droppableContainers.filter((container) => - rows.map((v) => v.id).includes(container.id as string) - ) - }); - } - - // Start by finding any intersecting droppable - const pointerIntersections = pointerWithin(args); - const intersections = - pointerIntersections.length > 0 - ? // If there are droppables intersecting with the pointer, return those - pointerIntersections - : []; //rectIntersection(args); - - let overId = getFirstCollision(intersections, 'id'); - - if (overId != null) { - if (rows.some((row) => row.id === overId)) { - const containerItems = rows.find((row) => row.id === overId)?.items || []; - - // If a container is matched and it contains items (columns 'A', 'B', 'C') - if (containerItems.length > 0) { - // Return the closest droppable within that container - overId = closestCenter({ - ...args, - droppableContainers: args.droppableContainers.filter( - (container) => - container.id !== overId && - containerItems.map((v) => v.id).includes(container.id as string) - ) - })[0]?.id; - } + const collisionDetectionStrategy: CollisionDetection = useCallback( + (args) => { + if (activeId && rows.some((row) => row.id === activeId)) { + return closestCenter({ + ...args, + droppableContainers: args.droppableContainers.filter((container) => + rows.map((v) => v.id).includes(container.id as string) + ) + }); } - lastOverId.current = overId as string; + // Start by finding any intersecting droppable + const pointerIntersections = pointerWithin(args); + const intersections = + pointerIntersections.length > 0 + ? // If there are droppables intersecting with the pointer, return those + pointerIntersections + : []; //rectIntersection(args); - return [{ id: overId }]; - } + let overId = getFirstCollision(intersections, 'id'); - // When a draggable item moves to a new container, the layout may shift - // and the `overId` may become `null`. We manually set the cached `lastOverId` - // to the id of the draggable item that was moved to the new container, otherwise - // the previous `overId` will be returned which can cause items to incorrectly shift positions - if (recentlyMovedToNewContainer.current) { - lastOverId.current = activeId; - } + if (overId != null) { + if (rows.some((row) => row.id === overId)) { + const containerItems = rows.find((row) => row.id === overId)?.items || []; - // If no droppable is matched, return the last match - return lastOverId.current ? [{ id: lastOverId.current }] : []; - }); + // If a container is matched and it contains items (columns 'A', 'B', 'C') + if (containerItems.length > 0) { + // Return the closest droppable within that container + overId = closestCenter({ + ...args, + droppableContainers: args.droppableContainers.filter( + (container) => + container.id !== overId && + containerItems.map((v) => v.id).includes(container.id as string) + ) + })[0]?.id; + } + } - const findContainer = useMemoizedFn((id: string) => { - if (rows.some((row) => row.id === id)) { - return id; - } + lastOverId.current = overId as string; - return rows.find((row) => row.items.some((item) => item.id === id))?.id; - }); + return [{ id: overId }]; + } - const onDragCancel = useMemoizedFn(() => { + // When a draggable item moves to a new container, the layout may shift + // and the `overId` may become `null`. We manually set the cached `lastOverId` + // to the id of the draggable item that was moved to the new container, otherwise + // the previous `overId` will be returned which can cause items to incorrectly shift positions + if (recentlyMovedToNewContainer.current) { + lastOverId.current = activeId; + } + + // If no droppable is matched, return the last match + return lastOverId.current ? [{ id: lastOverId.current }] : []; + }, + [activeId, rows] + ); + + const findContainer = useCallback( + (id: string) => { + if (rows.some((row) => row.id === id)) { + return id; + } + + return rows.find((row) => row.items.some((item) => item.id === id))?.id; + }, + [rows] + ); + + const onDragCancel = useCallback(() => { if (serverRows) { // Reset items to their original state in case items have been // Dragged across containers @@ -145,17 +151,20 @@ export const BusterResizeableGrid: React.FC<{ } setActiveId(null); - }); + }, [serverRows, onRowLayoutChangePreflight]); - const onDragStart = useMemoizedFn(({ active }: DragStartEvent) => { - const style = document.createElement('style'); - style.innerHTML = '* { cursor: grabbing; }'; - document.head.appendChild(style); - styleRef.current = style; - setActiveId(active.id as string); - onStartDrag?.({ id: active.id as string }); - isAnimating.current = true; - }); + const onDragStart = useCallback( + ({ active }: DragStartEvent) => { + const style = document.createElement('style'); + style.innerHTML = '* { cursor: grabbing; }'; + document.head.appendChild(style); + styleRef.current = style; + setActiveId(active.id as string); + onStartDrag?.({ id: active.id as string }); + isAnimating.current = true; + }, + [onStartDrag] + ); const onDragEnd = useMemoizedFn( ({ over, active, delta, activatorEvent, collisions }: DragEndEvent) => { diff --git a/apps/web/src/components/ui/layouts/PreventNavigation.tsx b/apps/web/src/components/ui/layouts/PreventNavigation.tsx index 3549eea15..e78659855 100644 --- a/apps/web/src/components/ui/layouts/PreventNavigation.tsx +++ b/apps/web/src/components/ui/layouts/PreventNavigation.tsx @@ -200,48 +200,44 @@ const LeavingDialog: React.FC<{ okText: string; canceling: boolean; okaying: boolean; -}> = React.memo( - ({ - onClose, - isOpen, - okaying, - canceling, - noCallback, - yesCallback, - title, - description, - okText, - cancelText - }) => { - const disableButtons = okaying || canceling; +}> = ({ + onClose, + isOpen, + okaying, + canceling, + noCallback, + yesCallback, + title, + description, + okText, + cancelText +}) => { + const disableButtons = okaying || canceling; - const memoizedHeader = useMemo(() => { - return { title, description }; - }, [title, description]); + const memoizedHeader = useMemo(() => { + return { title, description }; + }, [title, description]); - const memoizedFooter = useMemo(() => { - return { - primaryButton: { - text: cancelText, - onClick: noCallback, - loading: canceling, - disabled: disableButtons - }, - secondaryButton: { - text: okText, - onClick: yesCallback, - loading: okaying, - disabled: disableButtons - } - }; - }, [okaying, canceling, disableButtons, noCallback, yesCallback, cancelText, okText]); + const memoizedFooter = useMemo(() => { + return { + primaryButton: { + text: cancelText, + onClick: noCallback, + loading: canceling, + disabled: disableButtons + }, + secondaryButton: { + text: okText, + onClick: yesCallback, + loading: okaying, + disabled: disableButtons + } + }; + }, [okaying, canceling, disableButtons, noCallback, yesCallback, cancelText, okText]); - return ( - <> - - - ); - } -); + return ( + + ); +}; LeavingDialog.displayName = 'LeavingDialog';