mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 06:40:05 +08:00
CB-3585 migrate itemList to css-modules (#1832)
Co-authored-by: Alexey <wrouds@gmail.com>
This commit is contained in:
+55
-71
@@ -1,86 +1,73 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2023 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';
|
||||
import { css } from 'reshadow';
|
||||
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
export const ITEM_LIST_STYLES = css`
|
||||
item-list {
|
||||
.itemList {
|
||||
composes: theme-background-surface from global;
|
||||
box-sizing: border-box;
|
||||
border-collapse: collapse;
|
||||
z-index: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
item-list-overflow,
|
||||
item-list-overflow-top {
|
||||
}
|
||||
.itemListOverflow,
|
||||
.itemListOverflowTop {
|
||||
position: sticky;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
pointer-events: none;
|
||||
}
|
||||
item-list-overflow {
|
||||
}
|
||||
.itemListOverflow {
|
||||
composes: branding-overflow from global;
|
||||
bottom: 0;
|
||||
}
|
||||
item-list-overflow-top {
|
||||
}
|
||||
.itemListOverflowTop {
|
||||
composes: branding-overflow-inverse from global;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
list-search {
|
||||
}
|
||||
.listSearch {
|
||||
composes: theme-background-surface theme-text-on-surface theme-typography--body1 from global;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding: 16px 24px;
|
||||
z-index: 1;
|
||||
|
||||
& input-box {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
& input {
|
||||
padding: 4px 32px 4px 8px;
|
||||
}
|
||||
|
||||
& action-button {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
top: 50%;
|
||||
right: 4px;
|
||||
transform: translateY(-50%);
|
||||
|
||||
& IconButton {
|
||||
& .inputBox {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin: 0;
|
||||
|
||||
&[name='cross'] {
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
IconButton {
|
||||
|
||||
& .input {
|
||||
padding: 4px 32px 4px 8px;
|
||||
}
|
||||
|
||||
& .actionButton {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
top: 50%;
|
||||
right: 4px;
|
||||
transform: translateY(-50%);
|
||||
|
||||
& .iconButton {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& .crossIcon {
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.iconButton {
|
||||
composes: theme-ripple theme-background-primary theme-text-on-primary from global;
|
||||
}
|
||||
list-item:not(:nth-last-child(2)) {
|
||||
}
|
||||
.listItem:not(:nth-last-child(2)) {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
list-item {
|
||||
}
|
||||
.listItem {
|
||||
composes: theme-ripple theme-background-surface theme-border-color-secondary from global;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
@@ -88,19 +75,19 @@ export const ITEM_LIST_STYLES = css`
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
}
|
||||
list-item-icon {
|
||||
}
|
||||
.listItemIcon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
|
||||
& StaticImage {
|
||||
box-sizing: border-box;
|
||||
width: 24px;
|
||||
& .staticImage {
|
||||
box-sizing: border-box;
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
list-item-name {
|
||||
}
|
||||
.listItemName {
|
||||
composes: theme-border-color-secondary theme-typography--body1 from global;
|
||||
box-sizing: border-box;
|
||||
font-weight: 500;
|
||||
@@ -111,8 +98,8 @@ export const ITEM_LIST_STYLES = css`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
list-item-description {
|
||||
}
|
||||
.listItemDescription {
|
||||
composes: theme-typography--body2 from global;
|
||||
box-sizing: border-box;
|
||||
max-width: 460px;
|
||||
@@ -121,7 +108,4 @@ export const ITEM_LIST_STYLES = css`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Styles = createContext<ComponentStyle>(ITEM_LIST_STYLES);
|
||||
}
|
||||
@@ -5,24 +5,25 @@
|
||||
* 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 type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { useStyles } from '../useStyles';
|
||||
import { ITEM_LIST_STYLES, Styles } from './styles';
|
||||
import { s } from '../s';
|
||||
import { useS } from '../useS';
|
||||
import style from './ItemList.m.css';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
styles?: ComponentStyle;
|
||||
}
|
||||
|
||||
export const ItemList: React.FC<React.PropsWithChildren<Props>> = function ItemList({ children, className, styles }) {
|
||||
return styled(useStyles(styles || ITEM_LIST_STYLES))(
|
||||
<item-list className={className}>
|
||||
<item-list-overflow-top />
|
||||
<Styles.Provider value={styles || ITEM_LIST_STYLES}>{children}</Styles.Provider>
|
||||
<item-list-overflow />
|
||||
</item-list>,
|
||||
export const ItemList: React.FC<React.PropsWithChildren<Props>> = function ItemList({ children, className }) {
|
||||
const styles = useS(style);
|
||||
|
||||
return (
|
||||
<div className={s(styles, { itemList: true }, className)}>
|
||||
<div className={s(styles, { itemListOverflowTop: true }, className)} />
|
||||
{children}
|
||||
<div className={s(styles, { itemListOverflow: true }, className)} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { useCallback, useContext, useRef, useState } from 'react';
|
||||
import styled, { use } from 'reshadow';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { use } from 'reshadow';
|
||||
|
||||
import { IconButton } from '../IconButton';
|
||||
import { useTranslate } from '../localization/useTranslate';
|
||||
import { useStyles } from '../useStyles';
|
||||
import { Styles } from './styles';
|
||||
import { s } from '../s';
|
||||
import { useS } from '../useS';
|
||||
import style from './ItemList.m.css';
|
||||
|
||||
interface IProps {
|
||||
value?: string;
|
||||
@@ -24,7 +25,7 @@ interface IProps {
|
||||
|
||||
export const ItemListSearch: React.FC<IProps> = function ItemListSearch({ value, placeholder, disabled, onChange, onSearch, className }) {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const styles = useContext(Styles);
|
||||
const styles = useS(style);
|
||||
const [search, setSearch] = useState(value ?? '');
|
||||
const translate = useTranslate();
|
||||
const changeHandler = useCallback(
|
||||
@@ -54,12 +55,13 @@ export const ItemListSearch: React.FC<IProps> = function ItemListSearch({ value,
|
||||
|
||||
const inputValue = value ?? search;
|
||||
|
||||
return styled(useStyles(styles || []))(
|
||||
<list-search className={className}>
|
||||
<input-box>
|
||||
return (
|
||||
<div className={s(styles, { listSearch: true })}>
|
||||
<div className={s(styles, { inputBox: true })}>
|
||||
<input
|
||||
ref={inputRef}
|
||||
name="search"
|
||||
className={s(styles, { input: true }, className)}
|
||||
placeholder={translate(placeholder || 'ui_search')}
|
||||
value={inputValue}
|
||||
autoComplete="off"
|
||||
@@ -67,14 +69,14 @@ export const ItemListSearch: React.FC<IProps> = function ItemListSearch({ value,
|
||||
onChange={event => changeHandler(event.target.value)}
|
||||
{...use({ mod: 'surface' })}
|
||||
/>
|
||||
<action-button as="div">
|
||||
<div className={s(styles, { actionButton: true })}>
|
||||
{!onSearch && inputValue ? (
|
||||
<IconButton name="cross" onClick={() => changeHandler('')} />
|
||||
<IconButton className={s(styles, { iconButton: true, crossIcon: true })} name="cross" onClick={() => changeHandler('')} />
|
||||
) : (
|
||||
<IconButton name="search" onClick={searchHandler} />
|
||||
<IconButton className={s(styles, { iconButton: true })} name="search" onClick={searchHandler} />
|
||||
)}
|
||||
</action-button>
|
||||
</input-box>
|
||||
</list-search>,
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { useContext } from 'react';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import { useStyles } from '../useStyles';
|
||||
import { Styles } from './styles';
|
||||
import { s } from '../s';
|
||||
import { useS } from '../useS';
|
||||
import style from './ItemList.m.css';
|
||||
|
||||
interface Props {
|
||||
onClick: () => void;
|
||||
@@ -17,11 +15,11 @@ interface Props {
|
||||
}
|
||||
|
||||
export const ListItem: React.FC<React.PropsWithChildren<Props>> = function ListItem({ children, onClick, className }) {
|
||||
const styles = useContext(Styles);
|
||||
const styles = useS(style);
|
||||
|
||||
return styled(useStyles(styles))(
|
||||
<list-item as="div" className={className} onClick={onClick}>
|
||||
return (
|
||||
<div className={s(styles, { listItem: true }, className)} onClick={onClick}>
|
||||
{children}
|
||||
</list-item>,
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { useContext } from 'react';
|
||||
import styled from 'reshadow';
|
||||
import { s } from '../s';
|
||||
import { useS } from '../useS';
|
||||
import style from './ItemList.m.css';
|
||||
|
||||
import { useStyles } from '../useStyles';
|
||||
import { Styles } from './styles';
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
@@ -17,11 +16,11 @@ interface Props {
|
||||
}
|
||||
|
||||
export const ListItemDescription: React.FC<React.PropsWithChildren<Props>> = function ListItemDescription({ title, children, className }) {
|
||||
const styles = useContext(Styles);
|
||||
const styles = useS(style);
|
||||
|
||||
return styled(useStyles(styles))(
|
||||
<list-item-description title={title} className={className}>
|
||||
return (
|
||||
<div title={title} className={s(styles, { listItemDescription: true }, className)}>
|
||||
{children}
|
||||
</list-item-description>,
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -5,18 +5,16 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { useContext } from 'react';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import { useStyles } from '../useStyles';
|
||||
import { Styles } from './styles';
|
||||
import { s } from '../s';
|
||||
import { useS } from '../useS';
|
||||
import style from './ItemList.m.css';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ListItemIcon: React.FC<React.PropsWithChildren<Props>> = function ListItemIcon({ children, className }) {
|
||||
const styles = useContext(Styles);
|
||||
const styles = useS(style);
|
||||
|
||||
return styled(useStyles(styles))(<list-item-icon className={className}>{children}</list-item-icon>);
|
||||
return <div className={s(styles, { listItemIcon: true }, className)}>{children}</div>;
|
||||
};
|
||||
|
||||
@@ -5,18 +5,16 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { useContext } from 'react';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import { useStyles } from '../useStyles';
|
||||
import { Styles } from './styles';
|
||||
import { s } from '../s';
|
||||
import { useS } from '../useS';
|
||||
import style from './ItemList.m.css';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ListItemName: React.FC<React.PropsWithChildren<Props>> = function ListItemName({ children, className }) {
|
||||
const styles = useContext(Styles);
|
||||
const styles = useS(style);
|
||||
|
||||
return styled(useStyles(styles))(<list-item-name className={className}>{children}</list-item-name>);
|
||||
return <div className={s(styles, { listItemName: true }, className)}>{children}</div>;
|
||||
};
|
||||
|
||||
@@ -10,7 +10,6 @@ export * from './ItemList/ListItem';
|
||||
export * from './ItemList/ListItemDescription';
|
||||
export * from './ItemList/ListItemIcon';
|
||||
export * from './ItemList/ListItemName';
|
||||
export { ITEM_LIST_STYLES } from './ItemList/styles';
|
||||
|
||||
export * from './layout/AppLogo';
|
||||
export * from './layout/BASE_LAYOUT_GRID_STYLES';
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
.staticImage {
|
||||
box-sizing: border-box;
|
||||
width: 24px;
|
||||
max-height: 24px;
|
||||
};
|
||||
+7
-13
@@ -7,9 +7,10 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useCallback } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { ListItem, ListItemDescription, ListItemIcon, ListItemName, StaticImage } from '@cloudbeaver/core-blocks';
|
||||
import { ListItem, ListItemDescription, ListItemIcon, ListItemName, s, StaticImage, useS } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import style from './Driver.m.css';
|
||||
|
||||
export interface IDriver {
|
||||
id: string;
|
||||
@@ -18,14 +19,6 @@ export interface IDriver {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
const styles = css`
|
||||
StaticImage {
|
||||
box-sizing: border-box;
|
||||
width: 24px;
|
||||
max-height: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
driver: IDriver;
|
||||
onSelect: (driverId: string) => void;
|
||||
@@ -33,14 +26,15 @@ interface Props {
|
||||
|
||||
export const Driver = observer<Props>(function Driver({ driver, onSelect }) {
|
||||
const select = useCallback(() => onSelect(driver.id), [driver]);
|
||||
const styles = useS(style);
|
||||
|
||||
return styled(styles)(
|
||||
return (
|
||||
<ListItem onClick={select}>
|
||||
<ListItemIcon>
|
||||
<StaticImage icon={driver.icon} />
|
||||
<StaticImage icon={driver.icon} className={s(styles, { staticImage: true })} />
|
||||
</ListItemIcon>
|
||||
<ListItemName>{driver.name}</ListItemName>
|
||||
<ListItemDescription title={driver.description}>{driver.description}</ListItemDescription>
|
||||
</ListItem>,
|
||||
</ListItem>
|
||||
);
|
||||
});
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
+6
-13
@@ -7,11 +7,11 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useMemo, useState } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { ItemList, ItemListSearch, useFocus, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { ItemList, ItemListSearch, s, useFocus, useS, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import { Driver, IDriver } from './Driver';
|
||||
import style from './DriverSelector.m.css';
|
||||
|
||||
interface Props {
|
||||
drivers: IDriver[];
|
||||
@@ -19,15 +19,8 @@ interface Props {
|
||||
onSelect: (driverId: string) => void;
|
||||
}
|
||||
|
||||
const style = css`
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
export const DriverSelector = observer<Props>(function DriverSelector({ drivers, className, onSelect }) {
|
||||
const styles = useS(style);
|
||||
const translate = useTranslate();
|
||||
const [focusedRef] = useFocus<HTMLDivElement>({ focusFirstChild: true });
|
||||
const [search, setSearch] = useState('');
|
||||
@@ -38,14 +31,14 @@ export const DriverSelector = observer<Props>(function DriverSelector({ drivers,
|
||||
return drivers.filter(driver => driver.name?.toUpperCase().includes(search.toUpperCase()));
|
||||
}, [search, drivers]);
|
||||
|
||||
return styled(style)(
|
||||
<div ref={focusedRef}>
|
||||
return (
|
||||
<div ref={focusedRef} className={s(styles, { wrapper: true })}>
|
||||
<ItemListSearch placeholder={translate('connections_driver_search_placeholder')} onChange={setSearch} />
|
||||
<ItemList className={className}>
|
||||
{filteredDrivers.map(driver => (
|
||||
<Driver key={driver.id} driver={driver} onSelect={onSelect} />
|
||||
))}
|
||||
</ItemList>
|
||||
</div>,
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
.staticImage {
|
||||
box-sizing: border-box;
|
||||
width: 24px;
|
||||
max-height: 24px;
|
||||
};
|
||||
+6
-12
@@ -7,18 +7,11 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useCallback } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { ListItem, ListItemDescription, ListItemIcon, ListItemName, StaticImage } from '@cloudbeaver/core-blocks';
|
||||
import { ListItem, ListItemDescription, ListItemIcon, ListItemName, s, StaticImage, useS } from '@cloudbeaver/core-blocks';
|
||||
import type { DBDriver } from '@cloudbeaver/core-connections';
|
||||
|
||||
const styles = css`
|
||||
StaticImage {
|
||||
box-sizing: border-box;
|
||||
width: 24px;
|
||||
max-height: 24px;
|
||||
}
|
||||
`;
|
||||
import style from './Driver.m.css';
|
||||
|
||||
interface Props {
|
||||
driver: DBDriver;
|
||||
@@ -27,14 +20,15 @@ interface Props {
|
||||
|
||||
export const Driver = observer<Props>(function Driver({ driver, onSelect }) {
|
||||
const select = useCallback(() => onSelect(driver.id), [driver]);
|
||||
const styles = useS(style);
|
||||
|
||||
return styled(styles)(
|
||||
return (
|
||||
<ListItem onClick={select}>
|
||||
<ListItemIcon>
|
||||
<StaticImage icon={driver.icon} />
|
||||
<StaticImage icon={driver.icon} className={s(styles, { staticImage: true })} />
|
||||
</ListItemIcon>
|
||||
<ListItemName>{driver.name}</ListItemName>
|
||||
<ListItemDescription title={driver.description}>{driver.description}</ListItemDescription>
|
||||
</ListItem>,
|
||||
</ListItem>
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user