feat: CB-873 redesign driver properties

This commit is contained in:
Wroud
2021-04-26 22:14:47 +03:00
parent 59ed739d4f
commit ee3d3b8722
10 changed files with 101 additions and 57 deletions
@@ -29,6 +29,10 @@
</g>
</symbol>
<symbol id="add_sm" viewBox="-3 -3 14 14">
<path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M5 0H3v3H0v2h3v3h2V5h3V3H5V0z"/>
</symbol>
<symbol id="arrow" viewBox="0 0 16 16">
<path class="icon-fill" d="M8.245 10.66L4 6.4155L5.41567 5.001L8.245 7.831L11.0743 5L12.49 6.4155L8.245 10.66Z" />
</symbol>

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

+14 -2
View File
@@ -13,6 +13,7 @@ import { use } from 'reshadow';
import { useStyles, composes } from '@cloudbeaver/core-theming';
import { Icon } from './Icons';
import { Loader } from './Loader/Loader';
import { useObjectRef } from './useObjectRef';
@@ -21,6 +22,12 @@ const buttonStyles = composes(
Button {
composes: theme-button from global;
}
button-label {
composes: theme-button__label from global;
}
button-icon {
composes: theme-button__icon from global;
}
ripple {
composes: theme-button_ripple from global;
}
@@ -92,6 +99,8 @@ type ButtonProps = (
& React.LinkHTMLAttributes<HTMLLinkElement | HTMLButtonElement>
) & {
loading?: boolean;
icon?: string;
viewBox?: string;
mod?: Array<keyof typeof buttonMod>;
tag?: 'button' | 'a';
href?: string;
@@ -103,6 +112,8 @@ type ButtonProps = (
export const Button: React.FC<ButtonProps> = observer(function Button({
children,
icon,
viewBox,
mod,
tag = 'button',
disabled = false,
@@ -139,8 +150,9 @@ export const Button: React.FC<ButtonProps> = observer(function Button({
const Button = tag;
return styled(useStyles(buttonStyles, ...(mod || []).map(mod => buttonMod[mod])))(
<Button {...rest} disabled={disabled} {...use({ loading })} className={className} onClick={state.click}>
<ripple as="div" />
<button-label as='div'>{children}</button-label>
<ripple />
{icon && <button-icon><Icon name={icon} viewBox={viewBox} /></button-icon>}
<button-label as='span'>{children}</button-label>
<Loader small />
</Button>
);
@@ -71,6 +71,11 @@ export const BASE_CONTAINERS_STYLES = composes(
margin-right: 25%;
}
&[box] {
padding: 0;
overflow: hidden;
}
&[center] {
margin: 0 auto;
}
@@ -12,6 +12,7 @@ interface Props extends IContainerProps {
className?: string;
form?: boolean;
center?: boolean;
box?: boolean;
}
export const Group: React.FC<Props> = function Group({ children, className }) {
@@ -6,8 +6,9 @@
* you may not use this file except in compliance with the License.
*/
import { computed } from 'mobx';
import { observer } from 'mobx-react-lite';
import { useCallback } from 'react';
import { useCallback, useMemo } from 'react';
import styled from 'reshadow';
import { useTranslate } from '@cloudbeaver/core-localization';
@@ -21,22 +22,25 @@ import { PROPERTIES_TABLE_STYLES } from './styles';
type PropertiesState = Record<string, string>;
interface PropertiesTableProps {
interface Props {
properties: IProperty[];
propertiesState?: PropertiesState;
readOnly?: boolean;
onKeyChange?: (id: string, name: string) => void;
onChange?: (state: PropertiesState) => void;
onAdd?: () => void;
onRemove?: (id: string) => void;
onRemove?: (property: IProperty) => void;
className?: string;
}
export const PropertiesTable = observer(function PropertiesTable(props: PropertiesTableProps) {
export const PropertiesTable = observer(function PropertiesTable(props: Props) {
const { className, onAdd, readOnly, propertiesState } = props;
const translate = useTranslate();
const propsRef = useObjectRef({ ...props });
const sortedProperties = useMemo(() => computed(() => propsRef.properties.slice().sort(
(a, b) => (a?.displayName ?? '').localeCompare(b?.displayName ?? ''))), [propsRef.properties]);
const changeName = useCallback((id: string, key: string) => {
const { properties, propertiesState, onKeyChange } = propsRef;
const property = properties.find(property => property.id === id);
@@ -92,9 +96,8 @@ export const PropertiesTable = observer(function PropertiesTable(props: Properti
}
if (onRemove) {
onRemove(id);
onRemove(property);
}
properties.splice(properties.indexOf(property), 1);
}, []);
const isKeyUnique = useCallback(
@@ -102,24 +105,30 @@ export const PropertiesTable = observer(function PropertiesTable(props: Properti
[]
);
const alphabetOrderProperties = propsRef.properties.slice().sort(
(a, b) => (a?.displayName ?? '').localeCompare(b?.displayName ?? ''));
return styled(useStyles(PROPERTIES_TABLE_STYLES))(
<properties as="div" className={className}>
<properties-header as="div">
<properties-header-name as="div">
<properties className={className}>
<properties-header>
<properties-header-name>
{translate('block_properties_table_name')}
</properties-header-name>
<properties-header-value as="div">
<properties-header-value>
{translate('block_properties_table_value')}
</properties-header-value>
<properties-header-right as="div">
{onAdd && !readOnly && <Button type='button' mod={['outlined']} onClick={() => onAdd()}>{translate('block_properties_table_add')}</Button>}
</properties-header-right>
</properties-header>
<properties-list as="div">
{alphabetOrderProperties.map(property => (
<properties-list>
{onAdd && !readOnly && (
<properties-header-add>
<Button
icon='add_sm'
viewBox="0 0 18 18"
type='button'
onClick={() => onAdd()}
>
{translate('block_properties_table_add')}
</Button>
</properties-header-add>
)}
{sortedProperties.get().map(property => (
<PropertyItem
key={property.id}
property={property}
@@ -13,10 +13,7 @@ import { composes } from '@cloudbeaver/core-theming';
export const PROPERTIES_TABLE_STYLES = composes(
css`
properties-header {
composes: theme-background-secondary theme-text-on-secondary from global;
}
properties-header-name, properties-header-value {
composes: theme-background-surface theme-text-on-surface from global;
composes: theme-background-surface theme-text-on-surface theme-border-color-secondary from global;
}
`,
css`
@@ -28,10 +25,7 @@ export const PROPERTIES_TABLE_STYLES = composes(
properties-header {
box-sizing: border-box;
display: inline-flex;
padding: 2px 0;
position: sticky;
z-index: 1;
top: 0;
border-bottom: solid 1px;
}
properties-header-name, properties-header-value {
composes: theme-typography--caption from global;
@@ -40,22 +34,25 @@ export const PROPERTIES_TABLE_STYLES = composes(
flex: 1;
padding: 4px 36px;
}
properties-header-value, properties-header-right {
margin-left: 2px;
}
properties-header-name {
flex: 0 0 auto;
width: 300px;
}
properties-header-right {
flex: 0 0 auto;
properties-header-add {
flex: 0 0 100%;
padding: 8px 28px;
& Button {
text-transform: initial;
}
}
properties-list {
flex: 1;
display: flex;
flex-direction: column;
padding-bottom: 24px;
overflow: auto;
max-height: 600px;
}
`
);
@@ -1,5 +1,5 @@
export default [
['block_properties_table_name', 'Name'],
['block_properties_table_value', 'Value'],
['block_properties_table_add', 'Add'],
['block_properties_table_add', 'Add property'],
];
@@ -1,5 +1,5 @@
export default [
['block_properties_table_name', 'Название'],
['block_properties_table_value', 'Значение'],
['block_properties_table_add', 'Добавить'],
['block_properties_table_add', 'Добавить параметр'],
];
@@ -11,7 +11,7 @@ import { observer } from 'mobx-react-lite';
import { useMemo, useState } from 'react';
import styled, { css } from 'reshadow';
import { IProperty, Loader, PropertiesTable, TabContainerPanelComponent, useMapResource, useTab } from '@cloudbeaver/core-blocks';
import { BASE_CONTAINERS_STYLES, ColoredContainer, Group, IProperty, Loader, PropertiesTable, TabContainerPanelComponent, useMapResource, useTab } from '@cloudbeaver/core-blocks';
import { useStyles } from '@cloudbeaver/core-theming';
import { uuid } from '@cloudbeaver/core-utils';
@@ -19,14 +19,17 @@ import { DBDriverResource } from '../../DBDriverResource';
import type { IConnectionFormProps } from '../IConnectionFormProps';
const styles = css`
properties {
display: flex;
ColoredContainer {
flex: 1;
flex-direction: column;
overflow: auto;
}
center {
margin: auto;
Group {
max-height: 100%;
}
PropertiesTable {
padding-top: 8px;
max-height: 100%;
box-sizing: border-box;
}
`;
@@ -34,7 +37,7 @@ export const DriverProperties: TabContainerPanelComponent<IConnectionFormProps>
tabId,
state: formState,
}) {
const style = useStyles(styles);
const style = useStyles(styles, BASE_CONTAINERS_STYLES);
const { selected } = useTab(tabId);
const [state] = useState(() => {
@@ -50,7 +53,11 @@ export const DriverProperties: TabContainerPanelComponent<IConnectionFormProps>
});
}
return { propertiesList, add };
function remove(property: IProperty) {
propertiesList.splice(propertiesList.indexOf(property), 1);
}
return { propertiesList, add, remove };
});
const driver = useMapResource(
@@ -87,17 +94,20 @@ export const DriverProperties: TabContainerPanelComponent<IConnectionFormProps>
])), [driver.data]);
return styled(style)(
<properties as="div">
<Loader state={driver}>
{() => (
<PropertiesTable
properties={joinedProperties.get()}
propertiesState={formState.config.properties}
readOnly={formState.readonly}
onAdd={state.add}
/>
)}
</Loader>
</properties>
<Loader state={driver}>
{() => styled(style)(
<ColoredContainer parent>
<Group box keepSize large>
<PropertiesTable
properties={joinedProperties.get()}
propertiesState={formState.config.properties}
readOnly={formState.readonly}
onAdd={state.add}
onRemove={state.remove}
/>
</Group>
</ColoredContainer>
)}
</Loader>
);
});
@@ -15,12 +15,18 @@
.theme-button {
composes: mdc-button;
height: 28px;
height: 32px;
&[disabled] {
opacity: 1;
}
}
.theme-button__label {
composes: mdc-button__label;
}
.theme-button__icon {
composes: mdc-button__icon;
}
.theme-button_ripple {
composes: mdc-button__ripple;
}