feat(sidebar): add nested virtual table groups with drag and batch move
This commit is contained in:
1 parent
0db9756a57
commit
1f43d184aa
31 files changed
+1714
-20
No files matched your search
@@ -27,6 +27,9 @@ function installApiMocks() {
|
||||
saveEditorSettings: vi.fn().mockResolvedValue(undefined),
|
||||
saveSidebarLayout: vi.fn().mockResolvedValue(undefined),
|
||||
loadSidebarLayout: vi.fn().mockResolvedValue(null),
|
||||
loadTableVGroups: vi.fn().mockResolvedValue({}),
|
||||
saveTableVGroups: vi.fn().mockResolvedValue(undefined),
|
||||
deleteTableVGroupsForConnection: vi.fn().mockResolvedValue(undefined),
|
||||
connectionDatabaseInfo: vi.fn().mockResolvedValue(undefined),
|
||||
listInstalledAgents: vi.fn().mockResolvedValue([]),
|
||||
sessionCredentialStatus: vi.fn().mockResolvedValue(false),
|
||||
|
||||
@@ -230,6 +230,7 @@ describe("connectionStore timeout recovery", () => {
|
||||
loadConnections,
|
||||
loadPinnedTreeNodeIds: vi.fn().mockResolvedValue([]),
|
||||
loadSidebarLayout: vi.fn().mockResolvedValue(null),
|
||||
loadTableVGroups: vi.fn().mockResolvedValue({}),
|
||||
loadTunnelProfiles: vi.fn().mockResolvedValue([]),
|
||||
saveConnections: vi.fn().mockResolvedValue(undefined),
|
||||
saveEditorSettings,
|
||||
@@ -276,6 +277,7 @@ describe("connectionStore timeout recovery", () => {
|
||||
.mockResolvedValue([postgresConnection({ id: "default", connect_timeout_secs: 10, query_timeout_secs: 30 }), postgresConnection({ id: "custom", connect_timeout_secs: 45, query_timeout_secs: 300 }), postgresConnection({ id: "inherited", connect_timeout_secs: 60, query_timeout_secs: 60 })]),
|
||||
loadPinnedTreeNodeIds: vi.fn().mockResolvedValue([]),
|
||||
loadSidebarLayout: vi.fn().mockResolvedValue(null),
|
||||
loadTableVGroups: vi.fn().mockResolvedValue({}),
|
||||
loadTunnelProfiles: vi.fn().mockResolvedValue([]),
|
||||
saveConnections,
|
||||
saveEditorSettings: vi.fn().mockResolvedValue(undefined),
|
||||
@@ -318,6 +320,7 @@ describe("connectionStore timeout recovery", () => {
|
||||
loadConnections: vi.fn().mockResolvedValue([postgresConnection({ id: "local", connect_timeout_secs: 10, query_timeout_secs: 30 })]),
|
||||
loadPinnedTreeNodeIds: vi.fn().mockResolvedValue([]),
|
||||
loadSidebarLayout: vi.fn().mockResolvedValue(null),
|
||||
loadTableVGroups: vi.fn().mockResolvedValue({}),
|
||||
loadTunnelProfiles: vi.fn().mockResolvedValue([]),
|
||||
saveConnections: vi.fn().mockResolvedValue(undefined),
|
||||
saveEditorSettings: vi.fn().mockResolvedValue(undefined),
|
||||
@@ -353,6 +356,7 @@ describe("connectionStore timeout recovery", () => {
|
||||
loadConnections: vi.fn().mockResolvedValue([postgresConnection({ id: "inherited", connect_timeout_secs: 7, query_timeout_secs: 12 })]),
|
||||
loadPinnedTreeNodeIds: vi.fn().mockResolvedValue([]),
|
||||
loadSidebarLayout: vi.fn().mockResolvedValue(null),
|
||||
loadTableVGroups: vi.fn().mockResolvedValue({}),
|
||||
loadTunnelProfiles: vi.fn().mockResolvedValue([]),
|
||||
saveConnections: vi.fn().mockResolvedValue(undefined),
|
||||
saveEditorSettings: vi.fn().mockResolvedValue(undefined),
|
||||
@@ -384,6 +388,7 @@ describe("connectionStore timeout recovery", () => {
|
||||
loadConnections: vi.fn().mockResolvedValue([postgresConnection({ id: "inherited", connect_timeout_secs: 20, query_timeout_secs: 45 })]),
|
||||
loadPinnedTreeNodeIds: vi.fn().mockResolvedValue([]),
|
||||
loadSidebarLayout: vi.fn().mockResolvedValue(null),
|
||||
loadTableVGroups: vi.fn().mockResolvedValue({}),
|
||||
loadTunnelProfiles: vi.fn().mockResolvedValue([]),
|
||||
saveConnections: vi.fn().mockResolvedValue(undefined),
|
||||
saveEditorSettings: vi.fn().mockResolvedValue(undefined),
|
||||
@@ -426,6 +431,7 @@ describe("connectionStore timeout recovery", () => {
|
||||
loadConnections: vi.fn().mockResolvedValue([postgresConnection({ id: "inherited", connect_timeout_secs: 99, connect_timeout_inherit: true, query_timeout_secs: 99, query_timeout_inherit: true })]),
|
||||
loadPinnedTreeNodeIds: vi.fn().mockResolvedValue([]),
|
||||
loadSidebarLayout: vi.fn().mockResolvedValue(null),
|
||||
loadTableVGroups: vi.fn().mockResolvedValue({}),
|
||||
loadTunnelProfiles: vi.fn().mockResolvedValue([]),
|
||||
saveConnections: vi.fn().mockResolvedValue(undefined),
|
||||
saveEditorSettings: vi.fn().mockResolvedValue(undefined),
|
||||
@@ -462,6 +468,7 @@ describe("connectionStore timeout recovery", () => {
|
||||
loadConnections: vi.fn().mockResolvedValue([postgresConnection()]),
|
||||
loadPinnedTreeNodeIds: vi.fn().mockResolvedValue([]),
|
||||
loadSidebarLayout: vi.fn().mockResolvedValue(null),
|
||||
loadTableVGroups: vi.fn().mockResolvedValue({}),
|
||||
loadTunnelProfiles: vi.fn().mockResolvedValue([]),
|
||||
saveConnections: vi.fn().mockResolvedValue(undefined),
|
||||
saveEditorSettings: vi.fn().mockResolvedValue(undefined),
|
||||
@@ -488,6 +495,7 @@ describe("connectionStore timeout recovery", () => {
|
||||
loadConnections: vi.fn().mockResolvedValue([postgresConnection()]),
|
||||
loadPinnedTreeNodeIds: vi.fn().mockResolvedValue([]),
|
||||
loadSidebarLayout: vi.fn().mockResolvedValue(null),
|
||||
loadTableVGroups: vi.fn().mockResolvedValue({}),
|
||||
loadTunnelProfiles: vi.fn().mockResolvedValue([]),
|
||||
saveConnections: vi.fn().mockResolvedValue(undefined),
|
||||
saveEditorSettings: vi.fn().mockResolvedValue(undefined),
|
||||
|
||||
@@ -66,6 +66,30 @@ import {
|
||||
type DropPosition,
|
||||
type ReorderEntriesOptions,
|
||||
} from "@/lib/sidebar/sidebarLayout";
|
||||
import {
|
||||
applyTableVGroupsToChildren,
|
||||
collectTableTreeNames,
|
||||
createTableVGroup as createTableVGroupOp,
|
||||
deleteTableVGroups as deleteTableVGroupsOp,
|
||||
emptyTableVGroupLayout,
|
||||
findTableVGroupContainerNode,
|
||||
hasTableTreeLoadMore,
|
||||
hasTableVGroupEntries,
|
||||
moveTableToVGroup as moveTableToVGroupOp,
|
||||
normalizeTableVGroupLayout as normalizeTableVGroupLayoutOp,
|
||||
pruneTableVGroupMembers as pruneTableVGroupMembersOp,
|
||||
reorderTableVGroupEntry as reorderTableVGroupEntryOp,
|
||||
renameTableVGroup as renameTableVGroupOp,
|
||||
resolveTableVGroupScopeFromNode,
|
||||
setTableVGroupsEnabled as setTableVGroupsEnabledOp,
|
||||
stripTableVGroupsFromChildren,
|
||||
tableVGroupPathForTable as tableVGroupPathForTableOp,
|
||||
tableVGroupScopeKey,
|
||||
toggleTableVGroupCollapsed as toggleTableVGroupCollapsedOp,
|
||||
type TableVGroupDropPosition,
|
||||
type TableVGroupLayout,
|
||||
type TableVGroupScope,
|
||||
} from "@/lib/table/tableVGroup";
|
||||
import {
|
||||
buildConnectionConfigBundle,
|
||||
filterSidebarLayoutByConnectionIds,
|
||||
@@ -601,6 +625,9 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
allDatabases?: boolean;
|
||||
} | null>(null);
|
||||
const sidebarLayout = ref<SidebarLayout>(emptyLayout());
|
||||
const tableVGroupLayouts = ref<Record<string, TableVGroupLayout>>({});
|
||||
const dirtyTableVGroupScopeKeys = new Set<string>();
|
||||
let tableVGroupPersistTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
const connectionGroupPaths = computed(() => buildConnectionGroupPathMap(sidebarLayout.value));
|
||||
const connectionGroupOptions = computed(() => connectionGroupDestinationRows(sidebarLayout.value));
|
||||
const selectedConnectionGroupId = computed(() => {
|
||||
@@ -1789,6 +1816,9 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
}
|
||||
children = children.map((child) => {
|
||||
const old = oldMap.get(child.id);
|
||||
// Virtual group containers are rebuilt from the layout on every projection;
|
||||
// stale copies must never override the freshly computed arrangement.
|
||||
if (child.type === "table-vgroup") return child;
|
||||
if (old?.isLoading) {
|
||||
const isExpanded = old.isExpanded;
|
||||
const isLoading = old.isLoading;
|
||||
@@ -1823,6 +1853,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
persistPinnedTreeNodeIds();
|
||||
}
|
||||
syncPinnedTreeState(children);
|
||||
children = applyTableVGroupsToChildren(children, tableVGroupLayoutForNode(parent), parent);
|
||||
parent.children = markRawLeafTreeNodes(children);
|
||||
loadedTreeNodeChildrenIds.value.add(parent.id);
|
||||
syncConfirmedEmptyTreeNodeId(parent);
|
||||
@@ -2738,7 +2769,9 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
}
|
||||
|
||||
async function savePersistedTreeChildren(cacheKey: string, children: TreeNode[]) {
|
||||
await api.saveSchemaCache(cacheKey, encodeSchemaTreeCache(stripTreeNodeExpansionState(stripDatabaseSavedSqlTreeNodes(children)))).catch(() => undefined);
|
||||
// 分组是显示层投影,绝不写入元数据缓存——否则重启后会随缓存复现。
|
||||
const cacheChildren = stripTableVGroupsFromChildren(children);
|
||||
await api.saveSchemaCache(cacheKey, encodeSchemaTreeCache(stripTreeNodeExpansionState(stripDatabaseSavedSqlTreeNodes(cacheChildren)))).catch(() => undefined);
|
||||
}
|
||||
|
||||
function sidebarTableSearchTreeCacheKey(parent: TreeNode): string | null {
|
||||
@@ -3561,9 +3594,26 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
for (const id of removedIds) nextLayout = removeConnectionFromSidebarLayout(nextLayout, id);
|
||||
await persistConnectionDeletion(nextConnections, nextLayout);
|
||||
applyConnectionRemoval(removedIds, nextConnections, nextLayout);
|
||||
purgeTableVGroupsForConnections(removedIds);
|
||||
await cleanupRemovedOneTimeConnections(oneTimeIds);
|
||||
}
|
||||
|
||||
/** 删除连接后同步清理其名下的表分组布局(本地内存 + dbx.db 行)。 */
|
||||
function purgeTableVGroupsForConnections(removedIds: ReadonlySet<string>) {
|
||||
const nextLayouts = { ...tableVGroupLayouts.value };
|
||||
let removed = false;
|
||||
for (const id of removedIds) {
|
||||
const prefix = `${id}\u0000`;
|
||||
for (const key of Object.keys(nextLayouts)) {
|
||||
if (key.startsWith(prefix)) {
|
||||
delete nextLayouts[key];
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed) tableVGroupLayouts.value = nextLayouts;
|
||||
for (const id of removedIds) void api.deleteTableVGroupsForConnection(id).catch(() => {});
|
||||
}
|
||||
function applyConnectionRemoval(removedIds: ReadonlySet<string>, nextConnections: ConnectionConfig[], nextLayout: SidebarLayout) {
|
||||
connections.value = nextConnections;
|
||||
syncTimeoutInheritanceBackup();
|
||||
@@ -5972,6 +6022,8 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
setChildren(targetNode, children);
|
||||
if (!searchFilter && !isSidebarTableSearch && !tableNameFilter) {
|
||||
await savePersistedTreeChildren(cacheKey, children);
|
||||
// grouped 显示下这里加载的是分组占位节点;xugu 协议命名空间的空列表也不代表表被删。
|
||||
pruneTableVGroupStaleMembers(targetNode, children, simpleObjectDisplay && !isPublicSynonymScope && !isSchedulerJobScope);
|
||||
}
|
||||
const currentTargetNode = treeNodeLoadTarget(load);
|
||||
if (!currentTargetNode) return;
|
||||
@@ -6128,6 +6180,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
options?.onChildrenApplied?.(targetNode);
|
||||
if (!searchFilter && !isSidebarTableSearch && !tableNameFilter) {
|
||||
await savePersistedTreeChildren(cacheKey, children);
|
||||
pruneTableVGroupStaleMembers(targetNode, children, targetNode.type === "group-tables");
|
||||
}
|
||||
const currentTargetNode = treeNodeLoadTarget(load);
|
||||
if (currentTargetNode) currentTargetNode.isExpanded = true;
|
||||
@@ -6237,6 +6290,8 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
if (!options?.searchFilter) {
|
||||
await savePersistedTreeChildren(schemaCacheKey(parentConnectionId, parentDatabase, parent.schema || "", ownerAwareMetadataCacheVersion(config, "objects-simple-v8")), nextChildren);
|
||||
}
|
||||
// 该分支只服务 simple 库/模式表列表;搜索分页结果不是全量,不能作为成员依据。
|
||||
if (!page.hasMore && !options?.searchFilter) pruneTableVGroupStaleMembers(targetParent, nextChildren, true);
|
||||
const currentTargetParent = treeNodeLoadRelatedTarget(load, parent);
|
||||
if (currentTargetParent && parentEpoch.isCurrent()) currentTargetParent.isExpanded = true;
|
||||
return;
|
||||
@@ -6303,6 +6358,9 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
setChildren(targetParent, nextChildren);
|
||||
if (!options?.searchFilter) {
|
||||
await savePersistedTreeChildren(objectGroupCacheKey(targetParent), nextChildren);
|
||||
// 只认 group-tables:group-views 等分组与表共用 scope_key,其列表不是全量表;
|
||||
// 分页中间态(含 load-more)由 prune 内部再挡一次。
|
||||
pruneTableVGroupStaleMembers(targetParent, nextChildren, parent.type === "group-tables");
|
||||
}
|
||||
const currentTargetParent = treeNodeLoadRelatedTarget(load, parent);
|
||||
if (currentTargetParent && parentEpoch.isCurrent()) currentTargetParent.isExpanded = true;
|
||||
@@ -8765,6 +8823,72 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
}, 300);
|
||||
}
|
||||
|
||||
/** 表分组的 scope 一律从「解析到的容器」派生:simple 显示模式下表行带 schema
|
||||
* 而容器不带,直接用行字段算 key 会把读写劈成两份。入参可以是树行,也可以是
|
||||
* 只带身份字段的纯 scope 值(action 参数),后者按字段匹配容器。 */
|
||||
function resolveTableVGroupScope(node: TreeNode | TableVGroupScope): { scope: TableVGroupScope; scopeKey: string } | null {
|
||||
const scope = resolveTableVGroupScopeFromNode(treeNodes.value, node);
|
||||
const scopeKey = tableVGroupScopeKey(scope);
|
||||
return scopeKey ? { scope, scopeKey } : null;
|
||||
}
|
||||
|
||||
function tableVGroupLayoutForNode(node: TreeNode): TableVGroupLayout | undefined {
|
||||
const resolved = resolveTableVGroupScope(node);
|
||||
return resolved ? tableVGroupLayouts.value[resolved.scopeKey] : undefined;
|
||||
}
|
||||
|
||||
function scheduleTableVGroupPersistFlush() {
|
||||
if (tableVGroupPersistTimer) clearTimeout(tableVGroupPersistTimer);
|
||||
tableVGroupPersistTimer = setTimeout(() => {
|
||||
tableVGroupPersistTimer = null;
|
||||
for (const key of dirtyTableVGroupScopeKeys) {
|
||||
const layout = tableVGroupLayouts.value[key];
|
||||
if (layout) api.saveTableVGroups(key, layout).catch(() => {});
|
||||
}
|
||||
dirtyTableVGroupScopeKeys.clear();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
/** 分组节点是显示层投影:按已解析的作用域重建容器子节点,容器里不留投影副本。 */
|
||||
function reprojectTableVGroupScope(scope: TableVGroupScope, scopeKey: string, tableName?: string) {
|
||||
const container = findTableVGroupContainerNode(treeNodes.value, scope, tableName);
|
||||
if (!container?.children) return;
|
||||
container.children = applyTableVGroupsToChildren(stripTableVGroupsFromChildren(container.children), tableVGroupLayouts.value[scopeKey], scope);
|
||||
}
|
||||
|
||||
/** 表分组成员回收:只有「本 scope 的完整表列表」才能判定成员存亡。
|
||||
* 同一 scope_key 下并存多种列表:grouped 的各对象分组(视图/函数…只含非表行)、
|
||||
* simple 的库/模式表列表、以及分页中间态。非表列表或分页结果作为依据会把有效
|
||||
* 成员误删并持久化,故列表语义由调用点声明(`completeTableList`),本函数只复核分页态。 */
|
||||
function pruneTableVGroupStaleMembers(parent: TreeNode, children: TreeNode[], completeTableList: boolean) {
|
||||
if (!completeTableList) return;
|
||||
const resolved = resolveTableVGroupScope(parent);
|
||||
const layout = resolved ? tableVGroupLayouts.value[resolved.scopeKey] : undefined;
|
||||
if (!resolved || !hasTableVGroupEntries(layout)) return;
|
||||
if (hasTableTreeLoadMore(children)) return;
|
||||
const keepNames = collectTableTreeNames(stripTableVGroupsFromChildren(children));
|
||||
const next = pruneTableVGroupMembersOp(layout, keepNames);
|
||||
if (next === layout) return;
|
||||
updateTableVGroupLayout(resolved.scope, resolved.scopeKey, next);
|
||||
}
|
||||
|
||||
/** 写入某作用域的布局:即时重投影 + 合并 300ms 后落盘。作用域须已解析出 scopeKey。 */
|
||||
function updateTableVGroupLayout(scope: TableVGroupScope, scopeKey: string, nextLayout: TableVGroupLayout, tableName?: string) {
|
||||
tableVGroupLayouts.value = { ...tableVGroupLayouts.value, [scopeKey]: nextLayout };
|
||||
dirtyTableVGroupScopeKeys.add(scopeKey);
|
||||
scheduleTableVGroupPersistFlush();
|
||||
reprojectTableVGroupScope(scope, scopeKey, tableName);
|
||||
}
|
||||
|
||||
/** 分组变更的统一入口:读当前布局 → 变换 → 写回。作用域不可解析或尚无布局时跳过。 */
|
||||
function updateTableVGroupLayoutFor(scope: TableVGroupScope, transform: (layout: TableVGroupLayout) => TableVGroupLayout, tableName?: string) {
|
||||
const resolved = resolveTableVGroupScope(scope);
|
||||
if (!resolved) return;
|
||||
const current = tableVGroupLayouts.value[resolved.scopeKey];
|
||||
if (!current) return;
|
||||
updateTableVGroupLayout(resolved.scope, resolved.scopeKey, transform(current), tableName);
|
||||
}
|
||||
|
||||
function rebuildTreeNodes() {
|
||||
const existingNodesMap = new Map<string, TreeNode>();
|
||||
const collectExisting = (nodes: TreeNode[]) => {
|
||||
@@ -8828,6 +8952,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
await persistConnectionDeletion(nextConnections, nextLayout);
|
||||
if (removedConnectionIds.size) {
|
||||
applyConnectionRemoval(removedConnectionIds, nextConnections, nextLayout);
|
||||
purgeTableVGroupsForConnections(removedConnectionIds);
|
||||
} else {
|
||||
sidebarLayout.value = nextLayout;
|
||||
rebuildTreeNodes();
|
||||
@@ -9289,7 +9414,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
await settingsStore.initEditorSettings();
|
||||
if (!initFromDiskPromise) {
|
||||
initFromDiskPromise = (async () => {
|
||||
const [pinnedOrder, saved] = await Promise.all([loadPinnedTreeNodeOrder(), api.loadConnections(), tunnelProfileStore.init()]);
|
||||
const [pinnedOrder, saved, , loadedTableVGroups] = await Promise.all([loadPinnedTreeNodeOrder(), api.loadConnections(), tunnelProfileStore.init(), api.loadTableVGroups()]);
|
||||
setPinnedTreeNodeOrder(pinnedOrder);
|
||||
await migrateTimeoutInheritance(saved);
|
||||
const loadedConnections = saved.map(normalizeConnection);
|
||||
@@ -9303,6 +9428,8 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
await persistConnections();
|
||||
}
|
||||
syncTimeoutInheritanceBackup();
|
||||
// 旧版本写下的行可能缺字段(如 version),一律归一化后再进内存,避免脏数据被原样写回。
|
||||
tableVGroupLayouts.value = Object.fromEntries(Object.entries(loadedTableVGroups ?? {}).map(([scopeKey, layout]) => [scopeKey, normalizeTableVGroupLayoutOp(layout)]));
|
||||
const savedLayout = await api.loadSidebarLayout();
|
||||
const currentLayout = sidebarLayout.value.groups.length || sidebarLayout.value.order.length ? sidebarLayout.value : null;
|
||||
sidebarLayout.value = reconcileLayout(
|
||||
@@ -9584,5 +9711,40 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
const layout = reorderEntriesOp(sidebarLayout.value, draggedIds, targetId, position, options);
|
||||
if (layout !== sidebarLayout.value) updateLayoutAndRebuild(layout);
|
||||
},
|
||||
tableVGroupLayouts,
|
||||
resolveTableVGroupScope,
|
||||
tableVGroupLayoutFor(scope: TableVGroupScope) {
|
||||
const resolved = resolveTableVGroupScope(scope);
|
||||
return resolved ? tableVGroupLayouts.value[resolved.scopeKey] : undefined;
|
||||
},
|
||||
createTableVGroup(scope: TableVGroupScope, name: string, parentGroupId?: string | null) {
|
||||
const resolved = resolveTableVGroupScope(scope);
|
||||
if (!resolved) return null;
|
||||
const result = createTableVGroupOp(tableVGroupLayouts.value[resolved.scopeKey] ?? emptyTableVGroupLayout(), name, parentGroupId);
|
||||
updateTableVGroupLayout(resolved.scope, resolved.scopeKey, result.layout);
|
||||
return result.groupId;
|
||||
},
|
||||
renameTableVGroup(scope: TableVGroupScope, groupId: string, name: string) {
|
||||
updateTableVGroupLayoutFor(scope, (layout) => renameTableVGroupOp(layout, groupId, name));
|
||||
},
|
||||
deleteTableVGroups(scope: TableVGroupScope, groupIds: Iterable<string>) {
|
||||
updateTableVGroupLayoutFor(scope, (layout) => deleteTableVGroupsOp(layout, groupIds));
|
||||
},
|
||||
moveTableToVGroup(scope: TableVGroupScope, tableName: string, groupId: string | null) {
|
||||
updateTableVGroupLayoutFor(scope, (layout) => moveTableToVGroupOp(layout, tableName, groupId), tableName);
|
||||
},
|
||||
reorderTableVGroupEntry(scope: TableVGroupScope, draggedEntryId: string, targetEntryId: string, position: TableVGroupDropPosition) {
|
||||
updateTableVGroupLayoutFor(scope, (layout) => reorderTableVGroupEntryOp(layout, draggedEntryId, targetEntryId, position));
|
||||
},
|
||||
toggleTableVGroupCollapsed(scope: TableVGroupScope, groupId: string) {
|
||||
updateTableVGroupLayoutFor(scope, (layout) => toggleTableVGroupCollapsedOp(layout, groupId));
|
||||
},
|
||||
setTableVGroupsEnabled(scope: TableVGroupScope, enabled: boolean) {
|
||||
updateTableVGroupLayoutFor(scope, (layout) => setTableVGroupsEnabledOp(layout, enabled));
|
||||
},
|
||||
tableVGroupPathForTable(scope: TableVGroupScope, tableName: string) {
|
||||
const resolved = resolveTableVGroupScope(scope);
|
||||
return tableVGroupPathForTableOp(resolved ? tableVGroupLayouts.value[resolved.scopeKey] : undefined, tableName);
|
||||
},
|
||||
};
|
||||
});
|
||||
Reference in new issue
Block a user