Merge remote-tracking branch 'origin/devel' into devel

This commit is contained in:
Serge Rider
2020-10-21 19:20:14 +03:00
18 changed files with 219 additions and 190 deletions
@@ -6,7 +6,8 @@
* you may not use this file except in compliance with the License.
*/
import { observer, useLocalStore } from 'mobx-react';
import { observer } from 'mobx-react';
import { useMemo } from 'react';
import styled, { css } from 'reshadow';
import { Loader } from '@cloudbeaver/core-blocks';
@@ -53,12 +54,10 @@ export const ElementsTree: React.FC<Props> = observer(function ElementsTree({
const nodeChildren = useChildren(root);
const Placeholder = emptyPlaceholder;
const context = useLocalStore<ITreeContext, ITreeContext>(source => source, {
control,
onOpen,
onSelect,
isSelected,
});
const context = useMemo<ITreeContext>(
() => ({ control, onOpen, onSelect, isSelected }),
[control, onOpen, onSelect, isSelected]
);
if (!nodeChildren.children || nodeChildren.children.length === 0) {
if (nodeChildren.isLoading()) {
+2
View File
@@ -22,6 +22,8 @@ export * from './shared/NavigationTabs/TabNavigationContext';
export * from './shared/ToolsPanel/LogViewTab/LogViewerMenuService';
export * from './shared/ToolsPanel/LogViewTab/LogViewerService';
export * from './shared/SessionExpireDialog/SessionExpiredDialogService';
export * from './NavigationTree/ElementsTree';
export * from './NavigationTree/TreeContext';
export * from './NavigationTree/NavigationTreeService';
export * from './NavigationTree/NavigationTreeContextMenuService';
export * from './AppLocaleService';
@@ -120,7 +120,7 @@ export class NavNodeManagerService extends Bootstrap {
this.connectionInfo.onItemAdd.subscribe(this.connectionUpdateHandler.bind(this));
this.connectionInfo.onItemDelete.subscribe(this.connectionRemoveHandler.bind(this));
this.connectionInfo.onConnectionCreate.subscribe(this.connectionCreateHandler.bind(this));
this.sessionResource.onDataUpdate.subscribe(this.updateRootChildren.bind(this));
this.sessionResource.onDataUpdate.subscribe(this.refreshRoot.bind(this));
}
load(): void {}
@@ -319,7 +319,13 @@ export class NavNodeManagerService extends Bootstrap {
};
};
private async updateRootChildren() {
async updateRoot(): Promise<void> {
if (await this.isNavTreeEnabled()) {
await this.navTree.refresh(ROOT_NODE_PATH);
}
}
async refreshRoot(): Promise<void> {
this.navTree.delete(ROOT_NODE_PATH);
if (await this.isNavTreeEnabled()) {
await this.navTree.refresh(ROOT_NODE_PATH);
@@ -41,6 +41,7 @@ const checkboxStyles = composes(
}
checkbox-label {
composes: theme-typography--body2 from global;
cursor: pointer;
}
`
);
@@ -6,8 +6,8 @@
* you may not use this file except in compliance with the License.
*/
import { observer, useLocalStore } from 'mobx-react';
import { useRef } from 'react';
import { observer } from 'mobx-react';
import { useMemo, useRef } from 'react';
import styled, { use } from 'reshadow';
import { useStyles } from '@cloudbeaver/core-theming';
@@ -50,7 +50,7 @@ export const TreeNode: React.FC<Props> = observer(function TreeNode({
handlersRef.current.onOpen?.();
};
const nodeContext = useLocalStore<ITreeNodeContext, ITreeNodeContext>(source => source, {
const nodeContext = useMemo<ITreeNodeContext>(() => ({
loading,
selected,
expanded,
@@ -58,16 +58,7 @@ export const TreeNode: React.FC<Props> = observer(function TreeNode({
expand: handleExpand,
select: handleSelect,
open: handleOpen,
});
// const nodeContext = useMemo<ITreeNodeContext>(() => ({
// loading,
// selected,
// expanded,
// leaf,
// expand: handleExpand,
// select: handleSelect,
// open: handleOpen,
// }), [loading, selected, expanded, leaf, handleExpand, handleSelect, handleOpen]);
}), [loading, selected, expanded, leaf, handleExpand, handleSelect, handleOpen]);
return styled(useStyles(TREE_NODE_STYLES))(
<node as="div" {...use({ expanded })} className={className}>
@@ -28,7 +28,6 @@ import { useStyles } from '@cloudbeaver/core-theming';
import { EConnectionType } from '../EConnectionType';
import { IConnectionFormModel } from '../IConnectionFormModel';
import { formStyles } from './formStyles';
import { OptionsController } from './OptionsController';
import { ParametersForm } from './ParametersForm';
@@ -52,6 +51,12 @@ const styles = css`
flex-direction: column;
max-width: 630px;
}
connection-type {
margin-left: 180px;
}
Radio {
composes: theme-typography--body2 from global;
}
`;
export const Options = observer(function Options({
@@ -66,7 +71,7 @@ export const Options = observer(function Options({
const translate = useTranslate();
const [focusedRef] = useFocus<HTMLFormElement>({ focusFirstChild: true });
return styled(useStyles(styles, formStyles))(
return styled(useStyles(styles))(
<SubmittingForm ref={focusedRef} onChange={controller.onFormChange} onSubmit={onSave}>
<FormBox>
<FormBoxElement>
@@ -7,21 +7,25 @@
*/
import { observer } from 'mobx-react';
import styled, { use, css } from 'reshadow';
import styled, { css, use } from 'reshadow';
import { FormGroup, InputField } from '@cloudbeaver/core-blocks';
import { useTranslate } from '@cloudbeaver/core-localization';
import { ConnectionInfo } from '@cloudbeaver/core-sdk';
import { useStyles } from '@cloudbeaver/core-theming';
import { formStyles } from './formStyles';
interface ParametersFormProps {
connection: ConnectionInfo;
disabled?: boolean;
embedded?: boolean;
}
const parametersFormStyles = css`
sub-label {
composes: theme-typography--caption from global;
line-height: 14px;
}`;
export const ParametersForm = observer(function ParametersForm({
connection,
embedded,
@@ -29,7 +33,7 @@ export const ParametersForm = observer(function ParametersForm({
}: ParametersFormProps) {
const translate = useTranslate();
return styled(useStyles(formStyles))(
return styled(useStyles(parametersFormStyles))(
<>
{!embedded && (
<layout-grid-inner as="div">
@@ -1,31 +0,0 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 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 { css } from 'reshadow';
export const formStyles = css`
connection-form {
flex: 1;
display: flex;
overflow: auto;
flex-direction: row;
}
layout-grid {
flex: 1;
}
connection-type {
margin-left: 180px;
}
Radio {
composes: theme-typography--body1 from global;
}
sub-label {
composes: theme-typography--caption from global;
line-height: 14px;
}
`;
@@ -17,10 +17,17 @@ import { GQLErrorCatcher } from '@cloudbeaver/core-sdk';
import { ConnectionInfoResource } from '../ConnectionInfoResource';
import { DBDriverResource } from '../DBDriverResource';
interface IDBAuthConfig {
credentials: any;
saveCredentials: boolean;
}
@injectable()
export class DBAuthDialogController implements IInitializableController, IDestructibleController {
@observable isAuthenticating = false;
@observable credentials = {};
@observable config: IDBAuthConfig = {
credentials: {},
saveCredentials: false,
};
readonly error = new GQLErrorCatcher();
@@ -53,7 +60,7 @@ export class DBAuthDialogController implements IInitializableController, IDestru
this.isAuthenticating = true;
try {
await this.connectionInfoResource.init(this.connectionId, this.credentials);
await this.connectionInfoResource.init(this.connectionId, this.config.credentials);
this.close();
} catch (exception) {
if (!this.error.catch(exception) || this.isDistructed) {
@@ -10,10 +10,11 @@ import { observer } from 'mobx-react';
import styled, { css } from 'reshadow';
import {
SubmittingForm, ErrorMessage, Loader, useFocus, ObjectPropertyInfoForm
SubmittingForm, ErrorMessage, Loader, useFocus, ObjectPropertyInfoForm, FieldCheckbox, FormBox, FormBoxElement, FormGroup
} from '@cloudbeaver/core-blocks';
import { useController } from '@cloudbeaver/core-di';
import { CommonDialogWrapper, DialogComponentProps } from '@cloudbeaver/core-dialogs';
import { useTranslate } from '@cloudbeaver/core-localization';
import { composes, useStyles } from '@cloudbeaver/core-theming';
import { useConnectionInfo } from '../useConnectionInfo';
@@ -46,6 +47,10 @@ const styles = composes(
ObjectPropertyInfoForm {
align-items: center;
justify-content: center;
display: inline-flex;
}
FormBox {
align-items: center;
}
ErrorMessage {
position: sticky;
@@ -64,6 +69,7 @@ export const DatabaseAuthDialog = observer(function DatabaseAuthDialog({
const [focusedRef] = useFocus<HTMLFormElement>({ focusFirstChild: true });
const { driver } = useDBDriver(connection.connectionInfo?.driverId || '');
const controller = useController(DBAuthDialogController, payload, rejectDialog);
const translate = useTranslate();
return styled(useStyles(styles))(
<CommonDialogWrapper
@@ -82,12 +88,26 @@ export const DatabaseAuthDialog = observer(function DatabaseAuthDialog({
? <Loader />
: (
<SubmittingForm ref={focusedRef} onSubmit={controller.login}>
<ObjectPropertyInfoForm
autofillToken={`section-${connection.connectionInfo?.id || ''} section-auth`}
properties={connection.connectionInfo?.authProperties}
credentials={controller.credentials}
disabled={controller.isAuthenticating}
/>
<FormBox>
<FormBoxElement>
<ObjectPropertyInfoForm
autofillToken={`section-${connection.connectionInfo?.id || ''} section-auth`}
properties={connection.connectionInfo?.authProperties}
credentials={controller.config.credentials}
disabled={controller.isAuthenticating}
/>
<FormGroup>
<FieldCheckbox
name="saveCredentials"
value={connection.connectionInfo?.id || 'DBAuthSaveCredentials'}
checkboxLabel={translate('connections_connection_edit_save_credentials')}
disabled={controller.isAuthenticating}
state={controller.config}
mod='surface'
/>
</FormGroup>
</FormBoxElement>
</FormBox>
</SubmittingForm>
)}
{controller.error.responseMessage && (
@@ -34,6 +34,7 @@ export const ServerConfigurationConfigurationForm: React.FC<Props> = observer(fu
mod={['primary']}
disabled={!serverConfig.authenticationEnabled}
long
autoHide
>
{translate('administration_configuration_wizard_configuration_anonymous_access')}
</Switch>
@@ -45,6 +46,7 @@ export const ServerConfigurationConfigurationForm: React.FC<Props> = observer(fu
description={translate('administration_configuration_wizard_configuration_authentication_description')}
mod={['primary']}
long
autoHide
>
{translate('administration_configuration_wizard_configuration_authentication')}
</Switch>
@@ -56,6 +58,7 @@ export const ServerConfigurationConfigurationForm: React.FC<Props> = observer(fu
description={translate('administration_configuration_wizard_configuration_custom_connections_description')}
mod={['primary']}
long
autoHide
>
{translate('administration_configuration_wizard_configuration_custom_connections')}
</Switch>
@@ -10,7 +10,7 @@ import { observer } from 'mobx-react';
import styled, { use, css } from 'reshadow';
import { AdministrationTools, ADMINISTRATION_TOOLS_STYLES } from '@cloudbeaver/core-administration';
import { FormBox, FormBoxElement, IconButton, SubmittingForm, useFocus } from '@cloudbeaver/core-blocks';
import { FormBox, FormBoxElement, IconButton, Loader, SubmittingForm, useFocus } from '@cloudbeaver/core-blocks';
import { useController, useService } from '@cloudbeaver/core-di';
import { useFormValidator } from '@cloudbeaver/core-executor';
import { useTranslate } from '@cloudbeaver/core-localization';
@@ -44,6 +44,8 @@ const styles = composes(
layout-grid-cell {
position: relative;
display: flex;
flex-direction: column;
border: solid 1px;
}
@@ -62,6 +64,10 @@ const styles = composes(
flex-direction: row;
}
FormBox {
flex: 0;
}
p {
line-height: 2;
}
@@ -70,12 +76,13 @@ const styles = composes(
export const ServerConfigurationPage = observer(function ServerConfigurationPage() {
const translate = useTranslate();
const style = useStyles(styles, ADMINISTRATION_TOOLS_STYLES);
const [focusedRef] = useFocus<HTMLFormElement>({ focusFirstChild: true });
const service = useService(ServerConfigurationService);
const controller = useController(ServerConfigurationPageController);
useFormValidator(service.validationTask, focusedRef);
return styled(useStyles(styles, ADMINISTRATION_TOOLS_STYLES))(
return styled(style)(
<layout-grid as="div">
<layout-grid-inner as="div">
<layout-grid-cell as='div' {...use({ span: 12 })}>
@@ -91,15 +98,23 @@ export const ServerConfigurationPage = observer(function ServerConfigurationPage
</AdministrationTools>
)}
<SubmittingForm ref={focusedRef} name='server_config' onSubmit={controller.save} onChange={controller.change}>
<FormBox>
<FormBoxElement>
<ServerConfigurationInfoForm serverConfig={controller.state.serverConfig} />
{!controller.editing && <ServerConfigurationAdminForm serverConfig={controller.state.serverConfig} />}
</FormBoxElement>
<FormBoxElement>
<ServerConfigurationConfigurationForm serverConfig={controller.state.serverConfig} />
</FormBoxElement>
</FormBox>
{service.loading ? (
<Loader />
) : (
<>
<FormBox>
<FormBoxElement>
<ServerConfigurationInfoForm serverConfig={controller.state.serverConfig} />
{!controller.editing && (
<ServerConfigurationAdminForm serverConfig={controller.state.serverConfig} />
)}
</FormBoxElement>
<FormBoxElement>
<ServerConfigurationConfigurationForm serverConfig={controller.state.serverConfig} />
</FormBoxElement>
</FormBox>
</>
)}
</SubmittingForm>
</layout-grid-cell>
</layout-grid-inner>
@@ -26,6 +26,7 @@ export interface IValidationStatusContext {
@injectable()
export class ServerConfigurationService {
@observable state: IServerConfigurationPageState;
@observable loading: boolean;
readonly validationTask: IExecutor<boolean>;
constructor(
@@ -35,19 +36,27 @@ export class ServerConfigurationService {
private readonly notificationService: NotificationService,
private readonly usersResource: UsersResource
) {
this.loading = true;
this.state = this.getConfig();
this.validationTask = new Executor();
}
async loadConfig(): Promise<void> {
const config = await this.serverConfigResource.load(null);
this.loading = true;
try {
const config = await this.serverConfigResource.load(null);
this.state = this.administrationScreenService
.getItemState(
'server-configuration',
() => this.getConfig(config),
!config?.configurationMode
);
this.state = this.administrationScreenService
.getItemState(
'server-configuration',
() => this.getConfig(config),
!config?.configurationMode
);
} catch (exception) {
this.notificationService.logException(exception, 'Can\'t load server configuration');
} finally {
this.loading = false;
}
}
isDone(): boolean {
@@ -7,17 +7,16 @@
*/
import { observer } from 'mobx-react';
import styled from 'reshadow';
import styled, { css } from 'reshadow';
import {
Radio, InputField, useFocus, ObjectPropertyInfoForm, InputGroup
Radio, InputField, useFocus, ObjectPropertyInfoForm, InputGroup, FieldCheckbox, SubmittingForm, FormBox, FormBoxElement, FormGroup, RadioGroup
} from '@cloudbeaver/core-blocks';
import { DBDriver } from '@cloudbeaver/core-connections';
import { useTranslate } from '@cloudbeaver/core-localization';
import { useStyles } from '@cloudbeaver/core-theming';
import { ConnectionType } from '../ConnectionFormDialogController';
import { formStyles } from './formStyles';
import { IFormController } from './IFormController';
import { ParametersForm } from './ParametersForm';
@@ -26,68 +25,99 @@ interface ConnectionFormProps {
controller: IFormController;
}
const connectionFormStyles = css`
SubmittingForm {
flex: 1;
}
FormBoxElement {
flex: 1;
}
FormBox {
padding: 4px 24px 18px;
}
connection-type {
padding: 12px;
margin-left: 160px;
}
Radio {
composes: theme-typography--body2 from global;
font-weight: 500;
padding: 0 12px;
}
`;
export const ConnectionForm = observer(function ConnectionForm({
driver,
controller,
}: ConnectionFormProps) {
const translate = useTranslate();
const [focusedRef] = useFocus<HTMLDivElement>({ focusFirstChild: true });
const [focusedRef] = useFocus<HTMLFormElement>({ focusFirstChild: true });
return styled(useStyles(formStyles))(
<connection-form as='div'>
<connection-type ref={focusedRef} as="div">
<Radio
name="type"
id="custom"
value="custom"
checked={controller.connectionType === ConnectionType.Attributes}
disabled={controller.isConnecting}
mod={['primary']}
onClick={() => controller.onChangeType(ConnectionType.Attributes)}
>
{translate('customConnection_connectionType_custom')}
</Radio>
<Radio
name="type"
id="url"
value="url"
checked={controller.connectionType === ConnectionType.URL}
disabled={controller.isConnecting}
mod={['primary']}
onClick={() => controller.onChangeType(ConnectionType.URL)}
>
{translate('customConnection_connectionType_url')}
</Radio>
</connection-type>
{controller.connectionType === ConnectionType.Attributes ? (
<ParametersForm controller={controller} embedded={driver?.embedded} />
) : (
<group as="div">
<InputField
type="text"
name="url"
value={controller.config.url}
disabled={controller.isConnecting}
mod='surface'
onChange={value => controller.onChange('url', value)}
>
{translate('customConnection_url_JDBC')}
</InputField>
</group>
)}
{controller.authModel && (
<>
<group as="div">
<InputGroup>{translate('connections_connection_edit_authentication')}</InputGroup>
</group>
<ObjectPropertyInfoForm
autofillToken='new-password'
properties={controller.authModel.properties}
credentials={controller.config.credentials}
disabled={controller.isConnecting}
/>
</>
)}
</connection-form>
return styled(useStyles(connectionFormStyles))(
<SubmittingForm ref={focusedRef}>
<FormBox>
<FormBoxElement>
<FormGroup>
<connection-type as="div">
<RadioGroup name='type' value={controller.connectionType} onChange={controller.onChangeType}>
<Radio
value={ConnectionType.Attributes}
disabled={controller.isConnecting}
mod={['primary']}
>
{translate('customConnection_connectionType_custom')}
</Radio>
<Radio
value={ConnectionType.URL}
disabled={controller.isConnecting}
mod={['primary']}
>
{translate('customConnection_connectionType_url')}
</Radio>
</RadioGroup>
</connection-type>
</FormGroup>
{controller.connectionType === ConnectionType.Attributes ? (
<ParametersForm controller={controller} embedded={driver?.embedded} />
) : (
<FormGroup>
<InputField
type="text"
name="url"
value={controller.config.url}
disabled={controller.isConnecting}
mod='surface'
onChange={value => controller.onChange('url', value)}
>
{translate('customConnection_url_JDBC')}
</InputField>
</FormGroup>
)}
{controller.authModel && (
<>
<FormGroup>
<InputGroup>{translate('connections_connection_edit_authentication')}</InputGroup>
</FormGroup>
<ObjectPropertyInfoForm
autofillToken='new-password'
properties={controller.authModel.properties}
credentials={controller.config.credentials}
disabled={controller.isConnecting}
/>
<FormGroup>
<FieldCheckbox
name="saveCredentials"
value={controller.authModel.id}
state={controller.config}
checkboxLabel={translate('connections_connection_edit_save_credentials')}
disabled={controller.isConnecting}
mod='surface'
/>
</FormGroup>
</>
)}
</FormBoxElement>
</FormBox>
</SubmittingForm>
);
});
@@ -7,13 +7,12 @@
*/
import { observer } from 'mobx-react';
import styled, { use } from 'reshadow';
import styled, { css, use } from 'reshadow';
import { InputField } from '@cloudbeaver/core-blocks';
import { useTranslate } from '@cloudbeaver/core-localization';
import { useStyles } from '@cloudbeaver/core-theming';
import { formStyles } from './formStyles';
import { IFormController } from './IFormController';
interface ParametersFormProps {
@@ -21,13 +20,19 @@ interface ParametersFormProps {
embedded?: boolean;
}
const parametersFormStyles = css`
sub-label {
composes: theme-typography--caption from global;
line-height: 14px;
}`;
export const ParametersForm = observer(function ParametersForm({
controller,
embedded,
}: ParametersFormProps) {
const translate = useTranslate();
return styled(useStyles(formStyles))(
return styled(useStyles(parametersFormStyles))(
<>
{!embedded && (
<layout-grid-inner as="div">
@@ -1,39 +0,0 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 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 { css } from 'reshadow';
export const formStyles = css`
connection-form {
flex: 1;
display: flex;
flex-direction: column;
padding: 18px 24px;
padding-top: 4px;
}
connection-type {
padding: 12px;
}
Radio {
composes: theme-typography--body1 from global;
font-weight: 500;
padding: 0 12px;
}
sub-label {
composes: theme-typography--caption from global;
line-height: 14px;
}
group {
box-sizing: border-box;
display: flex;
}
hr {
margin-left: 24px;
margin-right: 24px;
}
`;
@@ -40,8 +40,8 @@ const styles = composes(
box-sizing: border-box;
}
CommonDialogWrapper {
max-height: 550px;
min-height: 550px;
max-height: 594px;
min-height: 594px;
}
SubmittingForm {
flex: 1;
@@ -39,6 +39,7 @@ implements IInitializableController, IDestructibleController {
url: '',
properties: {},
credentials: {},
saveCredentials: false,
};
readonly error = new GQLErrorCatcher();
@@ -126,6 +127,7 @@ implements IInitializableController, IDestructibleController {
if (this.authModel) {
config.authModelId = this.config.authModelId;
config.credentials = this.config.credentials;
config.saveCredentials = this.config.saveCredentials;
}
if (Object.keys(this.config.properties).length > 0) {
config.properties = this.config.properties;