diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 5e4c76a..c9f0294 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -95,7 +95,7 @@ import { type StatisticsViewActions } from "./views/statistics/StatisticsView"; import { buildDownloadsViewModel, formatRemainingDownloadBytes, getDownloadQueueTotalBytes, getDownloadSpeedBps, getPendingDownloadItemCount, getRemainingDownloadBytes, type DownloadDisplayMode, type DownloadSidebarFilter } from "./views/downloads/downloads-model"; -import { applyDownloadGridTemplate, downloadColumnDefinitions, type DownloadSortColumn } from "./views/downloads/DownloadsTable"; +import { downloadColumnDefinitions, type DownloadSortColumn } from "./views/downloads/DownloadsTable"; import { DeleteConfirmationDialog } from "./views/downloads/DeleteConfirmationDialog"; import { beginDownloadColumnDrag, clearDownloadColumnDrag, commitDownloadColumnDrag, createDownloadColumnOrderPersistence, DOWNLOAD_COLUMN_MOVE_DURATION_MS, updateDownloadColumnDrag, type DownloadColumnDragSession, type DownloadColumnOrderPersistence } from "./views/downloads/column-drag"; import { @@ -4917,11 +4917,11 @@ export function App(): ReactElement { suppressColumnSortRef.current = true; window.setTimeout(() => { suppressColumnSortRef.current = false; }, 0); const changed = next.join("|") !== session.measurements.map((measurement) => measurement.id).join("|"); - if (changed) persistColumnOrder(next); const animationsEnabled = snapshot.settings.animatePackageDisclosure; columnDragAnimationsRef.current = commitDownloadColumnDrag(session, next, (order) => { if (changed) flushSync(() => setColumnOrder(order)); - }, () => applyDownloadGridTemplate(session.root, next), animationsEnabled); + }, () => {}, animationsEnabled); + if (changed) persistColumnOrder(next); if (!animationsEnabled) { if (columnDragSessionRef.current === session) columnDragSessionRef.current = null; return; diff --git a/src/renderer/views/downloads/DownloadsTable.tsx b/src/renderer/views/downloads/DownloadsTable.tsx index 3b1a4ca..030d9c1 100644 --- a/src/renderer/views/downloads/DownloadsTable.tsx +++ b/src/renderer/views/downloads/DownloadsTable.tsx @@ -44,13 +44,6 @@ export function downloadGridTemplateForOrder(columnOrder: readonly string[]): st return downloadGridTemplate(columnOrder.map((column) => downloadColumnDefinitions[column]?.width ?? "100px").join(" ")); } -export function applyDownloadGridTemplate(root: HTMLElement, columnOrder: readonly string[]): void { - const value = downloadGridTemplateForOrder(columnOrder); - root.querySelectorAll(".downloads-table-header, .downloads-package-row, .downloads-item-row").forEach((row) => { - row.style.gridTemplateColumns = value; - }); -} - function isPackageRowDisclosureExcluded(target: EventTarget | null): boolean { const closest = (target as { closest?: (selector: string) => Element | null } | null)?.closest; return typeof closest === "function" && closest.call(target, PACKAGE_ROW_DISCLOSURE_EXCLUSION_SELECTOR) !== null; diff --git a/src/renderer/views/downloads/column-drag.ts b/src/renderer/views/downloads/column-drag.ts index 6a572f3..e86b0da 100644 --- a/src/renderer/views/downloads/column-drag.ts +++ b/src/renderer/views/downloads/column-drag.ts @@ -124,12 +124,22 @@ export function commitDownloadColumnDrag( prepare: () => void = () => {}, animationsEnabled = true ): Animation[] { - const elements = Array.from(session.root.querySelectorAll("[data-download-column]")); + if (!animationsEnabled) { + clearDownloadColumnDrag(session); + prepare(); + commit(order); + return []; + } + const originalOrder = session.measurements.map((measurement) => measurement.id); + const movedIds = originalOrder.filter((id, index) => ( + order[index] !== id || Math.abs(session.preview.settleOffsets[id] ?? 0) >= 0.5 + )); + const selector = movedIds.map((id) => `[data-download-column="${id}"]`).join(", "); + const elements = selector ? Array.from(session.root.querySelectorAll(selector)) : []; const before = new Map(elements.map((element) => [element, element.getBoundingClientRect()])); clearDownloadColumnDrag(session); prepare(); commit(order); - if (!animationsEnabled) return []; session.root.classList.add("is-column-drag-settling"); const animations: Animation[] = []; for (const element of elements) { diff --git a/tests/app-shell.test.tsx b/tests/app-shell.test.tsx index dd0c887..9c7ed46 100644 --- a/tests/app-shell.test.tsx +++ b/tests/app-shell.test.tsx @@ -79,6 +79,7 @@ describe("desktop shell", () => { expect(pointerUp.indexOf("commitDownloadColumnDrag")).toBeGreaterThanOrEqual(0); expect(pointerUp.indexOf("commitDownloadColumnDrag")).toBeLessThan(pointerUp.indexOf("columnDragSettleTimerRef.current = window.setTimeout")); + expect(pointerUp.indexOf("commitDownloadColumnDrag")).toBeLessThan(pointerUp.indexOf("persistColumnOrder")); expect(pointerUp).toContain("snapshot.settings.animatePackageDisclosure"); }); diff --git a/tests/column-drag.test.ts b/tests/column-drag.test.ts index 6ced94c..6771730 100644 --- a/tests/column-drag.test.ts +++ b/tests/column-drag.test.ts @@ -54,9 +54,19 @@ describe("animated download column drag", () => { }); it.each([ - { afterLeft: 150, beforeLeft: 0, expectedDelta: -150, next: ["size", "name", "status"] }, - { afterLeft: 0, beforeLeft: 150, expectedDelta: 150, next: ["name", "size", "status"] } - ])("commits the target grid before animating from a $expectedDelta px inverse offset", ({ afterLeft, beforeLeft, expectedDelta, next }) => { + { afterLeft: 150, beforeLeft: 0, expectedDelta: -150, measurements: columns, next: ["size", "name", "status"] }, + { + afterLeft: 0, + beforeLeft: 150, + expectedDelta: 150, + measurements: [ + { id: "size", left: 0, width: 150 }, + { id: "name", left: 150, width: 300 }, + { id: "status", left: 450, width: 100 } + ], + next: ["name", "size", "status"] + } + ])("commits the target grid before animating from a $expectedDelta px inverse offset", ({ afterLeft, beforeLeft, expectedDelta, measurements, next }) => { const commitDownloadColumnDrag = (columnDrag as unknown as { commitDownloadColumnDrag?: (session: DownloadColumnDragSession, order: string[], commit: (order: string[]) => void, prepare: () => void) => Animation[]; }).commitDownloadColumnDrag; @@ -64,6 +74,7 @@ describe("animated download column drag", () => { if (!commitDownloadColumnDrag) return; const events: string[] = []; + const selectors: string[] = []; let committed = false; const outerAnimate = vi.fn(() => ({ finished: Promise.resolve() } as unknown as Animation)); const animate = vi.fn(() => ({ finished: Promise.resolve() } as unknown as Animation)); @@ -76,7 +87,10 @@ describe("animated download column drag", () => { const root = { classList: { add: () => events.push("add-class"), remove: () => events.push("clear-classes") }, dataset: { columnDragging: "name" }, - querySelectorAll: (selector: string) => selector === "[data-download-column]" ? [element] : [], + querySelectorAll: (selector: string) => { + selectors.push(selector); + return selector.includes('data-download-column="name"') ? [element] : []; + }, style: { removeProperty: (property: string) => events.push(`clear:${property}`) } @@ -84,9 +98,9 @@ describe("animated download column drag", () => { const session = { active: true, draggedId: "name", - measurements: columns, + measurements, pointerId: -1, - preview: calculateColumnDragPreview(columns, "name", next[0] === "name" ? -160 : 160), + preview: calculateColumnDragPreview(measurements, "name", next[0] === "name" ? -160 : 160), root, startX: 0 } as DownloadColumnDragSession; @@ -97,6 +111,9 @@ describe("animated download column drag", () => { }, () => events.push("prepare-grid")); expect(animations).toHaveLength(1); + expect(selectors[0]).toContain('data-download-column="name"'); + expect(selectors[0]).toContain('data-download-column="size"'); + expect(selectors[0]).not.toContain('data-download-column="status"'); expect(events).toContain("prepare-grid"); expect(events.indexOf("prepare-grid")).toBeLessThan(events.indexOf(`commit:${next.join("|")}`)); expect(events).toContain(`commit:${next.join("|")}`); @@ -109,6 +126,7 @@ describe("animated download column drag", () => { it("commits immediately without WAAPI when application animations are disabled", () => { const animate = vi.fn(() => ({ finished: Promise.resolve() } as unknown as Animation)); + const querySelectorAll = vi.fn((selector: string) => selector === "[data-column-dragging]" ? [] : [element]); let committed = false; const element = { children: [{ animate }], @@ -117,7 +135,7 @@ describe("animated download column drag", () => { const root = { classList: { add: vi.fn(), remove: vi.fn() }, dataset: {}, - querySelectorAll: (selector: string) => selector === "[data-download-column]" ? [element] : [], + querySelectorAll, style: { removeProperty: vi.fn() } } as unknown as HTMLElement; const session = { @@ -136,6 +154,8 @@ describe("animated download column drag", () => { expect(committed).toBe(true); expect(animations).toEqual([]); + expect(querySelectorAll).toHaveBeenCalledTimes(1); + expect(querySelectorAll).toHaveBeenCalledWith("[data-column-dragging]"); expect(animate).not.toHaveBeenCalled(); });