| author | |
| committer | |
| log | 1afe5545b7d0397bbd6515981372072d146002ad |
| tree | b2a778faa6441e2b3e222a86695772544c116043 |
| parent | 730ce9edd9873144c1d2b01e5f1c85414c3760ad |
| signature | Signed by PGP key B5690EEEBB952194 |
3 files changed, 37 insertions(+), 0 deletions(-)
apps/web/src/components/Sidebar.logic.test.ts+28| ... | ... | @@ -1,5 +1,7 @@ |
| 1 | 1 | import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; |
| 2 | import { defaultAnimateLayoutChanges, type AnimateLayoutChanges } from "@dnd-kit/sortable"; | |
| 2 | 3 | import { |
| 4 | animatePinnedLayoutChanges, | |
| 3 | 5 | archiveSelectedThreadEntries, |
| 4 | 6 | buildBulkTitleRegenerationContextMenuItem, |
| 5 | 7 | buildMultiSelectThreadContextMenuItems, |
| ... | ... | @@ -53,6 +55,32 @@ import { |
| 53 | 55 | |
| 54 | 56 | const localEnvironmentId = EnvironmentId.make("environment-local"); |
| 55 | 57 | |
| 58 | describe("animatePinnedLayoutChanges", () => { | |
| 59 | const baseArgs: Parameters<AnimateLayoutChanges>[0] = { | |
| 60 | active: null, | |
| 61 | containerId: "pinned-threads", | |
| 62 | isDragging: false, | |
| 63 | isSorting: false, | |
| 64 | id: "thread-a", | |
| 65 | index: 1, | |
| 66 | items: ["thread-b", "thread-a"], | |
| 67 | newIndex: 0, | |
| 68 | previousItems: ["thread-a", "thread-b"], | |
| 69 | previousContainerId: "pinned-threads", | |
| 70 | transition: { duration: 200, easing: "ease" }, | |
| 71 | wasDragging: true, | |
| 72 | }; | |
| 73 | ||
| 74 | it("does not replay layout movement after the pointer is released", () => { | |
| 75 | expect(defaultAnimateLayoutChanges(baseArgs)).toBe(true); | |
| 76 | expect(animatePinnedLayoutChanges(baseArgs)).toBe(false); | |
| 77 | }); | |
| 78 | ||
| 79 | it("keeps layout movement while the user is sorting", () => { | |
| 80 | expect(animatePinnedLayoutChanges({ ...baseArgs, isSorting: true })).toBe(true); | |
| 81 | }); | |
| 82 | }); | |
| 83 | ||
| 56 | 84 | describe("shouldNavigateAfterProjectRemoval", () => { |
| 57 | 85 | const projectThreads = [{ environmentId: "environment-local", id: "thread-1" }]; |
| 58 | 86 |
apps/web/src/components/Sidebar.logic.ts+7| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | import * as React from "react"; |
| 2 | import { defaultAnimateLayoutChanges, type AnimateLayoutChanges } from "@dnd-kit/sortable"; | |
| 2 | 3 | import type { ContextMenuItem } from "@t3tools/contracts"; |
| 3 | 4 | import type { SidebarProjectSortOrder, SidebarThreadSortOrder } from "@t3tools/contracts/settings"; |
| 4 | 5 | import { |
| ... | ... | @@ -23,6 +24,12 @@ export const THREAD_JUMP_HINT_SHOW_DELAY_MS = 100; |
| 23 | 24 | // it small; cold opens still render instantly from the cached snapshot. |
| 24 | 25 | export const SIDEBAR_THREAD_PREWARM_LIMIT = 3; |
| 25 | 26 | |
| 27 | // The list already reaches its destination through sortable transforms while | |
| 28 | // the pointer is down. dnd-kit's default also animates the committed DOM order | |
| 29 | // after release, replaying the same movement across every affected row. | |
| 30 | export const animatePinnedLayoutChanges: AnimateLayoutChanges = (args) => | |
| 31 | args.isSorting ? defaultAnimateLayoutChanges(args) : false; | |
| 32 | ||
| 26 | 33 | type SidebarProject = { |
| 27 | 34 | id: string; |
| 28 | 35 | title: string; |
apps/web/src/components/Sidebar.tsx+2| ... | ... | @@ -121,6 +121,7 @@ import type { SidebarThreadSummary } from "../types"; |
| 121 | 121 | import { cn } from "~/lib/utils"; |
| 122 | 122 | import { buildThreadActionMenuItems } from "./threadActionMenu.logic"; |
| 123 | 123 | import { |
| 124 | animatePinnedLayoutChanges, | |
| 124 | 125 | buildBulkTitleRegenerationContextMenuItem, |
| 125 | 126 | formatWorkingDurationLabel, |
| 126 | 127 | firstValidTimestampMs, |
| ... | ... | @@ -448,6 +449,7 @@ function SortablePinnedThreadRow(props: { |
| 448 | 449 | }) { |
| 449 | 450 | const { listeners, setNodeRef, transform, transition, isDragging } = useSortable({ |
| 450 | 451 | id: props.id, |
| 452 | animateLayoutChanges: animatePinnedLayoutChanges, | |
| 451 | 453 | }); |
| 452 | 454 | return props.children({ listeners, setNodeRef, transform, transition, isDragging }); |
| 453 | 455 | } |