minor update to dashboard grid and memoizing

This commit is contained in:
Nate Kelley 2025-08-08 10:07:48 -06:00
parent 5456561734
commit 31b3116e52
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
4 changed files with 131 additions and 134 deletions

View File

@ -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<ContainerProps> = ({
const mouse = useMouse({ moveThrottleMs: 50, disabled: readOnly || !over });
const [stagedLayoutColumns, setStagedLayoutColumns] = useState<number[]>(() => 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<ContainerProps> = ({
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<ContainerProps> = ({
<BusterResizeColumnsSplitPanes
columnSpans={stagedLayoutColumns || []}
allowResize={!readOnly && canResize}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
// onDragStart={onDragStart}
// onDragEnd={onDragEnd}
onChange={onChangeLayout}>
{items.map((item, index) => (
<div

View File

@ -2,8 +2,9 @@
import { useDroppable } from '@dnd-kit/core';
import clamp from 'lodash/clamp';
import React, { useMemo, useRef, useState } from 'react';
import { useDebounceFn, useMemoizedFn, useUpdateLayoutEffect } from '@/hooks';
import React, { useRef, useState } from 'react';
import { useDebounceFn, useUpdateLayoutEffect } from '@/hooks';
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
import { cn } from '@/lib/classMerge';
import { BusterNewItemDropzone } from './_BusterBusterNewItemDropzone';
import { BusterResizeColumns } from './BusterResizeColumns';
@ -21,13 +22,13 @@ export const BusterResizeRows: React.FC<{
const [sizes, setSizes] = useState<number[]>(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<HTMLButtonElement>) => {
const handler = (mouseDownEvent: React.MouseEvent<HTMLButtonElement>) => {
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}
/>
<div

View File

@ -15,7 +15,7 @@ import {
} from '@dnd-kit/core';
import { arrayMove } from '@dnd-kit/sortable';
import isEqual from 'lodash/isEqual';
import React, { useEffect, useRef, useState } from 'react';
import React, { use, useCallback, useEffect, useRef, useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
import { useMemoizedFn, useUpdateEffect } from '@/hooks';
import { cn } from '@/lib/utils';
@ -74,70 +74,76 @@ export const BusterResizeableGrid: React.FC<{
const sensors = useSensors(useSensor(PointerSensor, pointerSensors));
const collisionDetectionStrategy: CollisionDetection = useMemoizedFn((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)
)
});
}
// 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) => {

View File

@ -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 (
<>
<AppModal open={isOpen} onClose={onClose} header={memoizedHeader} footer={memoizedFooter} />
</>
);
}
);
return (
<AppModal open={isOpen} onClose={onClose} header={memoizedHeader} footer={memoizedFooter} />
);
};
LeavingDialog.displayName = 'LeavingDialog';