make a few refernces more stable

This commit is contained in:
Nate Kelley 2025-04-05 18:59:53 -06:00
parent c7311dafd5
commit 5796ebf222
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
5 changed files with 106 additions and 78 deletions

View File

@ -25,10 +25,8 @@ export const OverviewData: React.FC<{
) : !isEmpty(data) ? ( ) : !isEmpty(data) ? (
<AppDataGrid <AppDataGrid
rows={data || []} rows={data || []}
headerFormat={isAdmin ? (v) => String(v) : undefined} headerFormat={isAdmin ? stableHeaderFormat : undefined}
cellFormat={defaultCellFormatter} cellFormat={defaultCellFormatter}
resizable={true}
sortable={false}
/> />
) : ( ) : (
<EmptyState /> <EmptyState />
@ -54,3 +52,7 @@ const LoadingState: React.FC<{}> = () => {
</div> </div>
); );
}; };
const stableHeaderFormat = (value: string | number | Date | null, key: string): string => {
return String(value);
};

View File

@ -26,7 +26,7 @@ export const DataContainer: React.FC<{
/> />
{hasData ? ( {hasData ? (
<AppDataGrid rows={data} resizable={true} sortable={false} /> <AppDataGrid rows={data} />
) : ( ) : (
<div className="flex h-full items-center justify-center"> <div className="flex h-full items-center justify-center">
{fetchingData ? 'Loading data...' : 'No data returned'} {fetchingData ? 'Loading data...' : 'No data returned'}

View File

@ -85,6 +85,7 @@ const BusterTableChartBase: React.FC<
columnWidths={tableColumnWidths || undefined} columnWidths={tableColumnWidths || undefined}
sortable={!readOnly} sortable={!readOnly}
resizable={!readOnly} resizable={!readOnly}
draggable={!readOnly}
onReady={onReady} onReady={onReady}
headerFormat={onFormatHeader} headerFormat={onFormatHeader}
cellFormat={onFormatCell} cellFormat={onFormatCell}

View File

@ -1,5 +1,5 @@
import React, { CSSProperties } from 'react'; import React, { CSSProperties } from 'react';
import { DragOverlay, useDraggable, useDroppable } from '@dnd-kit/core'; import { useDraggable, useDroppable } from '@dnd-kit/core';
import { Header, Table } from '@tanstack/react-table'; import { Header, Table } from '@tanstack/react-table';
import { flexRender } from '@tanstack/react-table'; import { flexRender } from '@tanstack/react-table';
import { cn } from '@/lib/classMerge'; import { cn } from '@/lib/classMerge';
@ -16,86 +16,95 @@ interface DraggableHeaderProps {
draggable: boolean; draggable: boolean;
} }
const DraggableHeader: React.FC<DraggableHeaderProps> = React.memo( const DraggableHeader: React.FC<DraggableHeaderProps> = ({
({ header, sortable, resizable, isOverTarget, draggable }) => { header,
// Set up dnd-kit's useDraggable for this header cell sortable,
const { resizable,
attributes, isOverTarget,
listeners, draggable
isDragging, }) => {
setNodeRef: setDragNodeRef // Set up dnd-kit's useDraggable for this header cell
} = useDraggable({ const {
disabled: !draggable, attributes,
id: header.id listeners,
}); isDragging,
setNodeRef: setDragNodeRef
} = useDraggable({
disabled: !draggable,
id: header.id
});
// Set up droppable area to detect when a header is over this target // Set up droppable area to detect when a header is over this target
const { setNodeRef: setDropNodeRef } = useDroppable({ const { setNodeRef: setDropNodeRef } = useDroppable({
id: `droppable-${header.id}` id: `droppable-${header.id}`
}); });
const style: CSSProperties = { const style: CSSProperties = {
position: 'relative', position: 'relative',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
width: header.column.getSize(), width: header.column.getSize(),
opacity: isDragging ? 0.65 : 1, opacity: isDragging ? 0.65 : 1,
transition: 'none', // Prevent any transitions for snappy changes transition: 'none', // Prevent any transitions for snappy changes
height: `${HEADER_HEIGHT}px` // Set fixed header height height: `${HEADER_HEIGHT}px` // Set fixed header height
}; };
return ( return (
<th <th
ref={draggable ? setDropNodeRef : undefined} ref={draggable ? setDropNodeRef : undefined}
style={style} style={style}
className={cn(
'group bg-background relative border-r select-none last:border-r-0',
header.column.getIsResizing() ? 'bg-primary/10' : 'hover:bg-item-hover',
isOverTarget && 'bg-primary/10 border-primary inset border border-r! border-dashed'
)}
// onClick toggles sorting if enabled
onClick={header.column.getCanSort() ? header.column.getToggleSortingHandler() : undefined}>
<span
className={cn( className={cn(
'group bg-background relative border-r select-none last:border-r-0', 'flex h-full flex-1 items-center space-x-1.5 p-2',
header.column.getIsResizing() ? 'bg-primary/10' : 'hover:bg-item-hover', draggable && 'cursor-grab'
isOverTarget && 'bg-primary/10 border-primary inset border border-r! border-dashed'
)} )}
// onClick toggles sorting if enabled ref={draggable ? setDragNodeRef : undefined}
onClick={header.column.getCanSort() ? header.column.getToggleSortingHandler() : undefined}> {...attributes}
<span {...listeners}>
className={cn( <span className="text-gray-dark text-base font-normal">
'flex h-full flex-1 items-center space-x-1.5 p-2', {flexRender(header.column.columnDef.header, header.getContext())}
draggable && 'cursor-grab'
)}
ref={draggable ? setDragNodeRef : undefined}
{...attributes}
{...listeners}>
<span className="text-gray-dark text-base font-normal">
{flexRender(header.column.columnDef.header, header.getContext())}
</span>
{header.column.getIsSorted() === 'asc' && (
<span className="text-icon-color text-xs">
<CaretUp />
</span>
)}
{header.column.getIsSorted() === 'desc' && (
<span className="text-icon-color text-xs">
<CaretDown />
</span>
)}
</span> </span>
{resizable && (
<span {sortable && (
onClick={(e) => { <>
e.stopPropagation(); {header.column.getIsSorted() === 'asc' && (
e.preventDefault(); <span className="text-icon-color text-xs">
}}> <CaretUp />
<span </span>
onMouseDown={header.getResizeHandler()} )}
onTouchStart={header.getResizeHandler()} {header.column.getIsSorted() === 'desc' && (
className={cn( <span className="text-icon-color text-xs">
'group-hover:bg-border hover:bg-primary absolute inset-y-0 -right-0.5 z-10 w-1 cursor-col-resize transition-colors duration-100 select-none', <CaretDown />
header.column.getIsResizing() && 'bg-primary' </span>
)} )}
/> </>
</span>
)} )}
</th> </span>
); {resizable && (
} <span
); onClick={(e) => {
e.stopPropagation();
e.preventDefault();
}}>
<span
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
className={cn(
'group-hover:bg-border hover:bg-primary absolute inset-y-0 -right-0.5 z-10 w-1 cursor-col-resize transition-colors duration-100 select-none',
header.column.getIsResizing() && 'bg-primary'
)}
/>
</span>
)}
</th>
);
};
DraggableHeader.displayName = 'DraggableHeader'; DraggableHeader.displayName = 'DraggableHeader';

View File

@ -71,6 +71,22 @@ export const NonDraggable: Story = {
} }
}; };
export const NonResizable: Story = {
args: {
...meta.args,
rows: sampleData,
resizable: false
}
};
export const NonSortable: Story = {
args: {
...meta.args,
rows: sampleData,
sortable: false
}
};
export const CustomColumnOrder: Story = { export const CustomColumnOrder: Story = {
args: { args: {
...meta.args, ...meta.args,