diff --git a/webapp/packages/administration/src/Administration/Administration.tsx b/webapp/packages/administration/src/Administration/Administration.tsx index 1a947c410a..27972a0f05 100644 --- a/webapp/packages/administration/src/Administration/Administration.tsx +++ b/webapp/packages/administration/src/Administration/Administration.tsx @@ -55,6 +55,8 @@ const administrationStyles = composes( } content { flex: 1; + display: flex; + flex-direction: column; } ` ); diff --git a/webapp/packages/administration/src/Administration/AdministrationTools/AdministrationTools.tsx b/webapp/packages/administration/src/Administration/AdministrationTools/AdministrationTools.tsx new file mode 100644 index 0000000000..ace1b8d424 --- /dev/null +++ b/webapp/packages/administration/src/Administration/AdministrationTools/AdministrationTools.tsx @@ -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)( + + {children} + + ); +} diff --git a/webapp/packages/administration/src/index.ts b/webapp/packages/administration/src/index.ts index 2169afc21f..5dbc811fe5 100644 --- a/webapp/packages/administration/src/index.ts +++ b/webapp/packages/administration/src/index.ts @@ -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'; diff --git a/webapp/packages/authentication/public/icons/account.svg b/webapp/packages/authentication/public/icons/account.svg new file mode 100644 index 0000000000..6767905111 --- /dev/null +++ b/webapp/packages/authentication/public/icons/account.svg @@ -0,0 +1,3 @@ + + + diff --git a/webapp/packages/authentication/src/Administration/Users/UsersAdministration.tsx b/webapp/packages/authentication/src/Administration/Users/UsersAdministration.tsx index 92d88d1a23..254dc008b5 100644 --- a/webapp/packages/authentication/src/Administration/Users/UsersAdministration.tsx +++ b/webapp/packages/authentication/src/Administration/Users/UsersAdministration.tsx @@ -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 + + + + + User login + User first name + User last name + User last name + + + + + a + b + c + d + + + + a + b + c + d + + + + a + b + c + d + + + + a + b + c + d + + + +
+
); -} +}); diff --git a/webapp/packages/authentication/src/Administration/Users/UsersDrawerItem.tsx b/webapp/packages/authentication/src/Administration/Users/UsersDrawerItem.tsx index ca2c65a424..ba041485ec 100644 --- a/webapp/packages/authentication/src/Administration/Users/UsersDrawerItem.tsx +++ b/webapp/packages/authentication/src/Administration/Users/UsersDrawerItem.tsx @@ -15,7 +15,7 @@ import { useStyles } from '@dbeaver/core/theming'; export function UsersDrawerItem({ item, onSelect, style }: AdministrationItemDrawerProps) { return styled(useStyles(...style))( onSelect(item.name)}> - + Users ); diff --git a/webapp/packages/core/assets/styles/_table.scss b/webapp/packages/core/assets/styles/_table.scss index 241be4020a..86d7418678 100644 --- a/webapp/packages/core/assets/styles/_table.scss +++ b/webapp/packages/core/assets/styles/_table.scss @@ -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; diff --git a/webapp/packages/core/src/blocks/Table/Table.tsx b/webapp/packages/core/src/blocks/Table/Table.tsx new file mode 100644 index 0000000000..df86cedd48 --- /dev/null +++ b/webapp/packages/core/src/blocks/Table/Table.tsx @@ -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; + className?: string; +}> + +export function Table({ selectedItems, children, className }: Props) { + const [selected] = useState>(() => selectedItems || observable(new Map())); + const setItemSelect = useCallback((item: any, state: boolean) => selected.set(item, state), []); + const clearSelection = useCallback(() => selected.clear(), []); + + const [context] = useState(() => ({ + selectedItems: selected, + setItemSelect, + clearSelection, + })); + + return styled(useStyles())( + + + {children} +
+
+ ); +} diff --git a/webapp/packages/core/src/blocks/Table/TableBody.tsx b/webapp/packages/core/src/blocks/Table/TableBody.tsx new file mode 100644 index 0000000000..eceb5b6f29 --- /dev/null +++ b/webapp/packages/core/src/blocks/Table/TableBody.tsx @@ -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())( + + {children} + + ); +} diff --git a/webapp/packages/core/src/blocks/Table/TableColumnHeader.tsx b/webapp/packages/core/src/blocks/Table/TableColumnHeader.tsx new file mode 100644 index 0000000000..aeae374094 --- /dev/null +++ b/webapp/packages/core/src/blocks/Table/TableColumnHeader.tsx @@ -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())( + + {children} + + ); +} diff --git a/webapp/packages/core/src/blocks/Table/TableColumnValue.tsx b/webapp/packages/core/src/blocks/Table/TableColumnValue.tsx new file mode 100644 index 0000000000..63b1a5d2f2 --- /dev/null +++ b/webapp/packages/core/src/blocks/Table/TableColumnValue.tsx @@ -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())( + {children} + ); +}); diff --git a/webapp/packages/core/src/blocks/Table/TableContext.tsx b/webapp/packages/core/src/blocks/Table/TableContext.tsx new file mode 100644 index 0000000000..10171a480d --- /dev/null +++ b/webapp/packages/core/src/blocks/Table/TableContext.tsx @@ -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; + setItemSelect: (item: any, state: boolean) => void; + clearSelection: () => void; +} + +export const TableContext = createContext(undefined); diff --git a/webapp/packages/core/src/blocks/Table/TableHeader.tsx b/webapp/packages/core/src/blocks/Table/TableHeader.tsx new file mode 100644 index 0000000000..e18148ac48 --- /dev/null +++ b/webapp/packages/core/src/blocks/Table/TableHeader.tsx @@ -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())( + + + {children} + + + ); +} diff --git a/webapp/packages/core/src/blocks/Table/TableItem.tsx b/webapp/packages/core/src/blocks/Table/TableItem.tsx new file mode 100644 index 0000000000..7b8a8528c8 --- /dev/null +++ b/webapp/packages/core/src/blocks/Table/TableItem.tsx @@ -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) => { + 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) => { + if (onDoubleClick) { + onDoubleClick(); + } + }, [onDoubleClick]); + + const isSelected = context.selectedItems.get(item); + + return styled(useStyles())( + + {children} + + ); +}); diff --git a/webapp/packages/core/src/blocks/Table/index.ts b/webapp/packages/core/src/blocks/Table/index.ts new file mode 100644 index 0000000000..98a4cf0832 --- /dev/null +++ b/webapp/packages/core/src/blocks/Table/index.ts @@ -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'; diff --git a/webapp/packages/core/src/blocks/index.ts b/webapp/packages/core/src/blocks/index.ts index dba7030142..004e30dead 100644 --- a/webapp/packages/core/src/blocks/index.ts +++ b/webapp/packages/core/src/blocks/index.ts @@ -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'; diff --git a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Header.tsx b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Header.tsx index 00bb6725f3..8463c1efee 100644 --- a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Header.tsx +++ b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Header.tsx @@ -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))( - - + <> + {properties.map(property => ( - + {property.displayName} - + ))} - + ); } diff --git a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx index f70f7fa368..296ce4531a 100644 --- a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx +++ b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx @@ -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) => onClick(objectId, e.ctrlKey), - [objectId, onClick] - ); if (!object || !databaseObjectInfo?.properties) { return styled(useStyles(itemStyles))( - - + + - + {Array(columns) .fill(0) .map((_, i) => ( - + - + ))} - + ); } return styled(useStyles(itemStyles))( - - + + - + {databaseObjectInfo.properties.map(property => ( - {getValue(property.value)} + {getValue(property.value)} ))} - + ); }); diff --git a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectChildrenPropertyTable.tsx b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectChildrenPropertyTable.tsx index df0e106371..7d275f796f 100644 --- a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectChildrenPropertyTable.tsx +++ b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectChildrenPropertyTable.tsx @@ -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([]); - 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))( - - + return ( +
+
- -
- {props.nodeIds.map(id => ( + + + {nodeIds.map(id => ( ))} - -
+ + ); });