diff --git a/src/lib/pinnedItems.ts b/src/lib/pinnedItems.ts index 62f7ab2ae6..f5d687e682 100644 --- a/src/lib/pinnedItems.ts +++ b/src/lib/pinnedItems.ts @@ -1,3 +1,5 @@ +import type { TreeNode } from "@/types/database"; + export function orderPinnedFirst(items: T[], isPinned: (item: T) => boolean): T[] { const pinned: T[] = []; const unpinned: T[] = []; @@ -9,3 +11,14 @@ export function orderPinnedFirst(items: T[], isPinned: (item: T) => boolean): return [...pinned, ...unpinned]; } + +export function applyPinnedTreeNodeState(nodes: TreeNode[], pinnedIds: Set): TreeNode[] { + return orderPinnedFirst( + nodes.map((node) => ({ + ...node, + pinned: pinnedIds.has(node.id), + children: node.children ? applyPinnedTreeNodeState(node.children, pinnedIds) : node.children, + })), + (node) => !!node.pinned, + ); +} diff --git a/src/stores/connectionStore.ts b/src/stores/connectionStore.ts index fdbdf4d1ea..96337a8533 100644 --- a/src/stores/connectionStore.ts +++ b/src/stores/connectionStore.ts @@ -2,7 +2,7 @@ import { defineStore } from "pinia"; import { uuid } from "@/lib/utils"; import { ref, computed, watch } from "vue"; import type { ColumnInfo, ConnectionConfig, SidebarLayout, TreeNode } from "@/types/database"; -import { orderPinnedFirst } from "@/lib/pinnedItems"; +import { applyPinnedTreeNodeState, orderPinnedFirst } from "@/lib/pinnedItems"; import { reconcileLayout, buildTreeNodesFromLayout, @@ -232,13 +232,8 @@ export const useConnectionStore = defineStore("connection", () => { return pinnedTreeNodeIds.value.has(id); } - function pinTreeNode(node: TreeNode): TreeNode { - node.pinned = isTreeNodePinned(node.id); - return node; - } - function setChildren(parent: TreeNode, children: TreeNode[]) { - parent.children = orderPinnedFirst(children.map(pinTreeNode), (node) => !!node.pinned); + parent.children = applyPinnedTreeNodeState(children, pinnedTreeNodeIds.value); loadedTreeNodeChildrenIds.value.add(parent.id); } diff --git a/tests/pinnedItems.test.ts b/tests/pinnedItems.test.ts index 68f834f577..1ab334883c 100644 --- a/tests/pinnedItems.test.ts +++ b/tests/pinnedItems.test.ts @@ -1,6 +1,7 @@ import { strict as assert } from "node:assert"; import test from "node:test"; -import { orderPinnedFirst } from "../src/lib/pinnedItems.ts"; +import { applyPinnedTreeNodeState, orderPinnedFirst } from "../src/lib/pinnedItems.ts"; +import type { TreeNode } from "../src/types/database.ts"; test("orders pinned items before unpinned items without changing relative order", () => { const items = [ @@ -16,3 +17,24 @@ test("orders pinned items before unpinned items without changing relative order" assert.deepEqual(ordered.map((item) => item.id), ["b", "d", "a", "c", "e"]); assert.deepEqual(items.map((item) => item.id), ["a", "b", "c", "d", "e"]); }); + +test("applies pinned state recursively to grouped tree nodes", () => { + const nodes: TreeNode[] = [ + { + id: "conn:db:__tables", + label: "tree.tables", + type: "group-tables", + children: [ + { id: "conn:db:orders", label: "orders", type: "table" }, + { id: "conn:db:users", label: "users", type: "table" }, + ], + }, + ]; + + const result = applyPinnedTreeNodeState(nodes, new Set(["conn:db:users"])); + + assert.equal(result[0].children?.[0].id, "conn:db:users"); + assert.equal(result[0].children?.[0].pinned, true); + assert.equal(result[0].children?.[1].id, "conn:db:orders"); + assert.equal(result[0].children?.[1].pinned, false); +});