feat(core): extract table to blocks

This commit is contained in:
Wroud
2020-05-27 16:35:53 +03:00
parent 3910531686
commit c174585766
19 changed files with 363 additions and 72 deletions
@@ -55,6 +55,8 @@ const administrationStyles = composes(
}
content {
flex: 1;
display: flex;
flex-direction: column;
}
`
);
@@ -0,0 +1,28 @@
/*
* 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 styled, { css } from 'reshadow';
const styles = css`
administration-tools {
height: 48px;
}
`;
type Props = React.PropsWithChildren<{
className?: string;
}>
export function AdministrationTools({ children, className }: Props) {
return styled(styles)(
<administration-tools as='div' className={className}>
{children}
</administration-tools>
);
}
@@ -2,6 +2,7 @@ import { manifest } from './manifest';
export default manifest;
export * from './Administration/AdministrationTools/AdministrationTools';
export * from './AdministrationItem/AdministrationItemService';
export * from './AdministrationItem/IAdministrationItem';
export * from './AdministrationScreen/AdministrationScreenService';
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<path fill="#F0840E" d="M9 0c2.485 0 4.5 2.015 4.5 4.5S11.485 9 9 9 4.5 6.985 4.5 4.5 6.515 0 9 0zm0 2.25c-1.243 0-2.25 1.007-2.25 2.25S7.757 6.75 9 6.75s2.25-1.007 2.25-2.25S10.243 2.25 9 2.25zm0 7.875c3.004 0 9 1.496 9 4.5V18H0v-3.375c0-3.004 5.996-4.5 9-4.5zm0 2.137c-3.341 0-6.862 1.643-6.862 2.363v1.238h13.725v-1.238c0-.72-3.522-2.363-6.863-2.363z"/>
</svg>

After

Width:  |  Height:  |  Size: 452 B

@@ -6,10 +6,76 @@
* you may not use this file except in compliance with the License.
*/
export function UsersAdministration() {
return (
import { observer } from 'mobx-react';
import styled, { css } from 'reshadow';
import { AdministrationTools } from '@dbeaver/administration';
import {
Table, TableHeader, TableColumnHeader, TableBody, TableItem, TableColumnValue
} from '@dbeaver/core/blocks';
import { useStyles, composes } from '@dbeaver/core/theming';
const styles = composes(
css`
AdministrationTools {
composes: theme-background-secondary theme-text-on-secondary from global;
}
`,
css`
content {
padding-top: 16px;
}
TableColumnHeader {
border-top: solid 1px;
}
`
);
export const UsersAdministration = observer(function UsersAdministration() {
return styled(useStyles(styles))(
<>
Hello
<AdministrationTools></AdministrationTools>
<content as='div'>
<Table>
<TableHeader>
<TableColumnHeader>User login</TableColumnHeader>
<TableColumnHeader>User first name</TableColumnHeader>
<TableColumnHeader>User last name</TableColumnHeader>
<TableColumnHeader>User last name</TableColumnHeader>
<TableColumnHeader></TableColumnHeader>
</TableHeader>
<TableBody>
<TableItem item={0}>
<TableColumnValue>a</TableColumnValue>
<TableColumnValue>b</TableColumnValue>
<TableColumnValue>c</TableColumnValue>
<TableColumnValue>d</TableColumnValue>
<TableColumnValue></TableColumnValue>
</TableItem>
<TableItem item={1}>
<TableColumnValue>a</TableColumnValue>
<TableColumnValue>b</TableColumnValue>
<TableColumnValue>c</TableColumnValue>
<TableColumnValue>d</TableColumnValue>
<TableColumnValue></TableColumnValue>
</TableItem>
<TableItem item={2}>
<TableColumnValue>a</TableColumnValue>
<TableColumnValue>b</TableColumnValue>
<TableColumnValue>c</TableColumnValue>
<TableColumnValue>d</TableColumnValue>
<TableColumnValue></TableColumnValue>
</TableItem>
<TableItem item={3}>
<TableColumnValue>a</TableColumnValue>
<TableColumnValue>b</TableColumnValue>
<TableColumnValue>c</TableColumnValue>
<TableColumnValue>d</TableColumnValue>
<TableColumnValue></TableColumnValue>
</TableItem>
</TableBody>
</Table>
</content>
</>
);
}
});
@@ -15,7 +15,7 @@ import { useStyles } from '@dbeaver/core/theming';
export function UsersDrawerItem({ item, onSelect, style }: AdministrationItemDrawerProps) {
return styled(useStyles(...style))(
<Tab tabId={item.name} onOpen={() => onSelect(item.name)}>
<TabIcon icon='/icons/grid.png' />
<TabIcon icon='/icons/account.svg' />
<TabTitle>Users</TabTitle>
</Tab>
);
@@ -8,6 +8,7 @@
@mixin table($query: mdc-feature-all()) {
table {
composes: theme-typography--body2 from global;
min-width: 100%;
text-align: left;
border-collapse: collapse;
@@ -0,0 +1,40 @@
/*
* 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 { observable } from 'mobx';
import { useState, useCallback } from 'react';
import styled from 'reshadow';
import { useStyles } from '@dbeaver/core/theming';
import { TableContext, ITableContext } from './TableContext';
type Props = React.PropsWithChildren<{
selectedItems?: Map<any, boolean>;
className?: string;
}>
export function Table({ selectedItems, children, className }: Props) {
const [selected] = useState<Map<any, boolean>>(() => selectedItems || observable(new Map()));
const setItemSelect = useCallback((item: any, state: boolean) => selected.set(item, state), []);
const clearSelection = useCallback(() => selected.clear(), []);
const [context] = useState<ITableContext>(() => ({
selectedItems: selected,
setItemSelect,
clearSelection,
}));
return styled(useStyles())(
<TableContext.Provider value={context}>
<table className={className}>
{children}
</table>
</TableContext.Provider>
);
}
@@ -0,0 +1,23 @@
/*
* 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 styled from 'reshadow';
import { useStyles } from '@dbeaver/core/theming';
type Props = React.PropsWithChildren<{
className?: string;
}>
export function TableBody({ children, className }: Props) {
return styled(useStyles())(
<tbody className={className}>
{children}
</tbody>
);
}
@@ -0,0 +1,24 @@
/*
* 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 styled from 'reshadow';
import { useStyles } from '@dbeaver/core/theming';
type Props = React.PropsWithChildren<{
title?: string;
className?: string;
}>
export function TableColumnHeader({ title, children, className }: Props) {
return styled(useStyles())(
<th title={title} className={className}>
{children}
</th>
);
}
@@ -0,0 +1,22 @@
/*
* 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 { observer } from 'mobx-react';
import styled from 'reshadow';
import { useStyles } from '@dbeaver/core/theming';
type Props = React.PropsWithChildren<{
className?: string;
}>
export const TableColumnValue = observer(function TableColumnValue({ children, className }: Props) {
return styled(useStyles())(
<td className={className}>{children}</td>
);
});
@@ -0,0 +1,17 @@
/*
* 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 { createContext } from 'react';
export interface ITableContext {
selectedItems: Map<any, boolean>;
setItemSelect: (item: any, state: boolean) => void;
clearSelection: () => void;
}
export const TableContext = createContext<ITableContext | undefined>(undefined);
@@ -0,0 +1,25 @@
/*
* 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 styled from 'reshadow';
import { useStyles } from '@dbeaver/core/theming';
type Props = React.PropsWithChildren<{
className?: string;
}>
export function TableHeader({ children, className }: Props) {
return styled(useStyles())(
<thead className={className}>
<tr>
{children}
</tr>
</thead>
);
}
@@ -0,0 +1,61 @@
/*
* 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 { observer } from 'mobx-react';
import { useContext, useCallback } from 'react';
import styled, { use } from 'reshadow';
import { useStyles } from '@dbeaver/core/theming';
import { TableContext } from './TableContext';
type Props = React.PropsWithChildren<{
item: any;
className?: string;
onClick?: () => void;
onDoubleClick?: () => void;
}>
export const TableItem = observer(function TableItem({
item, children, className, onClick, onDoubleClick,
}: Props) {
const context = useContext(TableContext);
if (!context) {
return null;
}
const handleClick = useCallback(
(e: React.MouseEvent<HTMLTableRowElement>) => {
if (!e.ctrlKey) {
context.clearSelection();
}
const isSelected = context.selectedItems.get(item);
context.setItemSelect(item, !isSelected);
if (onClick) {
onClick();
}
},
[context, item, onClick]
);
const handleDoubleClick = useCallback((e: React.MouseEvent<HTMLTableRowElement>) => {
if (onDoubleClick) {
onDoubleClick();
}
}, [onDoubleClick]);
const isSelected = context.selectedItems.get(item);
return styled(useStyles())(
<tr onClick={handleClick} onDoubleClick={handleDoubleClick} className={className} {...use({ isSelected })}>
{children}
</tr>
);
});
@@ -0,0 +1,7 @@
export * from './Table';
export * from './TableBody';
export * from './TableColumnHeader';
export * from './TableColumnValue';
export * from './TableContext';
export * from './TableHeader';
export * from './TableItem';
+1
View File
@@ -9,6 +9,7 @@ export * from './Placeholder/PlaceholderContainer';
export * from './PropertiesTable';
export * from './SanitizedHTML/SanitizedHTML';
export * from './Split';
export * from './Table';
export * from './Tabs';
export * from './Button';
export * from './ErrorMessage';
@@ -8,11 +8,12 @@
import styled, { css } from 'reshadow';
import { TableColumnHeader } from '@dbeaver/core/blocks';
import { ObjectPropertyInfo } from '@dbeaver/core/sdk';
import { useStyles } from '@dbeaver/core/theming';
const styles = css`
th:first-child {
TableColumnHeader:first-child {
width:16px;
border-right: none;
}
@@ -34,13 +35,13 @@ type HeaderProps = {
export function Header({ properties }: HeaderProps) {
return styled(useStyles(styles))(
<tr>
<th></th>
<>
<TableColumnHeader></TableColumnHeader>
{properties.map(property => (
<th key={property.id} title={property.description}>
<TableColumnHeader key={property.id} title={property.description}>
{property.displayName}
</th>
</TableColumnHeader>
))}
</tr>
</>
);
}
@@ -8,10 +8,10 @@
import { observer } from 'mobx-react';
import { useCallback } from 'react';
import styled, { css, use } from 'reshadow';
import styled, { css } from 'reshadow';
import { NodesManagerService, useDatabaseObjectInfo, useNode } from '@dbeaver/core/app';
import { StaticImage } from '@dbeaver/core/blocks';
import { StaticImage, TableItem, TableColumnValue } from '@dbeaver/core/blocks';
import { useService } from '@dbeaver/core/di';
import { useStyles } from '@dbeaver/core/theming';
@@ -32,10 +32,10 @@ const itemStyles = css`
height: 16px;
width: 180px;
}
tr {
TableItem {
position: relative;
}
td:nth-child(2) {
TableColumnValue:nth-child(2) {
cursor: pointer;
user-select: none;
}
@@ -44,51 +44,45 @@ const itemStyles = css`
type ItemProps = {
objectId: string;
columns: number;
isSelected: boolean;
onClick: (objectId: string, isMultiple: boolean) => void;
}
export const Item = observer(function Item({
objectId, columns, isSelected, onClick,
objectId, columns,
}: ItemProps) {
const nodesManagerService = useService(NodesManagerService);
const object = useNode(objectId);
const databaseObjectInfo = useDatabaseObjectInfo(objectId);
const handleOpen = useCallback(() => nodesManagerService.navToNode(objectId), [objectId]);
const handleClick = useCallback(
(e: React.MouseEvent<HTMLTableRowElement>) => onClick(objectId, e.ctrlKey),
[objectId, onClick]
);
if (!object || !databaseObjectInfo?.properties) {
return styled(useStyles(itemStyles))(
<tr tabIndex={0} onDoubleClick={handleOpen} onClick={handleClick} {...use({ isSelected })}>
<td>
<TableItem item={objectId} onDoubleClick={handleOpen}>
<TableColumnValue>
<icon as="div">
<placeholder as="div" />
</icon>
</td>
</TableColumnValue>
{Array(columns)
.fill(0)
.map((_, i) => (
<td key={i}>
<TableColumnValue key={i}>
<placeholder as="div" />
</td>
</TableColumnValue>
))}
</tr>
</TableItem>
);
}
return styled(useStyles(itemStyles))(
<tr tabIndex={0} onDoubleClick={handleOpen} onClick={handleClick} {...use({ isSelected })}>
<td>
<TableItem item={objectId} onDoubleClick={handleOpen}>
<TableColumnValue>
<icon as="div">
<StaticImage icon={object.icon} />
</icon>
</td>
</TableColumnValue>
{databaseObjectInfo.properties.map(property => (
<td key={property.id}>{getValue(property.value)}</td>
<TableColumnValue key={property.id}>{getValue(property.value)}</TableColumnValue>
))}
</tr>
</TableItem>
);
});
@@ -7,67 +7,42 @@
*/
import { observer } from 'mobx-react';
import { useCallback, useState } from 'react';
import styled, { css } from 'reshadow';
import { useDatabaseObjectInfo } from '@dbeaver/core/app';
import { useStyles } from '@dbeaver/core/theming';
import { TableHeader, TableBody, Table } from '@dbeaver/core/blocks';
import { Header } from './Header';
import { Item } from './Item';
const styles = css`
table {
composes: theme-typography--body2 from global;
}
`;
type ObjectChildrenPropertyTableProps = {
nodeIds?: string[];
}
export const ObjectChildrenPropertyTable = observer(function ObjectPropertyTable(
props: ObjectChildrenPropertyTableProps
) {
export const ObjectChildrenPropertyTable = observer(function ObjectPropertyTable({
nodeIds,
}: ObjectChildrenPropertyTableProps) {
const [selected, setSelected] = useState<string[]>([]);
const handleSelect = useCallback((objectId: string, isMultiple: boolean) => {
if (isMultiple) {
if (selected.includes(objectId)) {
setSelected(selected.filter(v => v !== objectId));
} else {
setSelected([...selected, objectId]);
}
} else if (!selected.includes(objectId)) {
setSelected([objectId]);
} else {
setSelected([]);
}
}, [selected]);
if (!props.nodeIds) {
if (!nodeIds) {
return null;
}
const firstChild = props.nodeIds[0] || '';
const firstChild = nodeIds[0] || '';
const properties = useDatabaseObjectInfo(firstChild)?.properties;
return styled(useStyles(styles))(
<table>
<thead>
return (
<Table>
<TableHeader>
<Header properties={properties || []} />
</thead>
<tbody>
{props.nodeIds.map(id => (
</TableHeader>
<TableBody>
{nodeIds.map(id => (
<Item
key={id}
objectId={id}
columns={properties?.length || 0}
isSelected={selected.includes(id)}
onClick={handleSelect}
/>
))}
</tbody>
</table>
</TableBody>
</Table>
);
});