mirror of
https://github.com/nimbold/Firelink.git
synced 2026-07-26 12:08:27 +00:00
fix(downloads): animate table column reordering
This commit is contained in:
@@ -189,6 +189,7 @@ export const DownloadItem = React.memo<DownloadItemProps>(({
|
|||||||
'File Name': (
|
'File Name': (
|
||||||
<div
|
<div
|
||||||
className="download-column-cell download-file-cell download-column-file-name"
|
className="download-column-cell download-file-cell download-column-file-name"
|
||||||
|
data-column-key="File Name"
|
||||||
style={columnStyle('File Name')}
|
style={columnStyle('File Name')}
|
||||||
>
|
>
|
||||||
<div className="download-cell-content">
|
<div className="download-cell-content">
|
||||||
@@ -209,6 +210,7 @@ export const DownloadItem = React.memo<DownloadItemProps>(({
|
|||||||
Size: (
|
Size: (
|
||||||
<div
|
<div
|
||||||
className="download-column-cell download-cell-truncate download-size-cell tabular-nums"
|
className="download-column-cell download-cell-truncate download-size-cell tabular-nums"
|
||||||
|
data-column-key="Size"
|
||||||
style={columnStyle('Size')}
|
style={columnStyle('Size')}
|
||||||
title={hasDownloadedAmount ? downloadedSizeLabel : completedSizeLabel}
|
title={hasDownloadedAmount ? downloadedSizeLabel : completedSizeLabel}
|
||||||
aria-label={hasDownloadedAmount ? downloadedSizeLabel : completedSizeLabel}
|
aria-label={hasDownloadedAmount ? downloadedSizeLabel : completedSizeLabel}
|
||||||
@@ -231,6 +233,7 @@ export const DownloadItem = React.memo<DownloadItemProps>(({
|
|||||||
Status: (
|
Status: (
|
||||||
<div
|
<div
|
||||||
className="download-column-cell download-status-cell"
|
className="download-column-cell download-status-cell"
|
||||||
|
data-column-key="Status"
|
||||||
data-column-alignment={columnAlignments.Status}
|
data-column-alignment={columnAlignments.Status}
|
||||||
style={columnStyle('Status')}
|
style={columnStyle('Status')}
|
||||||
>
|
>
|
||||||
@@ -294,21 +297,21 @@ export const DownloadItem = React.memo<DownloadItemProps>(({
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
Speed: (
|
Speed: (
|
||||||
<div className="download-column-cell download-cell-truncate" style={columnStyle('Speed')}>
|
<div className="download-column-cell download-cell-truncate" data-column-key="Speed" style={columnStyle('Speed')}>
|
||||||
<span className="download-cell-content tabular-nums" title={displaySpeed}>
|
<span className="download-cell-content tabular-nums" title={displaySpeed}>
|
||||||
{displaySpeed}
|
{displaySpeed}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
ETA: (
|
ETA: (
|
||||||
<div className="download-column-cell download-cell-truncate" style={columnStyle('ETA')}>
|
<div className="download-column-cell download-cell-truncate" data-column-key="ETA" style={columnStyle('ETA')}>
|
||||||
<span className="download-cell-content tabular-nums" title={displayEta}>
|
<span className="download-cell-content tabular-nums" title={displayEta}>
|
||||||
{displayEta}
|
{displayEta}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
'Date Added': (
|
'Date Added': (
|
||||||
<div className="download-column-cell download-cell-right download-column-date-added" style={columnStyle('Date Added')}>
|
<div className="download-column-cell download-cell-right download-column-date-added" data-column-key="Date Added" style={columnStyle('Date Added')}>
|
||||||
<span
|
<span
|
||||||
className="download-cell-content download-date-value tabular-nums"
|
className="download-cell-content download-date-value tabular-nums"
|
||||||
title={dateAddedLabel}
|
title={dateAddedLabel}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { useDownloadProgressStore } from '../store/downloadProgressStore';
|
|||||||
import { useToast } from '../contexts/ToastContext';
|
import { useToast } from '../contexts/ToastContext';
|
||||||
import { useSettingsStore } from '../store/useSettingsStore';
|
import { useSettingsStore } from '../store/useSettingsStore';
|
||||||
import { SidebarFilter } from './Sidebar';
|
import { SidebarFilter } from './Sidebar';
|
||||||
import { useAutoAnimate } from '@formkit/auto-animate/react';
|
|
||||||
import {
|
import {
|
||||||
Play, Pause, Plus, FileText, Image as ImageIcon, Music, Film, Box, Archive, FileQuestion,
|
Play, Pause, Plus, FileText, Image as ImageIcon, Music, Film, Box, Archive, FileQuestion,
|
||||||
ArrowDownCircle, ArrowUp, ArrowDown, Command, ChevronRight, ChevronUp, ChevronDown, MoreHorizontal,
|
ArrowDownCircle, ArrowUp, ArrowDown, Command, ChevronRight, ChevronUp, ChevronDown, MoreHorizontal,
|
||||||
@@ -87,6 +86,8 @@ const persistColumnAlignments = (alignments: Record<DownloadTableColumnKey, Down
|
|||||||
|
|
||||||
const QUEUE_ROW_REORDER_DURATION = 180;
|
const QUEUE_ROW_REORDER_DURATION = 180;
|
||||||
const QUEUE_ROW_REORDER_EASING = 'cubic-bezier(0.2, 0.8, 0.2, 1)';
|
const QUEUE_ROW_REORDER_EASING = 'cubic-bezier(0.2, 0.8, 0.2, 1)';
|
||||||
|
const COLUMN_REORDER_DURATION = QUEUE_ROW_REORDER_DURATION;
|
||||||
|
const COLUMN_REORDER_EASING = QUEUE_ROW_REORDER_EASING;
|
||||||
|
|
||||||
interface ColumnDragState {
|
interface ColumnDragState {
|
||||||
key: DownloadTableColumnKey;
|
key: DownloadTableColumnKey;
|
||||||
@@ -136,6 +137,11 @@ interface QueueDragGeometry {
|
|||||||
rows: Map<string, QueueDragRowGeometry>;
|
rows: Map<string, QueueDragRowGeometry>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ColumnMotionLayout {
|
||||||
|
element: HTMLElement;
|
||||||
|
left: number;
|
||||||
|
}
|
||||||
|
|
||||||
export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryChange }) => {
|
export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryChange }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
@@ -162,10 +168,6 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [contextMenu, setContextMenu] = useState<{ x: number; y: number; id: string } | null>(null);
|
const [contextMenu, setContextMenu] = useState<{ x: number; y: number; id: string } | null>(null);
|
||||||
const [headerAnimationParent, setHeaderAnimationEnabled] = useAutoAnimate<HTMLDivElement>({
|
|
||||||
duration: 140,
|
|
||||||
easing: 'ease-out',
|
|
||||||
});
|
|
||||||
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
||||||
const [lastSelectedId, setLastSelectedId] = useState<string | null>(null);
|
const [lastSelectedId, setLastSelectedId] = useState<string | null>(null);
|
||||||
const [sortConfig, setSortConfig] = useState<DownloadSortConfig>({ column: 'Date Added', direction: 'desc' });
|
const [sortConfig, setSortConfig] = useState<DownloadSortConfig>({ column: 'Date Added', direction: 'desc' });
|
||||||
@@ -182,8 +184,7 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
const headerContainerRef = useRef<HTMLDivElement>(null);
|
const headerContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const headerRef = useCallback((element: HTMLDivElement | null) => {
|
const headerRef = useCallback((element: HTMLDivElement | null) => {
|
||||||
headerContainerRef.current = element;
|
headerContainerRef.current = element;
|
||||||
headerAnimationParent(element);
|
}, []);
|
||||||
}, [headerAnimationParent]);
|
|
||||||
const columnDragStateRef = useRef<ColumnDragState | null>(null);
|
const columnDragStateRef = useRef<ColumnDragState | null>(null);
|
||||||
const columnDragOrderRef = useRef<DownloadTableColumnKey[] | null>(null);
|
const columnDragOrderRef = useRef<DownloadTableColumnKey[] | null>(null);
|
||||||
const columnDragLayoutRef = useRef<Map<DownloadTableColumnKey, ColumnLayoutBounds> | null>(null);
|
const columnDragLayoutRef = useRef<Map<DownloadTableColumnKey, ColumnLayoutBounds> | null>(null);
|
||||||
@@ -192,6 +193,11 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
const columnDragCapturePointerIdRef = useRef<number | null>(null);
|
const columnDragCapturePointerIdRef = useRef<number | null>(null);
|
||||||
const columnDragCleanupRef = useRef<(() => void) | null>(null);
|
const columnDragCleanupRef = useRef<(() => void) | null>(null);
|
||||||
const columnDropFlashTimerRef = useRef<number | null>(null);
|
const columnDropFlashTimerRef = useRef<number | null>(null);
|
||||||
|
const columnMotionFirstRef = useRef<ColumnMotionLayout[] | null>(null);
|
||||||
|
const columnMotionElementsRef = useRef(new Set<HTMLElement>());
|
||||||
|
const columnMotionFrameRef = useRef<number | null>(null);
|
||||||
|
const columnMotionTimerRef = useRef<number | null>(null);
|
||||||
|
const columnMotionGenerationRef = useRef(0);
|
||||||
const suppressHeaderClickRef = useRef(false);
|
const suppressHeaderClickRef = useRef(false);
|
||||||
const selectedIdsRef = useRef(selectedIds);
|
const selectedIdsRef = useRef(selectedIds);
|
||||||
const lastSelectedIdRef = useRef(lastSelectedId);
|
const lastSelectedIdRef = useRef(lastSelectedId);
|
||||||
@@ -264,6 +270,7 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
Math.max(COLUMN_MINIMUMS[index], width)
|
Math.max(COLUMN_MINIMUMS[index], width)
|
||||||
);
|
);
|
||||||
const orderedColumns = columnDragOrderRef.current ?? columnDragOrder ?? columnOrder;
|
const orderedColumns = columnDragOrderRef.current ?? columnDragOrder ?? columnOrder;
|
||||||
|
const columnMotionOrderKey = orderedColumns.join('\u0001');
|
||||||
const tableGridTemplate = buildColumnGridTemplate(orderedColumns, normalizedColumnWidths);
|
const tableGridTemplate = buildColumnGridTemplate(orderedColumns, normalizedColumnWidths);
|
||||||
const tableMinWidth = normalizedColumnWidths.reduce(
|
const tableMinWidth = normalizedColumnWidths.reduce(
|
||||||
(total, width) => total + width,
|
(total, width) => total + width,
|
||||||
@@ -278,6 +285,121 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
queueRowsContainerElementRef.current = element;
|
queueRowsContainerElementRef.current = element;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const captureColumnMotionLayout = (): ColumnMotionLayout[] => {
|
||||||
|
const header = headerContainerRef.current;
|
||||||
|
const body = queueRowsContainerElementRef.current;
|
||||||
|
const verticalViewport = queueListElementRef.current?.getBoundingClientRect();
|
||||||
|
const headerElements = header
|
||||||
|
? Array.from(header.children).filter(
|
||||||
|
(element): element is HTMLElement => element instanceof HTMLElement && Boolean(element.dataset.columnKey)
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
const bodyElements: HTMLElement[] = [];
|
||||||
|
if (body) {
|
||||||
|
for (const row of Array.from(body.children)) {
|
||||||
|
if (!(row instanceof HTMLElement) || !row.dataset.downloadId) continue;
|
||||||
|
if (verticalViewport) {
|
||||||
|
const rowRect = row.getBoundingClientRect();
|
||||||
|
if (rowRect.bottom < verticalViewport.top || rowRect.top > verticalViewport.bottom) continue;
|
||||||
|
}
|
||||||
|
const motionElement = row.querySelector<HTMLElement>('.download-row-motion');
|
||||||
|
if (!motionElement) continue;
|
||||||
|
bodyElements.push(...Array.from(motionElement.children).filter(
|
||||||
|
(element): element is HTMLElement => element instanceof HTMLElement && Boolean(element.dataset.columnKey)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...headerElements, ...bodyElements].map(element => ({
|
||||||
|
element,
|
||||||
|
left: element.getBoundingClientRect().left,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearColumnMotionStyles = () => {
|
||||||
|
columnMotionGenerationRef.current += 1;
|
||||||
|
if (columnMotionFrameRef.current !== null) {
|
||||||
|
window.cancelAnimationFrame(columnMotionFrameRef.current);
|
||||||
|
columnMotionFrameRef.current = null;
|
||||||
|
}
|
||||||
|
if (columnMotionTimerRef.current !== null) {
|
||||||
|
window.clearTimeout(columnMotionTimerRef.current);
|
||||||
|
columnMotionTimerRef.current = null;
|
||||||
|
}
|
||||||
|
for (const element of columnMotionElementsRef.current) {
|
||||||
|
element.style.removeProperty('transition');
|
||||||
|
element.style.removeProperty('transform');
|
||||||
|
element.style.removeProperty('will-change');
|
||||||
|
}
|
||||||
|
columnMotionElementsRef.current.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
const prepareColumnMotion = () => {
|
||||||
|
const prefersReducedMotion = typeof window.matchMedia === 'function' &&
|
||||||
|
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
if (prefersReducedMotion) {
|
||||||
|
clearColumnMotionStyles();
|
||||||
|
columnMotionFirstRef.current = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture the visual position before cancelling an in-flight transition.
|
||||||
|
// The next FLIP pass can then continue from what the user actually sees
|
||||||
|
// instead of snapping back to the previous logical grid position.
|
||||||
|
const first = captureColumnMotionLayout();
|
||||||
|
clearColumnMotionStyles();
|
||||||
|
columnMotionFirstRef.current = first;
|
||||||
|
};
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const first = columnMotionFirstRef.current;
|
||||||
|
columnMotionFirstRef.current = null;
|
||||||
|
if (!first || first.length === 0) return;
|
||||||
|
|
||||||
|
const prefersReducedMotion = typeof window.matchMedia === 'function' &&
|
||||||
|
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
if (prefersReducedMotion) return;
|
||||||
|
|
||||||
|
const moved = first.flatMap(({ element, left }) => {
|
||||||
|
if (!element.isConnected) return [];
|
||||||
|
const deltaX = left - element.getBoundingClientRect().left;
|
||||||
|
return Math.abs(deltaX) > 0.5 ? [{ element, deltaX }] : [];
|
||||||
|
});
|
||||||
|
if (moved.length === 0) return;
|
||||||
|
|
||||||
|
const generation = ++columnMotionGenerationRef.current;
|
||||||
|
for (const { element, deltaX } of moved) {
|
||||||
|
columnMotionElementsRef.current.add(element);
|
||||||
|
element.style.willChange = 'transform';
|
||||||
|
element.style.transition = 'none';
|
||||||
|
element.style.transform = `translate3d(${deltaX}px, 0, 0)`;
|
||||||
|
}
|
||||||
|
// Commit the inverse transforms before releasing them on the next frame so
|
||||||
|
// React's grid reflow becomes a smooth physical-LTR motion for headers and
|
||||||
|
// every visible row cell alike.
|
||||||
|
void headerContainerRef.current?.offsetHeight;
|
||||||
|
void queueRowsContainerElementRef.current?.offsetHeight;
|
||||||
|
|
||||||
|
columnMotionFrameRef.current = window.requestAnimationFrame(() => {
|
||||||
|
columnMotionFrameRef.current = null;
|
||||||
|
if (columnMotionGenerationRef.current !== generation) return;
|
||||||
|
for (const { element } of moved) {
|
||||||
|
element.style.transition = `transform ${COLUMN_REORDER_DURATION}ms ${COLUMN_REORDER_EASING}`;
|
||||||
|
element.style.transform = 'translate3d(0, 0, 0)';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
columnMotionTimerRef.current = window.setTimeout(() => {
|
||||||
|
columnMotionTimerRef.current = null;
|
||||||
|
if (columnMotionGenerationRef.current !== generation) return;
|
||||||
|
for (const { element } of moved) {
|
||||||
|
element.style.removeProperty('transition');
|
||||||
|
element.style.removeProperty('transform');
|
||||||
|
element.style.removeProperty('will-change');
|
||||||
|
columnMotionElementsRef.current.delete(element);
|
||||||
|
}
|
||||||
|
}, COLUMN_REORDER_DURATION + 60);
|
||||||
|
}, [columnMotionOrderKey]);
|
||||||
|
|
||||||
const updateColumnDragState = (next: ColumnDragState | null) => {
|
const updateColumnDragState = (next: ColumnDragState | null) => {
|
||||||
columnDragStateRef.current = next;
|
columnDragStateRef.current = next;
|
||||||
setColumnDragState(next);
|
setColumnDragState(next);
|
||||||
@@ -294,8 +416,8 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getColumnLayoutBounds = (element: HTMLDivElement) => {
|
const getColumnLayoutBounds = (element: HTMLDivElement) => {
|
||||||
// AutoAnimate transforms the visual box; offsetLeft/offsetWidth retain the
|
// CSS FLIP transforms affect the visual box; offsetLeft/offsetWidth retain
|
||||||
// stable grid geometry needed for hit-testing during a live reorder.
|
// the stable grid geometry needed for hit-testing during a live reorder.
|
||||||
const container = headerContainerRef.current;
|
const container = headerContainerRef.current;
|
||||||
if (!container) {
|
if (!container) {
|
||||||
const rect = element.getBoundingClientRect();
|
const rect = element.getBoundingClientRect();
|
||||||
@@ -348,6 +470,10 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
const current = columnDragStateRef.current;
|
const current = columnDragStateRef.current;
|
||||||
const nextOrder = columnDragOrderRef.current ?? columnOrderRef.current;
|
const nextOrder = columnDragOrderRef.current ?? columnOrderRef.current;
|
||||||
const captureTarget = columnDragCaptureTargetRef.current;
|
const captureTarget = columnDragCaptureTargetRef.current;
|
||||||
|
const shouldAnimateCancel = cancelled && gesture.active && Boolean(current);
|
||||||
|
if (shouldAnimateCancel) {
|
||||||
|
prepareColumnMotion();
|
||||||
|
}
|
||||||
dragGestureRef.current = null;
|
dragGestureRef.current = null;
|
||||||
columnDragTargetRef.current = null;
|
columnDragTargetRef.current = null;
|
||||||
columnDragCaptureTargetRef.current = null;
|
columnDragCaptureTargetRef.current = null;
|
||||||
@@ -358,7 +484,6 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
columnDragCleanupRef.current = null;
|
columnDragCleanupRef.current = null;
|
||||||
updateColumnDragState(null);
|
updateColumnDragState(null);
|
||||||
setColumnDragOrder(null);
|
setColumnDragOrder(null);
|
||||||
setHeaderAnimationEnabled(true);
|
|
||||||
if (captureTarget?.hasPointerCapture(pointerId)) {
|
if (captureTarget?.hasPointerCapture(pointerId)) {
|
||||||
captureTarget.releasePointerCapture(pointerId);
|
captureTarget.releasePointerCapture(pointerId);
|
||||||
}
|
}
|
||||||
@@ -416,9 +541,8 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
const rect = target?.getBoundingClientRect();
|
const rect = target?.getBoundingClientRect();
|
||||||
if (!rect) return;
|
if (!rect) return;
|
||||||
// The header children are reordered in the live grid while dragging.
|
// The header children are reordered in the live grid while dragging.
|
||||||
// AutoAnimate's MutationObserver must not animate the same structural
|
// The custom FLIP pass animates the visual boxes while offset geometry
|
||||||
// changes that drive pointer hit-testing; it is restored on completion.
|
// remains stable for pointer hit-testing.
|
||||||
setHeaderAnimationEnabled(false);
|
|
||||||
const currentOrder = columnOrderRef.current;
|
const currentOrder = columnOrderRef.current;
|
||||||
columnDragLayoutRef.current = captureColumnLayout(currentOrder);
|
columnDragLayoutRef.current = captureColumnLayout(currentOrder);
|
||||||
columnDragOrderRef.current = [...currentOrder];
|
columnDragOrderRef.current = [...currentOrder];
|
||||||
@@ -442,6 +566,7 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
const nextOrder = reorderColumn(currentOrder, key, dropIndex);
|
const nextOrder = reorderColumn(currentOrder, key, dropIndex);
|
||||||
const orderChanged = nextOrder.some((columnKey, index) => columnKey !== currentOrder[index]);
|
const orderChanged = nextOrder.some((columnKey, index) => columnKey !== currentOrder[index]);
|
||||||
if (orderChanged) {
|
if (orderChanged) {
|
||||||
|
prepareColumnMotion();
|
||||||
columnDragOrderRef.current = nextOrder;
|
columnDragOrderRef.current = nextOrder;
|
||||||
setColumnDragOrder(nextOrder);
|
setColumnDragOrder(nextOrder);
|
||||||
}
|
}
|
||||||
@@ -594,6 +719,9 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
const widths = [...DEFAULT_COLUMN_WIDTHS];
|
const widths = [...DEFAULT_COLUMN_WIDTHS];
|
||||||
const order = [...DEFAULT_COLUMN_ORDER];
|
const order = [...DEFAULT_COLUMN_ORDER];
|
||||||
const alignments = { ...DEFAULT_COLUMN_ALIGNMENTS };
|
const alignments = { ...DEFAULT_COLUMN_ALIGNMENTS };
|
||||||
|
if (order.join('|') !== columnOrderRef.current.join('|')) {
|
||||||
|
prepareColumnMotion();
|
||||||
|
}
|
||||||
setColumnWidths(widths);
|
setColumnWidths(widths);
|
||||||
columnWidthsRef.current = widths;
|
columnWidthsRef.current = widths;
|
||||||
columnOrderRef.current = order;
|
columnOrderRef.current = order;
|
||||||
@@ -627,6 +755,8 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
useEffect(() => () => {
|
useEffect(() => () => {
|
||||||
resizeCleanupRef.current?.();
|
resizeCleanupRef.current?.();
|
||||||
columnDragCleanupRef.current?.();
|
columnDragCleanupRef.current?.();
|
||||||
|
columnMotionFirstRef.current = null;
|
||||||
|
clearColumnMotionStyles();
|
||||||
try {
|
try {
|
||||||
queueDragCleanupRef.current?.();
|
queueDragCleanupRef.current?.();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -661,7 +791,11 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const cancelColumnDrag = () => {
|
const cancelColumnDrag = () => {
|
||||||
if (!dragGestureRef.current) return;
|
const gesture = dragGestureRef.current;
|
||||||
|
if (!gesture) return;
|
||||||
|
if (gesture.active && columnDragStateRef.current) {
|
||||||
|
prepareColumnMotion();
|
||||||
|
}
|
||||||
columnDragCleanupRef.current?.();
|
columnDragCleanupRef.current?.();
|
||||||
columnDragCleanupRef.current = null;
|
columnDragCleanupRef.current = null;
|
||||||
const captureTarget = columnDragCaptureTargetRef.current;
|
const captureTarget = columnDragCaptureTargetRef.current;
|
||||||
@@ -676,7 +810,6 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
|||||||
columnDragStateRef.current = null;
|
columnDragStateRef.current = null;
|
||||||
columnDragOrderRef.current = null;
|
columnDragOrderRef.current = null;
|
||||||
columnDragLayoutRef.current = null;
|
columnDragLayoutRef.current = null;
|
||||||
setHeaderAnimationEnabled(true);
|
|
||||||
setColumnDragState(null);
|
setColumnDragState(null);
|
||||||
setColumnDragOrder(null);
|
setColumnDragOrder(null);
|
||||||
suppressHeaderClickRef.current = false;
|
suppressHeaderClickRef.current = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user