CB-2647 refactor: use new context menu API for top app bar

This commit is contained in:
Aleksey Potsetsuev
2022-10-31 21:43:16 +03:00
parent 35f4cf524e
commit 0fc139d42c
108 changed files with 1057 additions and 299 deletions
@@ -1,11 +1,4 @@
export default [
['app_shared_connectionMenu_newConnection', 'New connection'],
['app_shared_connectionMenu_connection', 'Connection'],
['app_shared_connectionMenu_custom', 'Manual'],
['app_shared_connectionMenu_disconnect', 'Disconnect All'],
['app_shared_toolsMenu_tools', 'Tools'],
['app_shared_toolsMenu_logViewer', 'Log viewer'],
['app_shared_toolsMenu_logViewer_placeholder', 'Log viewer is not active'],
['app_shared_settingsMenu_config', 'Configuration'],
['app_shared_settingsMenu_theme', 'Theme'],
['app_shared_settingsMenu_lang', 'Language'],
@@ -1,11 +1,4 @@
export default [
['app_shared_connectionMenu_newConnection', 'Nuova connessione'],
['app_shared_connectionMenu_connection', 'Connessione'],
['app_shared_connectionMenu_custom', 'Manuale'],
['app_shared_connectionMenu_disconnect', 'Scollegati da tutto'],
['app_shared_toolsMenu_tools', 'Strumenti'],
['app_shared_toolsMenu_logViewer', 'Visualizzatore del Log'],
['app_shared_toolsMenu_logViewer_placeholder', 'Il visualizzatore del log non è attivo'],
['app_shared_settingsMenu_config', 'Configurazione'],
['app_shared_settingsMenu_theme', 'Tema'],
['app_shared_settingsMenu_lang', 'Lingua'],
@@ -1,10 +1,4 @@
export default [
['app_shared_connectionMenu_connection', 'Подключение'],
['app_shared_connectionMenu_custom', 'Создать...'],
['app_shared_connectionMenu_disconnect', 'Отключить все'],
['app_shared_toolsMenu_tools', 'Инструменты'],
['app_shared_toolsMenu_logViewer', 'Просмотр лога'],
['app_shared_toolsMenu_logViewer_placeholder', 'Просмотр логов неактивен'],
['app_shared_settingsMenu_config', 'Настройки'],
['app_shared_settingsMenu_theme', 'Тема'],
['app_shared_settingsMenu_lang', 'Язык'],
@@ -1,11 +1,4 @@
export default [
['app_shared_connectionMenu_newConnection', '新连接'],
['app_shared_connectionMenu_connection', '连接'],
['app_shared_connectionMenu_custom', '手动'],
['app_shared_connectionMenu_disconnect', '断开所有连接'],
['app_shared_toolsMenu_tools', '工具'],
['app_shared_toolsMenu_logViewer', '日志查看器'],
['app_shared_toolsMenu_logViewer_placeholder', '日志查看器未激活'],
['app_shared_settingsMenu_config', '配置'],
['app_shared_settingsMenu_theme', '主题'],
['app_shared_settingsMenu_lang', '语言'],
@@ -9,4 +9,4 @@
import type { UserInfo } from '@cloudbeaver/core-sdk';
import { createDataContext } from '@cloudbeaver/core-view';
export const DATA_CONTEXT_USER = createDataContext<UserInfo | null>('user-info', null);
export const DATA_CONTEXT_USER = createDataContext<UserInfo | null>('user-info', () => null);
@@ -11,16 +11,14 @@ import { observer } from 'mobx-react-lite';
import { useState, useEffect, useContext, useRef } from 'react';
import styled, { use } from 'reshadow';
import type { ComponentStyle } from '@cloudbeaver/core-theming';
import { uuid } from '@cloudbeaver/core-utils';
import { ILoadableState, uuid } from '@cloudbeaver/core-utils';
import { Button } from '../Button';
import { ExceptionMessage } from '../ExceptionMessage';
import { Translate } from '../localization/Translate';
import { StaticImage } from '../StaticImage';
import { useStyles } from '../useStyles';
import type { ILoadableState } from './ILoadableState';
import { ILoaderContext, LoaderContext } from './LoaderContext';
import { loaderStyles, overlayStyles } from './loaderStyles';
@@ -276,11 +274,3 @@ export const Loader = observer<Props>(function Loader({
</LoaderContext.Provider>
);
});
export function isContainsException(exception?: Error[] | Error | null): boolean {
if (Array.isArray(exception)) {
return exception.some(Boolean);
}
return !!exception;
}
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
import type { ILoadableState } from './ILoadableState';
import type { ILoadableState } from '@cloudbeaver/core-utils';
export type LoaderState = ILoadableState | {
loading: boolean;
@@ -8,18 +8,43 @@
import { useEffect } from 'react';
import type { ILoadableState } from './ILoadableState';
import { ILoadableState, isLoadableStateHasException } from '@cloudbeaver/core-utils';
import { getComputed } from '../getComputed';
export interface IAutoLoadable extends ILoadableState {
load: () => void;
}
export function useAutoLoad(state: IAutoLoadable, enabled = true) {
const canBeLoaded = enabled && !state.isLoaded();
export function useAutoLoad(state: IAutoLoadable | IAutoLoadable[], enabled = true) {
if (!Array.isArray(state)) {
state = [state];
}
for (const loader of state as IAutoLoadable[]) {
getComputed( // activate mobx subscriptions
() => (
(
!loader.isLoaded()
|| loader.isOutdated?.() === true
) && !isLoadableStateHasException(loader)
)
);
}
useEffect(() => {
if (canBeLoaded) {
state.load();
if (!enabled) {
return;
}
for (const loader of state as IAutoLoadable[]) {
if (isLoadableStateHasException(loader)) {
continue;
}
if (!loader.isLoaded() || loader.isOutdated?.() === true) {
loader.load();
}
}
});
}
-1
View File
@@ -18,7 +18,6 @@ export * from './layout/TopAppBar';
export * from './Loader/Loader';
export * from './Loader/useAutoLoad';
export * from './Loader/ILoadableState';
export * from './localization/Translate';
export * from './localization/useTranslate';
@@ -11,9 +11,9 @@ import { useEffect, useState } from 'react';
import { IServiceConstructor, useService } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
import { CachedDataResource, CachedDataResourceContext, CachedDataResourceGetter, CachedDataResourceParam, CachedResourceData, CachedResourceIncludeArgs, isResourceKeyList } from '@cloudbeaver/core-sdk';
import type { ILoadableState } from '@cloudbeaver/core-utils';
import { getComputed } from './getComputed';
import type { ILoadableState } from './Loader/ILoadableState';
import { useObjectRef } from './useObjectRef';
interface KeyWithIncludes<TKey, TIncludes> {
@@ -44,6 +44,7 @@ interface IMapResourceResult<
resource: TResource;
exception: Error | null;
reload: () => void;
load: () => void;
}
export function useDataResource<
@@ -183,6 +184,9 @@ export function useDataResource<
setException(null);
(refObj as any)[loadFunctionName](true);
},
load: () => {
(refObj as any)[loadFunctionName]();
},
isLoading: () => {
if (refObj.key === null) {
return false;
@@ -12,10 +12,9 @@ import { useEffect, useState } from 'react';
import { IServiceConstructor, useService } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
import { CachedResourceIncludeArgs, CachedMapResource, CachedMapResourceGetter, ResourceKey, CachedMapResourceValue, CachedMapResourceKey, CachedMapResourceArguments, CachedMapResourceLoader, ResourceKeyList, CachedMapResourceListGetter, isResourceKeyList } from '@cloudbeaver/core-sdk';
import { isArraysEqual } from '@cloudbeaver/core-utils';
import { ILoadableState, isArraysEqual } from '@cloudbeaver/core-utils';
import { getComputed } from './getComputed';
import type { ILoadableState } from './Loader/ILoadableState';
import { useObservableRef } from './useObservableRef';
interface IActions<
@@ -56,6 +55,7 @@ interface IMapResourceState<TResource extends CachedMapResource<any, any, any>>
loaded: boolean;
resource: TResource;
isOutdated: () => boolean;
load: () => void;
reload: () => void;
}
@@ -355,6 +355,9 @@ export function useMapResource<
reload: () => {
(refObj as any)[loadFunctionName](true);
},
load: () => {
(refObj as any)[loadFunctionName]();
},
isLoading() {
return this.loading;
},
@@ -10,8 +10,8 @@ import { computed, observable } from 'mobx';
import { useEffect } from 'react';
import { Task } from '@cloudbeaver/core-executor';
import type { ILoadableState } from '@cloudbeaver/core-utils';
import type { ILoadableState } from './Loader/ILoadableState';
import { useObservableRef } from './useObservableRef';
@@ -40,6 +40,7 @@ export function usePromiseState<T>(promise: Promise<T> | null): ILoadableState {
isLoaded(): boolean {
return this.promise === null;
},
load() {},
}), {
cancel: computed,
promise: observable.ref,
@@ -8,6 +8,8 @@
import { makeObservable, observable } from 'mobx';
import type { ILoadableState } from '@cloudbeaver/core-utils';
import { CachedResource } from './CachedResource';
import type { CachedResourceIncludeArgs, CachedResourceValueIncludes } from './CachedResourceIncludes';
@@ -87,3 +89,35 @@ export abstract class CachedDataResource<
return this.data;
}
}
export function getCachedDataResourceLoaderState<
TData,
TParam = void,
TKey = TParam,
TContext extends Record<string, any> | void = void,
>(
resource: CachedDataResource<TData, TParam, TKey, TContext>,
param: TParam,
context: ContextArg<TData, TContext>
): ILoadableState {
return {
get exception() {
return resource.getException(param);
},
isLoading() {
return resource.isDataLoading(param);
},
isLoaded() {
return resource.isLoaded(param, context);
},
isOutdated() {
return resource.isOutdated(param);
},
load() {
return resource.load(param, context);
},
reload() {
return resource.refresh(param, context);
},
};
}
@@ -9,7 +9,7 @@
import { action, computed, makeObservable, observable } from 'mobx';
import { ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor';
import { isArraysEqual, MetadataMap } from '@cloudbeaver/core-utils';
import { ILoadableState, isArraysEqual, MetadataMap } from '@cloudbeaver/core-utils';
import { CachedResource, CachedResourceKey, ICachedResourceMetadata } from './CachedResource';
import type { CachedResourceIncludeArgs, CachedResourceValueIncludes } from './CachedResourceIncludes';
@@ -475,3 +475,34 @@ export abstract class CachedMapResource<
this.onDataOutdated.execute(key);
}
}
export function getCachedMapResourceLoaderState<
TKey,
TValue,
TArguments = Record<string, any>
>(
resource: CachedMapResource<TKey, TValue, TArguments>,
key: ResourceKey<TKey>,
includes?: CachedResourceIncludeArgs<TValue, TArguments> | undefined
): ILoadableState {
return {
get exception() {
return resource.getException(key);
},
isLoading() {
return resource.isDataLoading(key);
},
isLoaded() {
return resource.isLoaded(key, includes);
},
isOutdated() {
return resource.isOutdated(key);
},
load() {
return resource.load(key, includes);
},
reload() {
return resource.refresh(key, includes);
},
};
}
@@ -11,9 +11,8 @@ import { ButtonHTMLAttributes, forwardRef, useRef } from 'react';
import type { MenuInitialState } from 'reakit/Menu';
import styled from 'reshadow';
import { getComputed, IMenuState, Menu, menuPanelStyles, useObjectRef, useTranslate, useStyles } from '@cloudbeaver/core-blocks';
import { getComputed, IMenuState, Menu, menuPanelStyles, useObjectRef, useTranslate, useStyles, useAutoLoad } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import type { ComponentStyle } from '@cloudbeaver/core-theming';
import { IMenuData, MenuActionItem, MenuService } from '@cloudbeaver/core-view';
@@ -62,6 +61,8 @@ export const ContextMenu = observer<IContextMenuProps, HTMLButtonElement>(forwar
const menu = useRef<IMenuState>();
const styles = useStyles(menuPanelStyles, style);
useAutoLoad(menuData.loaders, !lazy);
const handlers = useObjectRef(() => ({
handleItemClose() {
menu.current?.hide();
@@ -38,13 +38,13 @@ export const MENU_BAR_DEFAULT_STYLES = css`
width: 24px;
}
& item-label {
& menu-bar-item-label {
display: block;
text-transform: uppercase;
font-weight: 700;
}
& IconOrImage + item-label, & Loader + item-label {
& IconOrImage + menu-bar-item-label, & Loader + menu-bar-item-label {
padding-left: 8px
}
}
@@ -11,7 +11,7 @@ import { forwardRef, useCallback } from 'react';
import { MenuInitialState, MenuSeparator } from 'reakit';
import styled, { use } from 'reshadow';
import { useStyles } from '@cloudbeaver/core-blocks';
import { useAutoLoad, useStyles } from '@cloudbeaver/core-blocks';
import type { ComponentStyle } from '@cloudbeaver/core-theming';
import { DATA_CONTEXT_MENU_NESTED, IMenuActionItem, IMenuData, IMenuItem, MenuActionItem, MenuBaseItem, MenuSeparatorItem, MenuSubMenuItem, useMenu } from '@cloudbeaver/core-view';
@@ -36,6 +36,7 @@ export const MenuBar = observer<IMenuBarProps, HTMLDivElement>(forwardRef(functi
}, ref) {
const styles = useStyles(style);
const items = menu.items;
useAutoLoad(menu.loaders);
if (!items.length) {
return null;
@@ -60,6 +61,7 @@ interface IMenuBarElementProps {
item: IMenuItem;
menuData: IMenuData;
nestedMenuSettings?: INestedMenuSettings;
className?: string;
style?: ComponentStyle;
}
@@ -67,6 +69,7 @@ const MenuBarElement = observer<IMenuBarElementProps>(function MenuBarElement({
item,
menuData,
nestedMenuSettings,
className,
style,
}) {
const styles = useStyles(style);
@@ -81,6 +84,7 @@ const MenuBarElement = observer<IMenuBarElementProps>(function MenuBarElement({
item={item}
menuData={menuData}
style={style}
className={className}
nestedMenuSettings={nestedMenuSettings}
/>
);
@@ -88,7 +92,7 @@ const MenuBarElement = observer<IMenuBarElementProps>(function MenuBarElement({
if (item instanceof MenuSeparatorItem) {
return styled(styles)(
<MenuSeparator />
<MenuSeparator className={className} />
);
}
@@ -97,6 +101,7 @@ const MenuBarElement = observer<IMenuBarElementProps>(function MenuBarElement({
<MenuBarAction
item={item}
style={style}
className={className}
onClick={onClick}
/>
);
@@ -112,6 +117,7 @@ const MenuBarElement = observer<IMenuBarElementProps>(function MenuBarElement({
title={item.tooltip}
disabled={item.disabled}
style={style}
className={className}
onClick={onClick}
{...use({ hidden: item.hidden })}
/>
@@ -124,10 +130,11 @@ const MenuBarElement = observer<IMenuBarElementProps>(function MenuBarElement({
interface IMenuBarActionProps {
item: IMenuActionItem;
style?: ComponentStyle;
className?: string;
onClick: () => void;
}
const MenuBarAction = observer<IMenuBarActionProps>(function MenuBarAction({ item, style, onClick }) {
const MenuBarAction = observer<IMenuBarActionProps>(function MenuBarAction({ item, style, className, onClick }) {
const styles = useStyles(style);
const actionInfo = item.action.actionInfo;
@@ -148,6 +155,7 @@ const MenuBarAction = observer<IMenuBarActionProps>(function MenuBarAction({ ite
disabled={item.disabled}
loading={loading}
style={style}
className={className}
onClick={handleClick}
{...use({ hidden: item.hidden })}
/>
@@ -158,6 +166,7 @@ interface ISubMenuItemProps {
item: MenuSubMenuItem;
menuData: IMenuData;
nestedMenuSettings?: INestedMenuSettings;
className?: string;
style?: ComponentStyle;
}
@@ -165,6 +174,7 @@ const SubMenuItem = observer<ISubMenuItemProps>(function SubmenuItem({
item,
menuData,
nestedMenuSettings,
className,
style,
}) {
const styles = useStyles(style);
@@ -176,6 +186,7 @@ const SubMenuItem = observer<ISubMenuItemProps>(function SubmenuItem({
<ContextMenu
menu={subMenuData}
style={style}
className={className}
disclosure
{...nestedMenuSettings}
>
@@ -37,7 +37,7 @@ export const MenuBarItem = observer<Props, HTMLButtonElement>(forwardRef(functio
<menu-bar-item ref={ref} as='button' {...rest} title={title} aria-label={title}>
{loading && <Loader small fullSize />}
{!loading && icon && <IconOrImage icon={icon} viewBox={viewBox} />}
{label && <item-label>{translate(label)}</item-label>}
{label && <menu-bar-item-label>{translate(label)}</menu-bar-item-label>}
</menu-bar-item>
);
}));
@@ -0,0 +1,35 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
export interface ILoadableState {
isLoading: () => boolean;
isLoaded: () => boolean;
readonly exception?: (Error | null)[] | Error | null;
load: () => void;
reload?: () => void;
isOutdated?: () => boolean;
isCancelled?: () => boolean;
cancel?: () => void;
}
// export function composeLoadableState(...states: ILoadableState[]): ILoadableState {
// }
export function isLoadableStateHasException(state: ILoadableState): boolean {
return isContainsException(state.exception);
}
export function isContainsException(exception?: (Error | null)[] | Error | null): boolean {
if (Array.isArray(exception)) {
return exception.some(Boolean);
}
return !!exception;
}
+1
View File
@@ -16,6 +16,7 @@ export * from './flat';
export * from './generateFileName';
export * from './getMIME';
export * from './GlobalConstants';
export * from './ILoadableState';
export * from './isArraysEqual';
export * from './isPropertiesEqual';
export * from './isValuesEqual';
@@ -6,6 +6,8 @@
* you may not use this file except in compliance with the License.
*/
import { flat } from '@cloudbeaver/core-utils';
import type { IDataContextProvider } from '../DataContext/IDataContextProvider';
import type { IAction } from './IAction';
import type { IActionHandler } from './IActionHandler';
@@ -17,7 +19,7 @@ export class ActionItem implements IActionItem {
readonly action: IAction;
readonly handler: IActionHandler;
readonly binding: IKeyBindingHandler | null;
private context: IDataContextProvider;
private readonly context: IDataContextProvider;
get actionInfo(): IActionInfo {
if (this.handler.getActionInfo) {
@@ -44,7 +46,10 @@ export class ActionItem implements IActionItem {
}
isLoading(): boolean {
return this.handler.isLoading?.(this.context, this.action) ?? false;
return (
this.handler.isLoading?.(this.context, this.action)
|| flat([this.handler.getLoader?.(this.context, this.action)]).some(loader => loader?.isLoading())
);
}
isDisabled(): boolean {
@@ -6,6 +6,8 @@
* you may not use this file except in compliance with the License.
*/
import type { ILoadableState } from '@cloudbeaver/core-utils';
import type { IDataContextProvider } from '../DataContext/IDataContextProvider';
import type { IAction } from './IAction';
import type { IActionInfo } from './IActionInfo';
@@ -14,6 +16,7 @@ export interface IActionHandler {
id: string;
getActionInfo?: (context: IDataContextProvider, action: IAction) => IActionInfo;
getLoader?: (context: IDataContextProvider, action: IAction) => ILoadableState[] | ILoadableState;
isChecked?: (context: IDataContextProvider, action: IAction) => boolean;
isLoading?: (context: IDataContextProvider, action: IAction) => boolean;
@@ -36,8 +36,12 @@ export class DataContext implements IDataContext {
this.fallback = fallback;
}
has(context: DataContextGetter<any>): boolean {
return this.map.has(context) || this.fallback?.has(context) || false;
has(context: DataContextGetter<any>, nested = true): boolean {
if (nested) {
return this.map.has(context) || this.fallback?.has(context) || false;
}
return this.map.has(context);
}
hasValue<T>(context: DataContextGetter<T>, value: T): boolean {
@@ -11,7 +11,7 @@ import type { DataContextGetter } from './DataContextGetter';
export interface IDataContextProvider {
fallback?: IDataContextProvider;
readonly map: Map<DataContextGetter<any>, any>;
has: (context: DataContextGetter<any>) => boolean;
has: (context: DataContextGetter<any>, nested?: boolean) => boolean;
get: <T>(context: DataContextGetter<T>) => T;
find: <T>(context: DataContextGetter<T>, predicate: (item: T) => boolean) => T | undefined;
hasValue: <T>(context: DataContextGetter<T>, value: T) => boolean;
@@ -8,11 +8,14 @@
import type { DataContextGetter } from './DataContextGetter';
export function createDataContext<T>(name: string, defaultValue?: T): DataContextGetter<T> {
export function createDataContext<T>(
name: string,
defaultValue?: () => T extends any ? T : undefined
): DataContextGetter<T> {
name = `@context/${name}`;
const obj = {
[name](): T {
return defaultValue as T;
return defaultValue?.() as T;
},
};
return obj[name];
@@ -0,0 +1,31 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2021 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 { flat, ILoadableState } from '@cloudbeaver/core-utils';
import { createDataContext } from '../DataContext/createDataContext';
interface ILoadableStateContext {
getState(id: string, defaultState: () => ILoadableState | ILoadableState[]): ILoadableState[];
}
export function loadableStateContext(): ILoadableStateContext {
const state = new Map<string, ILoadableState[]>();
return {
getState(id, defaultState) {
if (!state.has(id)) {
state.set(id, flat([defaultState()]));
}
return state.get(id)!;
},
};
}
export const DATA_CONTEXT_LOADABLE_STATE = createDataContext<ILoadableStateContext>('loadable-state', loadableStateContext);
@@ -8,4 +8,4 @@
import { createDataContext } from '../DataContext/createDataContext';
export const DATA_CONTEXT_MENU_LOCAL = createDataContext<boolean | undefined>('menu-local', false);
export const DATA_CONTEXT_MENU_LOCAL = createDataContext<boolean | undefined>('menu-local', () => false);
@@ -8,4 +8,4 @@
import { createDataContext } from '../DataContext/createDataContext';
export const DATA_CONTEXT_MENU_NESTED = createDataContext<boolean>('menu-nested', false);
export const DATA_CONTEXT_MENU_NESTED = createDataContext<boolean>('menu-nested', () => false);
@@ -7,6 +7,7 @@
*/
import { injectable } from '@cloudbeaver/core-di';
import { flat, ILoadableState } from '@cloudbeaver/core-utils';
import { ActionService } from '../Action/ActionService';
import { isAction } from '../Action/createAction';
@@ -44,11 +45,6 @@ export class MenuService {
return null;
}
isMenuAvailable(context: IDataContextProvider): boolean {
return this.creators
.some(filterApplicable(context));
}
addCreator(creator: IMenuItemsCreator): void {
this.creators.push(creator);
}
@@ -70,18 +66,32 @@ export class MenuService {
return null;
}
getMenu(context: IDataContextProvider): IMenuItem[] {
const applicableCreators = this.creators.filter(filterApplicable(context));
getMenuItemLoaders(context: IDataContextProvider, itemCreators: MenuCreatorItem[]): ILoadableState[] {
return flat(itemCreators
.map(item => {
if (isAction(item)) {
const handler = this.actionService.getHandler(context, item);
return applicableCreators
.reduce<MenuCreatorItem[]>(
return handler?.getLoader?.(context, item) ?? null;
}
return null;
}))
.filter<ILoadableState>((item => item !== null) as ((obj: any) => obj is ILoadableState))
;
}
getMenuItemCreators(context: IDataContextProvider): MenuCreatorItem[] {
const creators = this.creators.filter(filterApplicable(context));
return creators.reduce<MenuCreatorItem[]>(
(items, creator) => {
if (creator.orderItems) {
return creator.orderItems(context, items);
}
return items;
},
applicableCreators
creators
.reduce<MenuCreatorItem[]>((items, creator) => creator.getItems(context, items), [])
.filter(item => {
if (isAction(item)) {
@@ -90,7 +100,11 @@ export class MenuService {
return true;
})
)
);
}
getMenu(context: IDataContextProvider, itemCreators: MenuCreatorItem[]): IMenuItem[] {
return itemCreators
.map(item => {
if (isAction(item)) {
return this.createActionItem(context, item) as IMenuItem;
@@ -132,6 +146,10 @@ function filterApplicable(contexts: IDataContextProvider): (creator: IMenuItemsC
return false;
}
return creator.isApplicable?.(contexts) ?? true;
if (creator.isApplicable?.(contexts) === false) {
return false;
}
return true;
};
}
+17 -3
View File
@@ -10,10 +10,12 @@ import { computed, observable } from 'mobx';
import { useObservableRef } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import type { ILoadableState } from '@cloudbeaver/core-utils';
import type { IDataContext } from '../DataContext/IDataContext';
import { DATA_CONTEXT_MENU_LOCAL } from './DATA_CONTEXT_MENU_LOCAL';
import type { IMenu } from './IMenu';
import type { MenuCreatorItem } from './IMenuItemsCreator';
import type { IMenuItem } from './MenuItem/IMenuItem';
import { MenuService } from './MenuService';
import { useMenuContext } from './useMenuContext';
@@ -22,6 +24,8 @@ export interface IMenuData {
menu: IMenu;
context: IDataContext;
available: boolean;
loaders: ILoadableState[];
itemCreators: MenuCreatorItem[];
items: IMenuItem[];
}
@@ -37,16 +41,24 @@ export function useMenu(options: IMenuOptions): IMenuData {
context.set(DATA_CONTEXT_MENU_LOCAL, options.local);
return useObservableRef<IMenuData>(() => ({
const state = useObservableRef<IMenuData>(() => ({
context,
get loaders() {
return menuService.getMenuItemLoaders(this.context, this.itemCreators);
},
get itemCreators() {
return menuService.getMenuItemCreators(this.context);
},
get available() {
return menuService.isMenuAvailable(this.context);
return this.itemCreators.length > 0;
},
get items() {
return menuService.getMenu(this.context);
return menuService.getMenu(this.context, this.itemCreators);
},
}), {
loaders: computed,
available: computed,
itemCreators: computed,
items: computed,
menu: observable.ref,
context: observable.ref,
@@ -54,4 +66,6 @@ export function useMenu(options: IMenuOptions): IMenuData {
menu: options.menu,
context,
});
return state;
}
@@ -10,6 +10,7 @@ import { useContext } from 'react';
import type { IDataContext } from '../DataContext/IDataContext';
import { useDataContext } from '../DataContext/useDataContext';
import { DATA_CONTEXT_LOADABLE_STATE, loadableStateContext } from '../LoadableStateContext/DATA_CONTEXT_LOADABLE_STATE';
import { CaptureViewContext } from '../View/CaptureViewContext';
import { DATA_CONTEXT_MENU } from './DATA_CONTEXT_MENU';
import type { IMenu } from './IMenu';
@@ -20,5 +21,9 @@ export function useMenuContext(menu: IMenu, _menuContext?: IDataContext): IDataC
context.set(DATA_CONTEXT_MENU, menu);
if (context.has(DATA_CONTEXT_LOADABLE_STATE, false)) {
context.set(DATA_CONTEXT_LOADABLE_STATE, loadableStateContext());
}
return context;
}
+1
View File
@@ -28,6 +28,7 @@ export * from './DataContext/IDataContext';
export * from './DataContext/IDataContextProvider';
export * from './DataContext/useDataContext';
export * from './DataContext/useDynamicDataContext';
export * from './LoadableStateContext/DATA_CONTEXT_LOADABLE_STATE';
export * from './Menu/MenuItem/IMenuCheckboxItem';
export * from './Menu/MenuItem/IMenuActionItem';
export * from './Menu/MenuItem/IMenuBaseItem';
@@ -10,9 +10,8 @@ import { observer } from 'mobx-react-lite';
import styled, { css } from 'reshadow';
import type { TeamInfo } from '@cloudbeaver/core-authentication';
import { Table, TableHeader, TableColumnHeader, TableBody, TableSelect, Loader, ILoadableState, useTranslate, useStyles } from '@cloudbeaver/core-blocks';
import { Table, TableHeader, TableColumnHeader, TableBody, TableSelect, Loader, useTranslate, useStyles } from '@cloudbeaver/core-blocks';
import type { ILoadableState } from '@cloudbeaver/core-utils';
import { Team } from './Team';
@@ -9,12 +9,12 @@
import { computed, observable } from 'mobx';
import { compareTeams, TeamInfo, TeamsResource } from '@cloudbeaver/core-authentication';
import { ILoadableState, TableState, useMapResource, useObservableRef, useTranslate } from '@cloudbeaver/core-blocks';
import { TableState, useMapResource, useObservableRef, useTranslate } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { CommonDialogService, ConfirmationDialogDelete, DialogueStateResult } from '@cloudbeaver/core-dialogs';
import { NotificationService } from '@cloudbeaver/core-events';
import { CachedMapAllKey, resourceKeyList } from '@cloudbeaver/core-sdk';
import type { ILoadableState } from '@cloudbeaver/core-utils';
interface State {
tableState: TableState;
@@ -11,10 +11,11 @@ import { useEffect } from 'react';
import { AdministrationScreenService } from '@cloudbeaver/core-administration';
import { AuthInfoService, AuthProvider, AuthProviderConfiguration, AuthProvidersResource, IAuthCredentials } from '@cloudbeaver/core-authentication';
import { ILoadableState, useMapResource, useObservableRef } from '@cloudbeaver/core-blocks';
import { useMapResource, useObservableRef } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
import { CachedMapAllKey } from '@cloudbeaver/core-sdk';
import type { ILoadableState } from '@cloudbeaver/core-utils';
import { FEDERATED_AUTH } from './FEDERATED_AUTH';
@@ -6,12 +6,11 @@
* you may not use this file except in compliance with the License.
*/
export interface ILoadableState {
isLoading: () => boolean;
isLoaded: () => boolean;
readonly exception?: Error[] | Error | null;
reload?: () => void;
import { createAction } from '@cloudbeaver/core-view';
isCancelled?: () => boolean;
cancel?: () => void;
}
export const ACTION_CONNECTION_CUSTOM = createAction(
'connection-custom',
{
label: 'plugin_connection_custom_action_custom_label',
}
);
@@ -9,35 +9,57 @@
import { ConnectionsManagerService } from '@cloudbeaver/core-connections';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
import { ProjectsService } from '@cloudbeaver/core-projects';
import { MainMenuService, EMainMenu } from '@cloudbeaver/plugin-top-app-bar';
import { ProjectInfoResource } from '@cloudbeaver/core-projects';
import { CachedMapAllKey, getCachedMapResourceLoaderState } from '@cloudbeaver/core-sdk';
import { ActionService, DATA_CONTEXT_LOADABLE_STATE, DATA_CONTEXT_MENU, MenuService } from '@cloudbeaver/core-view';
import { MENU_CONNECTIONS } from '@cloudbeaver/plugin-connections';
import { ACTION_CONNECTION_CUSTOM } from './Actions/ACTION_CONNECTION_CUSTOM';
import { CustomConnectionDialog } from './CustomConnection/CustomConnectionDialog';
@injectable()
export class CustomConnectionPluginBootstrap extends Bootstrap {
constructor(
private readonly mainMenuService: MainMenuService,
private readonly commonDialogService: CommonDialogService,
private readonly projectsService: ProjectsService,
private readonly connectionsManagerService: ConnectionsManagerService
private readonly projectInfoResource: ProjectInfoResource,
private readonly menuService: MenuService,
private readonly actionService: ActionService,
private readonly connectionsManagerService: ConnectionsManagerService,
) {
super();
}
register(): void | Promise<void> {
this.mainMenuService.onConnectionClick.addHandler(() => {
this.projectsService.load();
this.menuService.addCreator({
isApplicable: context => context.tryGet(DATA_CONTEXT_MENU) === MENU_CONNECTIONS,
getItems: (context, items) => [
...items,
ACTION_CONNECTION_CUSTOM,
],
});
this.mainMenuService.registerMenuItem(EMainMenu.mainMenuConnectionsPanel, {
id: 'customConnection',
order: 2,
title: 'app_shared_connectionMenu_custom',
isHidden: () => (
!this.projectsService.activeProjects.some(project => project.canEditDataSources)
),
onClick: () => this.openConnectionsDialog(),
this.actionService.addHandler({
id: 'connection-custom',
isActionApplicable: (context, action) => [
ACTION_CONNECTION_CUSTOM,
].includes(action),
isHidden: () => this.connectionsManagerService.createConnectionProjects.length === 0,
getLoader: (context, action) => {
const state = context.get(DATA_CONTEXT_LOADABLE_STATE);
return state.getState(
action.id,
() => getCachedMapResourceLoaderState(this.projectInfoResource, CachedMapAllKey)
);
},
handler: async (context, action) => {
switch (action) {
case ACTION_CONNECTION_CUSTOM: {
await this.openConnectionsDialog();
break;
}
}
},
});
}
@@ -17,4 +17,5 @@ export default [
['customConnection_test', 'Test Connection'],
['customConnection_create', 'Create'],
['customConnection_create_error', 'Сreate connection error'],
['plugin_connection_custom_action_custom_label', 'Manual'],
];
@@ -17,4 +17,5 @@ export default [
['customConnection_test', 'Prova la connessione'],
['customConnection_create', 'Crea'],
['customConnection_create_error', 'Errore di creazione connessione'],
['plugin_connection_custom_action_custom_label', 'Manuale'],
];
@@ -17,4 +17,5 @@ export default [
['customConnection_test', 'Проверить'],
['customConnection_create', 'Создать'],
['customConnection_create_error', 'Ошибка при создании подключения'],
['plugin_connection_custom_action_custom_label', 'Создать'],
];
@@ -17,4 +17,5 @@ export default [
['customConnection_test', '测试连接'],
['customConnection_create', '创建'],
['customConnection_create_error', '创建连接失败'],
['plugin_connection_custom_action_custom_label', '手动'],
];
@@ -0,0 +1,16 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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_CONNECTION_SEARCH = createAction(
'connection-search',
{
label: 'plugin_connection_search_action_search_label',
}
);
@@ -0,0 +1,36 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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 { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { LocalizationService } from '@cloudbeaver/core-localization';
@injectable()
export class LocaleService extends Bootstrap {
constructor(private readonly localizationService: LocalizationService) {
super();
}
register(): void | Promise<void> {
this.localizationService.addProvider(this.provider.bind(this));
}
load(): void | Promise<void> { }
private async provider(locale: string) {
switch (locale) {
case 'ru':
return (await import('./locales/ru')).default;
case 'it':
return (await import('./locales/it')).default;
case 'zh':
return (await import('./locales/zh')).default;
default:
return (await import('./locales/en')).default;
}
}
}
@@ -7,39 +7,63 @@
*/
import { EAdminPermission } from '@cloudbeaver/core-administration';
import { ConnectionsManagerService } from '@cloudbeaver/core-connections';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { ProjectsService } from '@cloudbeaver/core-projects';
import { ProjectInfoResource } from '@cloudbeaver/core-projects';
import { PermissionsService } from '@cloudbeaver/core-root';
import { MainMenuService, EMainMenu } from '@cloudbeaver/plugin-top-app-bar';
import { getCachedMapResourceLoaderState, CachedMapAllKey } from '@cloudbeaver/core-sdk';
import { MenuService, ActionService, DATA_CONTEXT_MENU, DATA_CONTEXT_LOADABLE_STATE } from '@cloudbeaver/core-view';
import { MENU_CONNECTIONS } from '@cloudbeaver/plugin-connections';
import { ACTION_CONNECTION_SEARCH } from './Actions/ACTION_CONNECTION_SEARCH';
import { ConnectionSearchService } from './Search/ConnectionSearchService';
@injectable()
export class SearchConnectionPluginBootstrap extends Bootstrap {
constructor(
private readonly mainMenuService: MainMenuService,
private readonly projectsService: ProjectsService,
private readonly permissionsService: PermissionsService,
private readonly projectInfoResource: ProjectInfoResource,
private readonly connectionSearchService: ConnectionSearchService,
private readonly connectionsManagerService: ConnectionsManagerService,
private readonly menuService: MenuService,
private readonly actionService: ActionService,
) {
super();
}
register(): void | Promise<void> {
this.mainMenuService.onConnectionClick.addHandler(() => {
this.projectsService.load();
this.menuService.addCreator({
isApplicable: context => context.tryGet(DATA_CONTEXT_MENU) === MENU_CONNECTIONS,
getItems: (context, items) => [
...items,
ACTION_CONNECTION_SEARCH,
],
});
this.mainMenuService.registerMenuItem(EMainMenu.mainMenuConnectionsPanel, {
id: 'searchConnection',
order: 3,
title: 'connections_connection_create_search_database',
this.actionService.addHandler({
id: 'connection-search',
isActionApplicable: (context, action) => [
ACTION_CONNECTION_SEARCH,
].includes(action),
isHidden: () => (
!this.projectsService.activeProjects.some(project => project.canEditDataSources)
this.connectionsManagerService.createConnectionProjects.length === 0
|| !this.permissionsService.has(EAdminPermission.admin)
),
onClick: () => {
this.connectionSearchService.open();
getLoader: (context, action) => {
const state = context.get(DATA_CONTEXT_LOADABLE_STATE);
return state.getState(
action.id,
() => getCachedMapResourceLoaderState(this.projectInfoResource, CachedMapAllKey)
);
},
handler: async (context, action) => {
switch (action) {
case ACTION_CONNECTION_SEARCH: {
this.connectionSearchService.open();
break;
}
}
},
});
}
@@ -0,0 +1,3 @@
export default [
['plugin_connection_search_action_search_label', 'Search'],
];
@@ -0,0 +1,3 @@
export default [
['plugin_connection_search_action_search_label', 'Search'],
];
@@ -0,0 +1,3 @@
export default [
['plugin_connection_search_action_search_label', 'Поиск'],
];
@@ -0,0 +1,3 @@
export default [
['plugin_connection_search_action_search_label', 'Search'],
];
@@ -1,5 +1,6 @@
import type { PluginManifest } from '@cloudbeaver/core-di';
import { LocaleService } from './LocaleService';
import { ConnectionSearchService } from './Search/ConnectionSearchService';
import { SearchConnectionPluginBootstrap } from './SearchConnectionPluginBootstrap';
@@ -10,5 +11,6 @@ export const connectionSearchPlugin: PluginManifest = {
providers: [
SearchConnectionPluginBootstrap,
ConnectionSearchService,
LocaleService,
],
};
@@ -0,0 +1,16 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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_CONNECTION_TEMPLATE = createAction(
'connection-template',
{
label: 'plugin_connection_template_action_connection_template_label',
}
);
@@ -8,11 +8,13 @@
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
import { NotificationService } from '@cloudbeaver/core-events';
import { ProjectsService } from '@cloudbeaver/core-projects';
import { ProjectInfoResource, ProjectsService } from '@cloudbeaver/core-projects';
import { PermissionsService, EPermission } from '@cloudbeaver/core-root';
import { MainMenuService, EMainMenu } from '@cloudbeaver/plugin-top-app-bar';
import { CachedMapAllKey, getCachedDataResourceLoaderState, getCachedMapResourceLoaderState } from '@cloudbeaver/core-sdk';
import { MenuService, ActionService, DATA_CONTEXT_MENU, DATA_CONTEXT_LOADABLE_STATE } from '@cloudbeaver/core-view';
import { MENU_CONNECTIONS } from '@cloudbeaver/plugin-connections';
import { ACTION_CONNECTION_TEMPLATE } from './Actions/ACTION_CONNECTION_TEMPLATE';
import { ConnectionDialog } from './ConnectionDialog/ConnectionDialog';
import { TemplateConnectionsResource } from './TemplateConnectionsResource';
import { TemplateConnectionsService } from './TemplateConnectionsService';
@@ -20,10 +22,11 @@ import { TemplateConnectionsService } from './TemplateConnectionsService';
@injectable()
export class TemplateConnectionPluginBootstrap extends Bootstrap {
constructor(
private readonly mainMenuService: MainMenuService,
private readonly menuService: MenuService,
private readonly actionService: ActionService,
private readonly projectInfoResource: ProjectInfoResource,
private readonly templateConnectionsResource: TemplateConnectionsResource,
private readonly commonDialogService: CommonDialogService,
private readonly notificationService: NotificationService,
private readonly permissionsService: PermissionsService,
private readonly templateConnectionsService: TemplateConnectionsService,
private readonly projectsService: ProjectsService,
@@ -32,40 +35,49 @@ export class TemplateConnectionPluginBootstrap extends Bootstrap {
}
register(): void | Promise<void> {
this.mainMenuService.onConnectionClick.addHandler(this.loadTemplateConnections.bind(this));
this.mainMenuService.registerMenuItem(EMainMenu.mainMenuConnectionsPanel, {
id: 'mainMenuConnect',
order: 1,
titleGetter: this.getMenuTitle.bind(this),
onClick: this.openConnectionsDialog.bind(this),
this.menuService.addCreator({
isApplicable: context => context.tryGet(DATA_CONTEXT_MENU) === MENU_CONNECTIONS,
getItems: (context, items) => [
...items,
ACTION_CONNECTION_TEMPLATE,
],
});
this.actionService.addHandler({
id: 'connection-template',
isActionApplicable: (context, action) => [
ACTION_CONNECTION_TEMPLATE,
].includes(action),
isHidden: () => (
!this.permissionsService.has(EPermission.public)
|| !this.projectsService.userProject?.canEditDataSources
|| !this.templateConnectionsService.projectTemplates.length
),
getLoader: (context, action) => {
const state = context.get(DATA_CONTEXT_LOADABLE_STATE);
return state.getState(
action.id,
() => [
getCachedMapResourceLoaderState(this.projectInfoResource, CachedMapAllKey),
getCachedDataResourceLoaderState(this.templateConnectionsResource, undefined, undefined),
]
);
},
handler: async (context, action) => {
switch (action) {
case ACTION_CONNECTION_TEMPLATE: {
await this.openConnectionsDialog();
break;
}
}
},
});
}
load(): void | Promise<void> { }
private getMenuTitle(): string {
if (this.templateConnectionsResource.isLoading()) {
return 'ui_processing_loading';
}
return 'basicConnection_main_menu_item';
}
private async openConnectionsDialog() {
this.loadTemplateConnections();
await this.commonDialogService.open(ConnectionDialog, null);
}
private async loadTemplateConnections() {
try {
await this.projectsService.load();
await this.templateConnectionsResource.load();
} catch (error: any) {
this.notificationService.logException(error, 'Template Connections loading failed');
}
}
}
@@ -8,5 +8,5 @@ export default [
['basicConnection_connectionDialog_passwordPlaceholder', 'password'],
['basicConnection_connectionDialog_connecting', 'Connecting...'],
['basicConnection_connectionDialog_connecting_message', 'Connecting to database...'],
['basicConnection_main_menu_item', 'From Template'],
['plugin_connection_template_action_connection_template_label', 'From Template'],
];
@@ -0,0 +1,16 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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_CONNECTION_DISCONNECT_ALL = createAction(
'connection-disconnect-all',
{
label: 'plugin_connections_action_disconnect_all_label',
}
);
@@ -13,6 +13,7 @@ import { NotificationService } from '@cloudbeaver/core-events';
import { DATA_CONTEXT_NAV_NODE, EObjectFeature, NavNodeManagerService } from '@cloudbeaver/core-navigation-tree';
import { CONNECTION_NAVIGATOR_VIEW_SETTINGS, isNavigatorViewSettingsEqual, NavigatorViewSettings, PermissionsService } from '@cloudbeaver/core-root';
import { ActionService, ACTION_DELETE, DATA_CONTEXT_MENU, DATA_CONTEXT_MENU_NESTED, MenuSeparatorItem, MenuService } from '@cloudbeaver/core-view';
import { TOP_APP_BAR_MENU } from '@cloudbeaver/plugin-top-app-bar';
import { ConnectionsSettingsService } from '../ConnectionsSettingsService';
import { PublicConnectionFormService } from '../PublicConnectionForm/PublicConnectionFormService';
@@ -23,6 +24,7 @@ import { ACTION_CONNECTION_VIEW_SIMPLE } from './Actions/ACTION_CONNECTION_VIEW_
import { ACTION_CONNECTION_VIEW_SYSTEM_OBJECTS } from './Actions/ACTION_CONNECTION_VIEW_SYSTEM_OBJECTS';
import { DATA_CONTEXT_CONNECTION } from './DATA_CONTEXT_CONNECTION';
import { MENU_CONNECTION_VIEW } from './MENU_CONNECTION_VIEW';
import { MENU_CONNECTIONS } from './MENU_CONNECTIONS';
@injectable()
export class ConnectionMenuBootstrap extends Bootstrap {
@@ -41,6 +43,8 @@ export class ConnectionMenuBootstrap extends Bootstrap {
}
register(): void | Promise<void> {
this.addConnectionsMenuToTopAppBar();
this.menuService.addCreator({
isApplicable: context => {
if (
@@ -225,4 +229,14 @@ export class ConnectionMenuBootstrap extends Bootstrap {
this.notificationService.logException(exception);
}
}
private addConnectionsMenuToTopAppBar() {
this.menuService.addCreator({
isApplicable: context => context.tryGet(DATA_CONTEXT_MENU) === TOP_APP_BAR_MENU,
getItems: (context, items) => [
...items,
MENU_CONNECTIONS,
],
});
}
}
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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_CONNECTIONS = createMenu(
'connections',
'plugin_connections_menu_connections_label'
);
@@ -8,28 +8,45 @@
import { ConnectionsManagerService } from '@cloudbeaver/core-connections';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { EMainMenu, MainMenuService } from '@cloudbeaver/plugin-top-app-bar';
import { MenuService, ActionService, DATA_CONTEXT_MENU } from '@cloudbeaver/core-view';
import { ACTION_CONNECTION_DISCONNECT_ALL } from './ContextMenu/Actions/ACTION_CONNECTION_DISCONNECT_ALL';
import { MENU_CONNECTIONS } from './ContextMenu/MENU_CONNECTIONS';
@injectable()
export class PluginBootstrap extends Bootstrap {
constructor(
private readonly mainMenuService: MainMenuService,
private readonly menuService: MenuService,
private readonly actionService: ActionService,
private readonly connectionsManagerService: ConnectionsManagerService,
) {
super();
}
register(): void {
this.mainMenuService.registerMenuItem(
EMainMenu.mainMenuConnectionsPanel,
{
id: 'mainMenuDisconnect',
order: 3,
title: 'app_shared_connectionMenu_disconnect',
onClick: () => this.connectionsManagerService.closeAllConnections(),
isDisabled: () => !this.connectionsManagerService.hasAnyConnection(true),
}
);
this.menuService.addCreator({
isApplicable: context => context.tryGet(DATA_CONTEXT_MENU) === MENU_CONNECTIONS,
getItems: (context, items) => [
...items,
ACTION_CONNECTION_DISCONNECT_ALL,
],
});
this.actionService.addHandler({
id: 'connection-base',
isActionApplicable: (context, action) => [
ACTION_CONNECTION_DISCONNECT_ALL,
].includes(action),
isDisabled: () => !this.connectionsManagerService.hasAnyConnection(true),
handler: async (context, action) => {
switch (action) {
case ACTION_CONNECTION_DISCONNECT_ALL: {
await this.connectionsManagerService.closeAllConnections();
break;
}
}
},
});
}
load(): void { }
@@ -16,6 +16,7 @@ export * from './ConnectionForm/IConnectionFormProps';
export * from './ConnectionForm/useConnectionFormState';
export * from './ContextMenu/DATA_CONTEXT_CONNECTION';
export * from './ContextMenu/MENU_CONNECTION_VIEW';
export * from './ContextMenu/MENU_CONNECTIONS';
export * from './PublicConnectionForm/PublicConnectionFormService';
export * from './ConnectionAuthService';
@@ -12,4 +12,6 @@ export default [
['plugin_connections_connection_form_host_configuration_invalid', 'Host configuration is not supported'],
['plugin_connections_connection_form_name_invalid', 'Field \'name\' can\'t be empty'],
['connections_public_connection_folder_delete_confirmation', 'You\'re going to delete "{arg:name}". Connections won\'t be deleted. Are you sure?'],
['plugin_connections_menu_connections_label', 'Connection'],
['plugin_connections_action_disconnect_all_label', 'Disconnect All'],
];
@@ -11,4 +11,6 @@ export default [
['plugin_connections_connection_form_host_configuration_invalid', 'Host configuration is not supported'],
['plugin_connections_connection_form_name_invalid', 'Field \'name\' can\'t be empty'],
['connections_public_connection_folder_delete_confirmation', 'You\'re going to delete "{arg:name}". Connections won\'t be deleted. Are you sure?'],
['plugin_connections_menu_connections_label', 'Connessione'],
['plugin_connections_action_disconnect_all_label', 'Scollegati da tutto'],
];
@@ -11,4 +11,6 @@ export default [
['plugin_connections_connection_form_host_configuration_invalid', 'Конфигурация хоста не поддерживается'],
['plugin_connections_connection_form_name_invalid', 'Поле \'Название\' не может быть пустым'],
['connections_public_connection_folder_delete_confirmation', 'Вы удаляете "{arg:name}". Подключения не будут удалены. Вы уверены?'],
['plugin_connections_menu_connections_label', 'Подключение'],
['plugin_connections_action_disconnect_all_label', 'Отключить все'],
];
@@ -11,4 +11,6 @@ export default [
['plugin_connections_connection_form_host_configuration_invalid', 'Host configuration is not supported'],
['plugin_connections_connection_form_name_invalid', 'Field \'name\' can\'t be empty'],
['connections_public_connection_folder_delete_confirmation', 'You\'re going to delete "{arg:name}". Connections won\'t be deleted. Are you sure?'],
['plugin_connections_menu_connections_label', '连接'],
['plugin_connections_action_disconnect_all_label', '断开所有连接'],
];
@@ -9,10 +9,11 @@
import { action, computed, observable } from 'mobx';
import { useEffect } from 'react';
import { ILoadableState, isContainsException, useMapResource, useObservableRef } from '@cloudbeaver/core-blocks';
import { useMapResource, useObservableRef } from '@cloudbeaver/core-blocks';
import { ConnectionInfoResource } from '@cloudbeaver/core-connections';
import { useService } from '@cloudbeaver/core-di';
import { NavNodeManagerService } from '@cloudbeaver/core-navigation-tree';
import { ILoadableState, isContainsException } from '@cloudbeaver/core-utils';
import type { ITab } from '@cloudbeaver/plugin-navigation-tabs';
import type { IObjectViewerTabState } from '@cloudbeaver/plugin-object-viewer';
@@ -60,7 +61,7 @@ export function useDataViewerDatabaseDataModel(tab: ITab<IObjectViewerTabState>)
},
async reload() {
if (isContainsException(connection.exception)) {
connection.reload?.();
connection.reload();
}
this.init();
},
@@ -51,13 +51,13 @@ const TABLE_HEADER_MENU_BAR_STYLES = css`
width: 16px;
}
& item-label {
& menu-bar-item-label {
display: block;
text-transform: uppercase;
font-weight: 700;
}
& IconOrImage + item-label, & Loader + item-label {
& IconOrImage + menu-bar-item-label, & Loader + menu-bar-item-label {
padding-left: 8px
}
}
@@ -8,4 +8,4 @@
import { createDataContext } from '@cloudbeaver/core-view';
export const DATA_CONTEXT_MENU_SEARCH = createDataContext<string>('menu-local', '');
export const DATA_CONTEXT_MENU_SEARCH = createDataContext<string>('menu-local', () => '');
@@ -0,0 +1,17 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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_LOG_VIEWER_ENABLE = createAction(
'log-viewer-enable',
{
label: 'plugin_log_viewer_action_enable_label',
type: 'checkbox',
}
);
@@ -0,0 +1,45 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2019-2022 DBeaver Corp
*
* All Rights Reserved
*
* NOTICE: All information contained herein is, and remains
* the property of DBeaver Corp and its suppliers, if any.
* The intellectual and technical concepts contained
* herein are proprietary to DBeaver Corp and its suppliers
* and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from DBeaver Corp.
*/
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { LocalizationService } from '@cloudbeaver/core-localization';
@injectable()
export class LocaleService extends Bootstrap {
constructor(private readonly localizationService: LocalizationService) {
super();
}
register(): void | Promise<void> {
this.localizationService.addProvider(this.provider.bind(this));
}
load(): void | Promise<void> { }
private async provider(locale: string) {
switch (locale) {
case 'ru':
return (await import('./locales/ru')).default;
case 'it':
return (await import('./locales/it')).default;
case 'zh':
return (await import('./locales/zh')).default;
default:
return (await import('./locales/en')).default;
}
}
}
@@ -40,7 +40,7 @@ export const LogViewer = observer(function LogViewer() {
}, [logViewerState]);
if (!logViewerState.isActive) {
return <TextPlaceholder>{translate('app_shared_toolsMenu_logViewer_placeholder')}</TextPlaceholder>;
return <TextPlaceholder>{translate('plugin_log_viewer_placeholder')}</TextPlaceholder>;
}
return styled(style)(
@@ -7,9 +7,10 @@
*/
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { ToolsPanelService } from '@cloudbeaver/plugin-tools-panel';
import { MainMenuService, EMainMenu } from '@cloudbeaver/plugin-top-app-bar';
import { MenuService, ActionService, DATA_CONTEXT_MENU } from '@cloudbeaver/core-view';
import { MENU_TOOLS, ToolsPanelService } from '@cloudbeaver/plugin-tools-panel';
import { ACTION_LOG_VIEWER_ENABLE } from '../Actions/ACTION_LOG_VIEWER_ENABLE';
import { LogViewer } from './LogViewer';
import { LogViewerService } from './LogViewerService';
@@ -17,30 +18,43 @@ import { LogViewerService } from './LogViewerService';
@injectable()
export class LogViewerBootstrap extends Bootstrap {
constructor(
private readonly mainMenuService: MainMenuService,
private readonly toolsPanelService: ToolsPanelService,
private readonly menuService: MenuService,
private readonly actionService: ActionService,
private readonly logViewerService: LogViewerService
) {
super();
}
register(): void {
this.mainMenuService.registerMenuItem(
EMainMenu.mainMenuToolsPanel,
{
id: 'openLogViewer',
order: 1,
type: 'checkbox',
isChecked: () => this.logViewerService.isActive,
title: 'app_shared_toolsMenu_logViewer',
onClick: () => this.logViewerService.toggle(),
}
);
this.menuService.addCreator({
isApplicable: context => context.tryGet(DATA_CONTEXT_MENU) === MENU_TOOLS,
getItems: (context, items) => [
...items,
ACTION_LOG_VIEWER_ENABLE,
],
});
this.actionService.addHandler({
id: 'log-viewer-base',
isActionApplicable: (context, action) => [
ACTION_LOG_VIEWER_ENABLE,
].includes(action),
isChecked: () => this.logViewerService.isActive,
handler: (context, action) => {
switch (action) {
case ACTION_LOG_VIEWER_ENABLE: {
this.logViewerService.toggle();
break;
}
}
},
});
this.toolsPanelService.tabsContainer.add({
key: 'log-viewer-tab',
order: 0,
name: 'app_shared_toolsMenu_logViewer',
name: 'plugin_log_viewer_action_enable_label',
isHidden: () => !this.logViewerService.isActive,
onClose: () => this.logViewerService.toggle(),
panel: () => LogViewer,
@@ -0,0 +1,4 @@
export default [
['plugin_log_viewer_action_enable_label', 'Log Viewer'],
['plugin_log_viewer_placeholder', 'Log viewer is not active'],
];
@@ -0,0 +1,4 @@
export default [
['plugin_log_viewer_action_enable_label', 'Visualizzatore del Log'],
['plugin_log_viewer_placeholder', 'Il visualizzatore del log non è attivo'],
];
@@ -0,0 +1,4 @@
export default [
['plugin_log_viewer_action_enable_label', 'Просмотр лога'],
['plugin_log_viewer_placeholder', 'Просмотр логов неактивен'],
];
@@ -0,0 +1,4 @@
export default [
['plugin_log_viewer_action_enable_label', '日志查看器'],
['plugin_log_viewer_placeholder', '日志查看器未激活'],
];
@@ -8,6 +8,7 @@
import type { PluginManifest } from '@cloudbeaver/core-di';
import { LocaleService } from './LocaleService';
import { LogViewerBootstrap } from './LogViewer/LogViewerBootstrap';
import { LogViewerService } from './LogViewer/LogViewerService';
import { LogViewerSettingsService } from './LogViewer/LogViewerSettingsService';
@@ -17,6 +18,7 @@ export const logViewerPlugin: PluginManifest = {
providers: [
LogViewerBootstrap,
LogViewerService,
LocaleService,
LogViewerSettingsService,
],
};
@@ -9,8 +9,8 @@
import { observer } from 'mobx-react-lite';
import styled, { css } from 'reshadow';
import { ILoadableState, Loader, Translate, TreeNodeNestedMessage, TREE_NODE_STYLES } from '@cloudbeaver/core-blocks';
import { Loader, Translate, TreeNodeNestedMessage, TREE_NODE_STYLES } from '@cloudbeaver/core-blocks';
import type { ILoadableState } from '@cloudbeaver/core-utils';
import type { IElementsTreeContext } from './ElementsTreeContext';
@@ -50,7 +50,7 @@ const MENU_STYLES = css`
width: 16px;
}
& item-label {
& menu-bar-item-label {
display: none;
}
@@ -9,7 +9,7 @@
import { action, computed, observable, runInAction } from 'mobx';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { IFolderExplorerContext, ILoadableState, useExecutor, useMapResource, useObjectRef, useObservableRef, useUserData } from '@cloudbeaver/core-blocks';
import { IFolderExplorerContext, useExecutor, useMapResource, useObjectRef, useObservableRef, useUserData } from '@cloudbeaver/core-blocks';
import { ConnectionInfoResource } from '@cloudbeaver/core-connections';
import { useService } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
@@ -17,7 +17,7 @@ import { ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor';
import { type NavNode, NavNodeInfoResource, NavTreeResource } from '@cloudbeaver/core-navigation-tree';
import { ProjectInfoResource, ProjectsService } from '@cloudbeaver/core-projects';
import { CachedMapAllKey, ResourceKeyUtils } from '@cloudbeaver/core-sdk';
import { MetadataMap, throttle } from '@cloudbeaver/core-utils';
import { ILoadableState, MetadataMap, throttle } from '@cloudbeaver/core-utils';
import type { IElementsTreeAction } from './IElementsTreeAction';
import type { NavigationNodeRendererComponent } from './NavigationNodeComponent';
@@ -0,0 +1,17 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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_RESOURCE_MANAGER = createAction(
'resource-manager-enable',
{
label: 'plugin_resource_manager_action_enable_label',
type: 'checkbox',
}
);
@@ -7,35 +7,61 @@
*/
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { ServerConfigResource } from '@cloudbeaver/core-root';
import { getCachedDataResourceLoaderState } from '@cloudbeaver/core-sdk';
import { SideBarPanelService } from '@cloudbeaver/core-ui';
import { EMainMenu, MainMenuService } from '@cloudbeaver/plugin-top-app-bar';
import { ActionService, DATA_CONTEXT_LOADABLE_STATE, DATA_CONTEXT_MENU, MenuService } from '@cloudbeaver/core-view';
import { MENU_TOOLS } from '@cloudbeaver/plugin-tools-panel';
import { ACTION_RESOURCE_MANAGER } from './Actions/ACTION_RESOURCE_MANAGER';
import { ResourceManager } from './ResourceManager';
import { ResourceManagerService } from './ResourceManagerService';
@injectable()
export class PluginBootstrap extends Bootstrap {
constructor(
private readonly mainMenuService: MainMenuService,
private readonly resourceManagerService: ResourceManagerService,
private readonly sideBarPanelService: SideBarPanelService,
private readonly menuService: MenuService,
private readonly actionService: ActionService,
private readonly serverConfigResource: ServerConfigResource
) {
super();
}
register(): void | Promise<void> {
this.mainMenuService.registerMenuItem(
EMainMenu.mainMenuToolsPanel,
{
id: 'resourceManagementTrigger',
order: 3,
type: 'checkbox',
title: 'plugin_resource_manager_title',
isHidden: () => !this.resourceManagerService.enabled,
isChecked: () => this.resourceManagerService.active,
onClick: this.resourceManagerService.togglePanel,
}
);
this.menuService.addCreator({
isApplicable: context => context.tryGet(DATA_CONTEXT_MENU) === MENU_TOOLS,
getItems: (context, items) => [
...items,
ACTION_RESOURCE_MANAGER,
],
});
this.actionService.addHandler({
id: 'resource-manager-base',
isActionApplicable: (context, action) => [
ACTION_RESOURCE_MANAGER,
].includes(action),
isHidden: () => !this.resourceManagerService.enabled,
isChecked: () => this.resourceManagerService.active,
getLoader: (context, action) => {
const state = context.get(DATA_CONTEXT_LOADABLE_STATE);
return state.getState(
action.id,
() => getCachedDataResourceLoaderState(this.serverConfigResource, undefined, undefined)
);
},
handler: (context, action) => {
switch (action) {
case ACTION_RESOURCE_MANAGER: {
this.resourceManagerService.togglePanel();
break;
}
}
},
});
this.sideBarPanelService.tabsContainer.add({
key: 'resource-manager-tab',
@@ -1,4 +1,5 @@
export default [
['plugin_resource_manager_action_enable_label', 'Resource manager'],
['plugin_resource_manager_title', 'Resource manager'],
['plugin_resource_manager_no_resources_placeholder', 'There are no resources yet'],
['plugin_resource_manager_script_name', 'Script name'],
@@ -1,4 +1,5 @@
export default [
['plugin_resource_manager_action_enable_label', 'Resource manager'],
['plugin_resource_manager_title', 'Resource manager'],
['plugin_resource_manager_no_resources_placeholder', 'No resources'],
['plugin_resource_manager_script_name', 'Script name'],
@@ -1,4 +1,5 @@
export default [
['plugin_resource_manager_action_enable_label', 'Управление ресурсами'],
['plugin_resource_manager_title', 'Управление ресурсами'],
['plugin_resource_manager_no_resources_placeholder', 'Нет ресурсов'],
['plugin_resource_manager_script_name', 'Имя скрипта'],
@@ -1,4 +1,5 @@
export default [
['plugin_resource_manager_action_enable_label', '资源管理器'],
['plugin_resource_manager_title', '资源管理器'],
['plugin_resource_manager_no_resources_placeholder', '无资源'],
['plugin_resource_manager_script_name', '脚本名称'],
@@ -0,0 +1,16 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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_SQL_EDITOR_NEW = createAction(
'sql-editor-new',
{
label: 'plugin_sql_editor_navigation_tab_action_sql_editor_new',
}
);
@@ -0,0 +1,45 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2019-2022 DBeaver Corp
*
* All Rights Reserved
*
* NOTICE: All information contained herein is, and remains
* the property of DBeaver Corp and its suppliers, if any.
* The intellectual and technical concepts contained
* herein are proprietary to DBeaver Corp and its suppliers
* and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from DBeaver Corp.
*/
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { LocalizationService } from '@cloudbeaver/core-localization';
@injectable()
export class LocaleService extends Bootstrap {
constructor(private readonly localizationService: LocalizationService) {
super();
}
register(): void | Promise<void> {
this.localizationService.addProvider(this.provider.bind(this));
}
load(): void | Promise<void> { }
private async provider(locale: string) {
switch (locale) {
case 'ru':
return (await import('./locales/ru')).default;
case 'it':
return (await import('./locales/it')).default;
case 'zh':
return (await import('./locales/zh')).default;
default:
return (await import('./locales/en')).default;
}
}
}
@@ -13,13 +13,14 @@ import type { IExecutorHandler } from '@cloudbeaver/core-executor';
import { ExtensionUtils } from '@cloudbeaver/core-extensions';
import { DATA_CONTEXT_NAV_NODE, EObjectFeature } from '@cloudbeaver/core-navigation-tree';
import { ISessionAction, sessionActionContext, SessionActionService } from '@cloudbeaver/core-root';
import { ActionService, ACTION_RENAME, DATA_CONTEXT_MENU_NESTED, menuExtractActions, MenuService, ViewService } from '@cloudbeaver/core-view';
import { DATA_CONTEXT_CONNECTION } from '@cloudbeaver/plugin-connections';
import { ActionService, ACTION_RENAME, DATA_CONTEXT_MENU, DATA_CONTEXT_MENU_NESTED, menuExtractActions, MenuService, ViewService } from '@cloudbeaver/core-view';
import { DATA_CONTEXT_CONNECTION, MENU_CONNECTIONS } from '@cloudbeaver/plugin-connections';
import { ConnectionSchemaManagerService } from '@cloudbeaver/plugin-datasource-context-switch';
import { NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs';
import { DATA_CONTEXT_SQL_EDITOR_STATE, ESqlDataSourceFeatures, getSqlEditorName, LocalStorageSqlDataSource, SqlDataSourceService, SqlEditorService } from '@cloudbeaver/plugin-sql-editor';
import { MainMenuService } from '@cloudbeaver/plugin-top-app-bar';
import { TOP_APP_BAR_MENU } from '@cloudbeaver/plugin-top-app-bar';
import { ACTION_SQL_EDITOR_NEW } from './ACTION_SQL_EDITOR_NEW';
import { ACTION_SQL_EDITOR_OPEN } from './ACTION_SQL_EDITOR_OPEN';
import { DATA_CONTEXT_SQL_EDITOR_TAB } from './DATA_CONTEXT_SQL_EDITOR_TAB';
import { isSessionActionOpenSQLEditor } from './sessionActionOpenSQLEditor';
@@ -30,7 +31,6 @@ import { SqlEditorTabService } from './SqlEditorTabService';
@injectable()
export class SqlEditorBootstrap extends Bootstrap {
constructor(
private readonly mainMenuService: MainMenuService,
private readonly sqlEditorNavigatorService: SqlEditorNavigatorService,
private readonly navigationTabsService: NavigationTabsService,
private readonly connectionSchemaManagerService: ConnectionSchemaManagerService,
@@ -48,14 +48,7 @@ export class SqlEditorBootstrap extends Bootstrap {
}
register(): void {
this.mainMenuService.registerRootItem(
{
id: 'sql-editor',
title: 'SQL',
order: 2,
onClick: this.openSQLEditor.bind(this),
}
);
this.registerTopAppBarItem();
this.menuService.addCreator({
isApplicable: context => context.has(DATA_CONTEXT_SQL_EDITOR_STATE) && context.has(DATA_CONTEXT_SQL_EDITOR_TAB),
@@ -179,6 +172,48 @@ export class SqlEditorBootstrap extends Bootstrap {
load(): void { }
private registerTopAppBarItem() {
this.menuService.addCreator({
isApplicable: context => context.tryGet(DATA_CONTEXT_MENU) === TOP_APP_BAR_MENU,
getItems: (context, items) => [
...items,
ACTION_SQL_EDITOR_NEW,
],
orderItems: (context, items) => {
let placeIndex = items.indexOf(ACTION_SQL_EDITOR_NEW);
const actionsOpen = menuExtractActions(items, [
ACTION_SQL_EDITOR_NEW,
]);
const connectionsIndex = items.indexOf(MENU_CONNECTIONS);
if (connectionsIndex !== -1) {
placeIndex = connectionsIndex + 1;
}
items.splice(placeIndex, 0, ...actionsOpen);
return items;
},
});
this.actionService.addHandler({
id: 'sql-editor-new',
isActionApplicable: (context, action) => [
ACTION_SQL_EDITOR_NEW,
].includes(action),
handler: (context, action) => {
switch (action) {
case ACTION_SQL_EDITOR_NEW: {
this.openSQLEditor();
break;
}
}
},
});
}
private openSQLEditor() {
let connectionKey: IConnectionInfoParams | undefined;
let catalogId: string | undefined;
@@ -0,0 +1,3 @@
export default [
['plugin_sql_editor_navigation_tab_action_sql_editor_new', 'SQL'],
];
@@ -0,0 +1,2 @@
export default [
];
@@ -0,0 +1,2 @@
export default [
];
@@ -0,0 +1,2 @@
export default [
];
@@ -8,6 +8,7 @@
import type { PluginManifest } from '@cloudbeaver/core-di';
import { LocaleService } from './LocaleService';
import { SqlEditorBootstrap } from './SqlEditorBootstrap';
import { SqlEditorNavigatorService } from './SqlEditorNavigatorService';
import { SqlEditorTabService } from './SqlEditorTabService';
@@ -21,5 +22,6 @@ export const sqlEditorTabPluginManifest: PluginManifest = {
SqlEditorBootstrap,
SqlEditorTabService,
SqlEditorNavigatorService,
LocaleService,
],
};
@@ -6,9 +6,9 @@
* you may not use this file except in compliance with the License.
*/
import type { ILoadableState } from '@cloudbeaver/core-blocks';
import type { IConnectionExecutionContextInfo } from '@cloudbeaver/core-connections';
import type { ISyncExecutor } from '@cloudbeaver/core-executor';
import type { ILoadableState } from '@cloudbeaver/core-utils';
import type { ESqlDataSourceFeatures } from './ESqlDataSourceFeatures';
@@ -43,13 +43,13 @@ export const SQL_EDITOR_ACTIONS_MENU_STYLES = css`
width: 24px;
}
& item-label {
& menu-bar-item-label {
display: block;
text-transform: uppercase;
font-weight: 700;
}
& IconOrImage + item-label, & Loader + item-label {
& IconOrImage + menu-bar-item-label, & Loader + menu-bar-item-label {
padding-left: 8px
}
}
@@ -38,7 +38,8 @@
"@cloudbeaver/core-utils": "~0.1.0",
"@cloudbeaver/core-view": "~0.1.0",
"@cloudbeaver/plugin-version": "~0.1.0",
"@cloudbeaver/plugin-settings-menu": "~0.1.0"
"@cloudbeaver/plugin-settings-menu": "~0.1.0",
"@cloudbeaver/plugin-top-app-bar": "~0.1.0"
},
"peerDependencies": {
"react": "~18.x.x",
@@ -0,0 +1,45 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2019-2022 DBeaver Corp
*
* All Rights Reserved
*
* NOTICE: All information contained herein is, and remains
* the property of DBeaver Corp and its suppliers, if any.
* The intellectual and technical concepts contained
* herein are proprietary to DBeaver Corp and its suppliers
* and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from DBeaver Corp.
*/
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { LocalizationService } from '@cloudbeaver/core-localization';
@injectable()
export class LocaleService extends Bootstrap {
constructor(private readonly localizationService: LocalizationService) {
super();
}
register(): void | Promise<void> {
this.localizationService.addProvider(this.provider.bind(this));
}
load(): void | Promise<void> { }
private async provider(locale: string) {
switch (locale) {
case 'ru':
return (await import('./locales/ru')).default;
case 'it':
return (await import('./locales/it')).default;
case 'zh':
return (await import('./locales/zh')).default;
default:
return (await import('./locales/en')).default;
}
}
}
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2022 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_TOOLS = createMenu(
'tools',
'plugin_tools_panel_menu_title'
);
@@ -7,14 +7,28 @@
*/
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { DATA_CONTEXT_MENU, MenuService } from '@cloudbeaver/core-view';
import { TOP_APP_BAR_MENU } from '@cloudbeaver/plugin-top-app-bar';
import { MENU_TOOLS } from './Menu/MENU_TOOLS';
@injectable()
export class PluginBootstrap extends Bootstrap {
constructor() {
constructor(
private readonly menuService: MenuService,
) {
super();
}
register(): void | Promise<void> { }
register(): void {
this.menuService.addCreator({
isApplicable: context => context.tryGet(DATA_CONTEXT_MENU) === TOP_APP_BAR_MENU,
getItems: (context, items) => [
...items,
MENU_TOOLS,
],
});
}
load(): void | Promise<void> { }
}
@@ -1,3 +1,4 @@
export * from './Menu/MENU_TOOLS';
export * from './ToolsPanel/ToolsPanelService';
export * from './ToolsPanel/ToolsPanel';
@@ -0,0 +1,3 @@
export default [
['plugin_tools_panel_menu_title', 'Tools'],
];
@@ -0,0 +1,3 @@
export default [
['plugin_tools_panel_menu_title', 'Strumenti'],
];
@@ -0,0 +1,3 @@
export default [
['plugin_tools_panel_menu_title', 'Инструменты'],
];

Some files were not shown because too many files have changed in this diff Show More