dbeaver/pro#7566 feat: add settings functionality to the new tree (#4184)

* dbeaver/pro#7566 feat: add settings functionality to the new tree

Implemented settings as a part of Toolbar. The key reason was that the TreeToolbar sets different contexts for all other actions related to tree and easily extendible as a menu service. It might limit in some weird cases, but for now it looks like a valid decision

Settings are made as unknown intentionally to be polymorphic. We could use Record<string, unknown> instead, but I don't see it really improving anything.

* dbeaver/pro#7566 refactor: introduce ITreeSettings interface

add filter with settings connection via useTreeFilterState hook

* dbeaver/pro#7566 refactor: remove redundant hooks, update ITreeSettings

* dbeaver/pro#7566 refactor: move actions to cloudbeaver part

* dbeaver/pro#7566 feat: add universal hook that persists any tree settings via UserDataService as a flat key-value record

* dbeaver/pro#7566 refactor: remove unused childrenTransformers and stateTransformers from ITreeFilterState

* dbeaver/pro#7566 feat: enhance tree settings functionality with replace method and user data synchronization

* dbeaver/pro#7566 refactor: remove toolbarFilter from ITreeFilterState interface

* dbeaver/pro#7566 refactor: simplify useUserTreeSettings

* dbeaver/pro#7566 fix: lint rule and defaults inconsistency

* dbeaver/pro#7566 fix: defaults

* dbeaver/pro#7566 fix: update Russian translations for navigation tree settings

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: sergeyteleshev <iamsergeyteleshev@gmail.com>
This commit is contained in:
Sychev Andrey
2026-03-17 17:21:35 +01:00
committed by GitHub
co-authored by Evgenia sergeyteleshev
parent 051695451b
commit 2abdbce8ff
21 changed files with 344 additions and 17 deletions
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -10,17 +10,19 @@ import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { LeftBarPanelService } from '@cloudbeaver/core-ui';
import { NavigationTreeSettingsService } from '../NavigationTreeSettingsService.js';
import { TreeSettingsMenuService } from '../TreeNew/TreeSettingsMenuService.js';
import { TreeToolbarMenuService } from '../TreeNew/TreeToolbarMenuService.js';
import { ElementsTreeToolsMenuService } from './ElementsTree/ElementsTreeTools/ElementsTreeToolsMenuService.js';
const NavigationTreePanel = importLazyComponent(() => import('./NavigationTreePanel.js').then(m => m.NavigationTreePanel));
@injectable(() => [NavigationTreeSettingsService, ElementsTreeToolsMenuService, TreeToolbarMenuService, LeftBarPanelService])
@injectable(() => [NavigationTreeSettingsService, ElementsTreeToolsMenuService, TreeToolbarMenuService, TreeSettingsMenuService, LeftBarPanelService])
export class NavigationTreeBootstrap extends Bootstrap {
constructor(
private readonly navigationTreeSettingsService: NavigationTreeSettingsService,
private readonly elementsTreeToolsMenuService: ElementsTreeToolsMenuService,
private readonly treeToolbarMenuService: TreeToolbarMenuService,
private readonly treeSettingsMenuService: TreeSettingsMenuService,
private readonly leftBarPanelService: LeftBarPanelService,
) {
super();
@@ -29,6 +31,7 @@ export class NavigationTreeBootstrap extends Bootstrap {
override register(): void {
this.elementsTreeToolsMenuService.register();
this.treeToolbarMenuService.register();
this.treeSettingsMenuService.register();
this.leftBarPanelService.tabsContainer.add({
key: 'navigation-tree-tab',
order: 0,
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -8,11 +8,13 @@
import { createDataContext } from '@cloudbeaver/core-data-context';
import type { ITreeData } from './ITreeData.js';
import type { ITreeFilter } from './useTreeFilter.js';
import type { ITreeFilter, ITreeFilterState } from './useTreeFilter.js';
import type { ITreeSettings } from './useTreeSettings.js';
export type ITreeToolbarFilter = ITreeFilter & { filterPlaceholder?: string };
export type ITreeToolbarFilter = ITreeFilter & ITreeFilterState & { filterPlaceholder?: string };
export type TreeRefreshHandler = () => Promise<void>;
export const DATA_CONTEXT_TREE_DATA = createDataContext<ITreeData>('tree-new-data');
export const DATA_CONTEXT_TREE_FILTER = createDataContext<ITreeToolbarFilter>('tree-new-filter');
export const DATA_CONTEXT_TREE_REFRESH = createDataContext<TreeRefreshHandler>('tree-new-refresh');
export const DATA_CONTEXT_TREE_SETTINGS = createDataContext<ITreeSettings>('tree-new-settings');
@@ -0,0 +1,13 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createMenu } from '@cloudbeaver/core-view';
export const MENU_TREE_SETTINGS = createMenu('tree-settings', {
label: 'plugin_navigation_tree_settings_menu_title',
icon: '/icons/settings_cog_sm.svg',
});
@@ -0,0 +1,82 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import type { IDataContextProvider } from '@cloudbeaver/core-data-context';
import { injectable } from '@cloudbeaver/core-di';
import { ActionService, type IAction, MenuService } from '@cloudbeaver/core-view';
import { DATA_CONTEXT_TREE_SETTINGS } from './DATA_CONTEXT_TREE.js';
import { MENU_TREE_SETTINGS } from './MENU_TREE_SETTINGS.js';
import { ACTION_TREE_SAVE_STATE } from './actions/ACTION_TREE_SAVE_STATE.js';
import { ACTION_TREE_SHOW_DESCRIPTIONS } from './actions/ACTION_TREE_SHOW_DESCRIPTIONS.js';
import { ACTION_TREE_SHOW_FILTER } from './actions/ACTION_TREE_SHOW_FILTER.js';
import { TREE_SETTINGS_FILTER_ENABLED } from './useTreeFilter.js';
export const TREE_SETTINGS_SHOW_DESCRIPTIONS = 'tree.showDescriptions';
export const TREE_SETTINGS_SAVE_STATE = 'tree.saveState';
@injectable(() => [ActionService, MenuService])
export class TreeSettingsMenuService {
constructor(
private readonly actionService: ActionService,
private readonly menuService: MenuService,
) { }
register(): void {
this.actionService.addHandler({
id: 'tree-settings-handler',
menus: [MENU_TREE_SETTINGS],
actions: [ACTION_TREE_SHOW_FILTER, ACTION_TREE_SHOW_DESCRIPTIONS, ACTION_TREE_SAVE_STATE],
contexts: [DATA_CONTEXT_TREE_SETTINGS],
isActionApplicable(context: IDataContextProvider): boolean {
const settings = context.get(DATA_CONTEXT_TREE_SETTINGS);
return settings !== undefined;
},
isChecked(context: IDataContextProvider, action: IAction): boolean {
const settings = context.get(DATA_CONTEXT_TREE_SETTINGS);
if (!settings) {
return false;
}
if (action === ACTION_TREE_SHOW_FILTER) {
return settings.get<boolean>(TREE_SETTINGS_FILTER_ENABLED) ?? false;
}
if (action === ACTION_TREE_SHOW_DESCRIPTIONS) {
return settings.get<boolean>(TREE_SETTINGS_SHOW_DESCRIPTIONS) ?? false;
}
if (action === ACTION_TREE_SAVE_STATE) {
return settings.get<boolean>(TREE_SETTINGS_SAVE_STATE) ?? false;
}
return false;
},
handler(context: IDataContextProvider, action: IAction): void {
const settings = context.get(DATA_CONTEXT_TREE_SETTINGS);
if (!settings) {
return;
}
if (action === ACTION_TREE_SHOW_FILTER) {
settings.set(TREE_SETTINGS_FILTER_ENABLED, !(settings.get<boolean>(TREE_SETTINGS_FILTER_ENABLED) ?? false));
} else if (action === ACTION_TREE_SHOW_DESCRIPTIONS) {
settings.set(TREE_SETTINGS_SHOW_DESCRIPTIONS, !(settings.get<boolean>(TREE_SETTINGS_SHOW_DESCRIPTIONS) ?? false));
} else if (action === ACTION_TREE_SAVE_STATE) {
settings.set(TREE_SETTINGS_SAVE_STATE, !(settings.get<boolean>(TREE_SETTINGS_SAVE_STATE) ?? false));
}
},
});
this.menuService.addCreator({
menus: [MENU_TREE_SETTINGS],
getItems: (context, items) => [...items, ACTION_TREE_SHOW_FILTER, ACTION_TREE_SHOW_DESCRIPTIONS, ACTION_TREE_SAVE_STATE],
});
}
}
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -8,22 +8,37 @@
import React, { use } from 'react';
import { observer } from 'mobx-react-lite';
import { DATA_CONTEXT_TREE_DATA, DATA_CONTEXT_TREE_FILTER, DATA_CONTEXT_TREE_REFRESH, type ITreeToolbarFilter } from './DATA_CONTEXT_TREE.js';
import {
DATA_CONTEXT_TREE_DATA,
DATA_CONTEXT_TREE_FILTER,
DATA_CONTEXT_TREE_REFRESH,
DATA_CONTEXT_TREE_SETTINGS,
type ITreeToolbarFilter,
} from './DATA_CONTEXT_TREE.js';
import type { ITreeSettings } from './useTreeSettings.js';
import { clsx } from '@dbeaver/ui-kit';
import { useCaptureViewContext } from '@cloudbeaver/core-view';
import { TreeDataContext } from './contexts/TreeDataContext.js';
export interface TreeToolbarProps {
settings?: ITreeSettings;
filter?: ITreeToolbarFilter;
onRefresh?: () => Promise<void> | void;
className?: string;
}
export const TreeToolbar = observer<React.PropsWithChildren<TreeToolbarProps>>(function TreeToolbar({ className, children, filter, onRefresh }) {
export const TreeToolbar = observer<React.PropsWithChildren<TreeToolbarProps>>(function TreeToolbar({
className,
children,
settings,
filter,
onRefresh,
}) {
const data = use(TreeDataContext);
useCaptureViewContext((context, id) => {
context.set(DATA_CONTEXT_TREE_DATA, data, id);
context.set(DATA_CONTEXT_TREE_SETTINGS, settings, id);
context.set(DATA_CONTEXT_TREE_FILTER, filter, id);
context.set(DATA_CONTEXT_TREE_REFRESH, onRefresh, id);
});
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ export const TreeToolbarFilter = observer(function TreeToolbarFilter() {
const context = useDataContext(viewContext);
const filter = context.get(DATA_CONTEXT_TREE_FILTER);
if (!filter) {
if (!filter?.enabled) {
return null;
}
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -10,6 +10,7 @@ import { injectable } from '@cloudbeaver/core-di';
import { ACTION_COLLAPSE_ALL, ActionService, type IAction, MenuService } from '@cloudbeaver/core-view';
import { DATA_CONTEXT_TREE_DATA, DATA_CONTEXT_TREE_REFRESH } from './DATA_CONTEXT_TREE.js';
import { MENU_TREE_SETTINGS } from './MENU_TREE_SETTINGS.js';
import { MENU_TREE_TOOLBAR } from './MENU_TREE_TOOLBAR.js';
import { ACTION_TREE_REFRESH } from './actions/ACTION_TREE_REFRESH.js';
@@ -51,7 +52,7 @@ export class TreeToolbarMenuService {
this.menuService.addCreator({
menus: [MENU_TREE_TOOLBAR],
getItems: (_, items) => [...items, ACTION_TREE_REFRESH, ACTION_COLLAPSE_ALL],
getItems: (_, items) => [...items, MENU_TREE_SETTINGS, ACTION_TREE_REFRESH, ACTION_COLLAPSE_ALL],
});
}
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createAction } from '@cloudbeaver/core-view';
export const ACTION_TREE_SAVE_STATE = createAction('tree-save-state', {
label: 'plugin_navigation_tree_settings_save_state',
type: 'checkbox',
});
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createAction } from '@cloudbeaver/core-view';
export const ACTION_TREE_SHOW_DESCRIPTIONS = createAction('tree-show-descriptions', {
label: 'plugin_navigation_tree_settings_show_descriptions',
type: 'checkbox',
});
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createAction } from '@cloudbeaver/core-view';
export const ACTION_TREE_SHOW_FILTER = createAction('tree-show-filter', {
label: 'plugin_navigation_tree_settings_show_filter',
type: 'checkbox',
});
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -12,6 +12,7 @@ import { useObjectRef, useObservableRef } from '@cloudbeaver/core-blocks';
import type { TreeDataTransformer } from './DataTransformers/TreeDataTransformer.js';
import type { ITreeData } from './ITreeData.js';
import type { INodeState } from './INodeState.js';
import type { ITreeSettings } from './useTreeSettings.js';
export interface ITreeFilterOptions {
isNodeMatched?: (nodeId: string, filter: string, isMatched: boolean) => boolean;
@@ -25,7 +26,22 @@ export interface ITreeFilter {
setFilter(filter: string): void;
}
export function useTreeFilter(options: ITreeFilterOptions = {}): Readonly<ITreeFilter> {
export interface ITreeFilterState {
enabled: boolean;
}
export type ITreeFilterWithState = Readonly<ITreeFilter & ITreeFilterState>;
export const TREE_SETTINGS_FILTER_ENABLED = 'tree.filter.enabled';
interface ITreeFilterStateObject extends ITreeFilter, ITreeFilterState {
settings?: ITreeSettings;
}
export function useTreeFilter(
options: ITreeFilterOptions = {},
settings?: ITreeSettings,
): ITreeFilterWithState {
options = useObjectRef(options);
const matchCache = new Map<string, boolean>();
@@ -57,9 +73,13 @@ export function useTreeFilter(options: ITreeFilterOptions = {}): Readonly<ITreeF
return false;
}
return useObservableRef<ITreeFilter>(
return useObservableRef<ITreeFilterStateObject>(
() => ({
settings,
filter: '',
get enabled() {
return this.settings?.get<boolean>(TREE_SETTINGS_FILTER_ENABLED) ?? false;
},
isNodeMatched(treeData: ITreeData, nodeId: string): boolean {
const filter = this.filter.trim();
if (!filter) {
@@ -75,6 +95,10 @@ export function useTreeFilter(options: ITreeFilterOptions = {}): Readonly<ITreeF
return isNodeMatched || treeData.getChildren(nodeId).length > 0;
},
transformer(treeData: ITreeData, nodeId: string, children: string[]): string[] {
if (!this.enabled) {
return children;
}
const filter = this.filter.trim();
if (!filter) {
return children;
@@ -93,6 +117,10 @@ export function useTreeFilter(options: ITreeFilterOptions = {}): Readonly<ITreeF
return children.filter(child => this.isNodeMatched(treeData, child));
},
stateTransformer(treeData: ITreeData, nodeId: string, state: INodeState): INodeState {
if (!this.enabled) {
return state;
}
const filter = this.filter.trim();
if (!filter) {
return state;
@@ -110,9 +138,10 @@ export function useTreeFilter(options: ITreeFilterOptions = {}): Readonly<ITreeF
},
}),
{
settings: observable.ref,
filter: observable.ref,
},
false,
{ settings },
['setFilter', 'isNodeMatched', 'transformer', 'stateTransformer'],
);
}
@@ -0,0 +1,60 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { action, observable } from 'mobx';
import { useObservableRef } from '@cloudbeaver/core-blocks';
export interface ITreeSettings {
get<T>(key: string): T | undefined;
set<T>(key: string, value: T): void;
delete(key: string): void;
clear(): void;
replace(entries: Record<string, unknown>): void;
}
export interface ITreeSettingsOptions {
initialSettings?: Record<string, unknown>;
onChange?(entities: ReadonlyMap<string, unknown>): void;
}
export function useTreeSettings(options: ITreeSettingsOptions = {}): ITreeSettings {
const { initialSettings = {}, onChange } = options;
return useObservableRef(
() => ({
settings: observable.map<string, unknown>(initialSettings),
get<T>(key: string): T | undefined {
return this.settings.get(key) as T | undefined;
},
set<T>(key: string, value: T): void {
this.settings.set(key, value);
onChange?.(this.settings);
},
delete(key: string): void {
this.settings.delete(key);
onChange?.(this.settings);
},
clear(): void {
this.settings.clear();
onChange?.(this.settings);
},
replace(entries: Record<string, unknown>): void {
this.settings.replace(entries);
},
}),
{
settings: observable.ref,
set: action.bound,
delete: action.bound,
clear: action.bound,
replace: action.bound,
},
false,
);
}
@@ -0,0 +1,59 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { useEffect, useRef } from 'react';
import { useUserData } from '@cloudbeaver/core-blocks';
import { type ITreeSettings, useTreeSettings } from './useTreeSettings.js';
function validateRecord(data: unknown): boolean {
return typeof data === 'object' && data !== null && !Array.isArray(data);
}
export function useUserTreeSettings(settingsId: string): ITreeSettings {
const persistedSettingsRef = useRef<Record<string, unknown> | null>(null);
const treeSettingsRef = useRef<ITreeSettings | null>(null);
const persistedSettings = useUserData<Record<string, unknown>>(
settingsId,
() => ({}),
data => {
treeSettingsRef.current?.replace(data);
},
validateRecord,
);
const treeSettings = useTreeSettings({
initialSettings: persistedSettings,
onChange(newSettings) {
const current = persistedSettingsRef.current;
if (!current) {
return;
}
for (const key of Object.keys(current)) {
if (!newSettings.has(key)) {
delete current[key];
}
}
for (const [key, value] of newSettings) {
current[key] = value;
}
},
});
useEffect(() => {
persistedSettingsRef.current = persistedSettings;
treeSettingsRef.current = treeSettings;
});
return treeSettings;
}
@@ -54,6 +54,8 @@ export * from './TreeNew/NodeControlLazy.js';
export * from './TreeNew/useTreeData.js';
export * from './TreeNew/ITreeData.js';
export * from './TreeNew/useTreeFilter.js';
export * from './TreeNew/useTreeSettings.js';
export * from './TreeNew/useUserTreeSettings.js';
export * from './TreeNew/useTreeSelection.js';
export * from './TreeNew/useTreeClickSelection.js';
export * from './TreeNew/ITreeSelection.js';
@@ -65,7 +67,12 @@ export * from './TreeNew/TreeToolbarMenuLazy.js';
export * from './TreeNew/TreeToolbarMenuService.js';
export * from './TreeNew/DATA_CONTEXT_TREE.js';
export * from './TreeNew/MENU_TREE_TOOLBAR.js';
export * from './TreeNew/MENU_TREE_SETTINGS.js';
export * from './TreeNew/actions/ACTION_TREE_REFRESH.js';
export * from './TreeNew/actions/ACTION_TREE_SHOW_FILTER.js';
export * from './TreeNew/actions/ACTION_TREE_SHOW_DESCRIPTIONS.js';
export * from './TreeNew/actions/ACTION_TREE_SAVE_STATE.js';
export * from './TreeNew/TreeSettingsMenuService.js';
export * from './NavigationTree/getNavigationTreeUserSettingsId.js';
export * from './NodesManager/NavNodeView/IFolderTransform.js';
@@ -35,4 +35,8 @@ export default [
['plugin_navigation_tree_settings_disable', 'Disable'],
['plugin_navigation_tree_settings_disable_description', 'Disable navigation tree for all users'],
['plugin_navigation_tree_toolbar_menu_title', 'Tree Toolbar'],
['plugin_navigation_tree_settings_menu_title', 'Tree settings'],
['plugin_navigation_tree_settings_show_filter', 'Show filter'],
['plugin_navigation_tree_settings_show_descriptions', 'Show descriptions'],
['plugin_navigation_tree_settings_save_state', 'Save tree state'],
];
@@ -36,4 +36,5 @@ export default [
['plugin_navigation_tree_settings_disable', 'Désactiver'],
['plugin_navigation_tree_settings_disable_description', 'Désactiver l’arborescence de navigation pour tous les utilisateurs'],
['plugin_navigation_tree_toolbar_menu_title', "Barre d'outils de l'arborescence"],
['plugin_navigation_tree_settings_menu_title', "Paramètres de l'arborescence"],
];
@@ -32,4 +32,5 @@ export default [
['plugin_navigation_tree_settings_disable', 'Disabilita'],
['plugin_navigation_tree_settings_disable_description', "Disabilita l'albero di navigazione per tutti gli utenti"],
['plugin_navigation_tree_toolbar_menu_title', "Barra degli strumenti dell'albero"],
['plugin_navigation_tree_settings_menu_title', "Impostazioni dell'albero"],
];
@@ -38,4 +38,8 @@ export default [
['plugin_navigation_tree_settings_disable', 'Отключить'],
['plugin_navigation_tree_settings_disable_description', 'Отключить дерево навигации для всех пользователей'],
['plugin_navigation_tree_toolbar_menu_title', 'Панель инструментов дерева'],
['plugin_navigation_tree_settings_menu_title', 'Настройки дерева'],
['plugin_navigation_tree_settings_show_filter', 'Показывать фильтр'],
['plugin_navigation_tree_settings_show_descriptions', 'Показывать описания'],
['plugin_navigation_tree_settings_save_state', 'Сохранять состояние дерева'],
];
@@ -35,4 +35,5 @@ export default [
['plugin_navigation_tree_settings_disable', 'Tắt'],
['plugin_navigation_tree_settings_disable_description', 'Tắt cây điều hướng cho tất cả người dùng'],
['plugin_navigation_tree_toolbar_menu_title', 'Menu Thanh công cụ Cây'],
['plugin_navigation_tree_settings_menu_title', 'Cài đặt Cây'],
];
@@ -34,4 +34,5 @@ export default [
['plugin_navigation_tree_settings_disable', 'Disable'],
['plugin_navigation_tree_settings_disable_description', 'Disable navigation tree for all users'],
['plugin_navigation_tree_toolbar_menu_title', '树工具栏菜单'],
['plugin_navigation_tree_settings_menu_title', '树设置'],
];
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ import { ElementsTreeSettingsService } from './NavigationTree/ElementsTree/Eleme
import { ElementsTreeToolsMenuService } from './NavigationTree/ElementsTree/ElementsTreeTools/ElementsTreeToolsMenuService.js';
import { ElementsTreeService } from './NavigationTree/ElementsTree/ElementsTreeService.js';
import { TreeToolbarMenuService } from './TreeNew/TreeToolbarMenuService.js';
import { TreeSettingsMenuService } from './TreeNew/TreeSettingsMenuService.js';
import { LocaleService } from './LocaleService.js';
export default ModuleRegistry.add({
@@ -37,6 +38,7 @@ export default ModuleRegistry.add({
.addSingleton(ElementsTreeSettingsService)
.addSingleton(ElementsTreeToolsMenuService)
.addSingleton(TreeToolbarMenuService)
.addSingleton(TreeSettingsMenuService)
.addSingleton(ElementsTreeService);
},
});