mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
This reverts commit f294ca7767.
This commit is contained in:
Vendored
-1
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.linkedEditing": true,
|
||||
|
||||
"task.autoDetect": "off",
|
||||
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
.background {
|
||||
fill: transparent;
|
||||
}
|
||||
@@ -7,9 +7,6 @@
|
||||
*/
|
||||
import { GlobalConstants, isValidUrl } from '@cloudbeaver/core-utils';
|
||||
|
||||
import { useS } from '../useS';
|
||||
import style from './ConnectionImageWithMaskSvg.m.css';
|
||||
|
||||
interface Props {
|
||||
icon?: string;
|
||||
connected: boolean;
|
||||
@@ -21,7 +18,6 @@ interface Props {
|
||||
}
|
||||
|
||||
export const ConnectionImageWithMaskSvg: React.FC<Props> = ({ icon, connected, maskId, size = 16, markerRadius = 4, paddingSize = 0, className }) => {
|
||||
const styles = useS(style);
|
||||
if (!icon) {
|
||||
return null;
|
||||
}
|
||||
@@ -52,7 +48,6 @@ export const ConnectionImageWithMaskSvg: React.FC<Props> = ({ icon, connected, m
|
||||
mask={`url(#${maskId})`}
|
||||
/>
|
||||
</mask>
|
||||
<rect className={styles.background} x="0" y="0" width={size} height={size} mask={connected ? `url(#${maskId})` : undefined} />
|
||||
<image xlinkHref={url} width={size} height={size} mask={connected ? `url(#${maskId})` : undefined} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -49,10 +49,6 @@ export const BASE_CONTAINERS_STYLES = css`
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&[flexEnd] {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
&[center] {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -68,7 +64,7 @@ export const BASE_CONTAINERS_STYLES = css`
|
||||
}
|
||||
|
||||
&[parent] {
|
||||
padding: 16px;
|
||||
padding: 24px;
|
||||
|
||||
&[compact] {
|
||||
padding: 16px;
|
||||
@@ -80,14 +76,14 @@ export const BASE_CONTAINERS_STYLES = css`
|
||||
}
|
||||
|
||||
&[gap] {
|
||||
gap: 16px;
|
||||
gap: 24px;
|
||||
|
||||
&[compact] {
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
&[dense] {
|
||||
gap: 16px;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,7 +91,7 @@ export const BASE_CONTAINERS_STYLES = css`
|
||||
Group {
|
||||
align-content: baseline;
|
||||
box-sizing: border-box;
|
||||
padding: 16px;
|
||||
padding: 24px;
|
||||
border-radius: var(--theme-group-element-radius);
|
||||
|
||||
&[dense] {
|
||||
@@ -221,7 +217,7 @@ export const BASE_CONTAINERS_STYLES = css`
|
||||
}
|
||||
|
||||
Group[box] > GroupTitle {
|
||||
padding: 16px;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
GroupSubTitle {
|
||||
|
||||
@@ -10,7 +10,6 @@ import type { ILayoutSizeProps } from './ILayoutSizeProps';
|
||||
export interface IContainerProps extends ILayoutSizeProps {
|
||||
hideEmpty?: boolean;
|
||||
flexStart?: boolean;
|
||||
flexEnd?: boolean;
|
||||
baseline?: boolean;
|
||||
center?: boolean;
|
||||
vertical?: boolean;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { filterLayoutFakeProps } from './filterLayoutFakeProps';
|
||||
import type { IContainerProps } from './IContainerProps';
|
||||
|
||||
export function filterContainerFakeProps<T extends IContainerProps>(props: T): Omit<T, keyof IContainerProps> {
|
||||
const { hideEmpty, flexStart, flexEnd, baseline, center, vertical, wrap, overflow, parent, gap, grid, dense, compact, ...rest } =
|
||||
const { hideEmpty, flexStart, baseline, center, vertical, wrap, overflow, parent, gap, grid, dense, compact, ...rest } =
|
||||
filterLayoutFakeProps(props);
|
||||
|
||||
return rest as Omit<T, keyof IContainerProps>;
|
||||
|
||||
@@ -29,7 +29,7 @@ const style = css`
|
||||
error-inner-block {
|
||||
display: flex;
|
||||
margin: auto;
|
||||
padding: 8px 16px;
|
||||
padding: 16px 24px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ const style = css`
|
||||
error {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
error-name {
|
||||
@@ -30,7 +30,6 @@ const style = css`
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
|
||||
& span {
|
||||
display: inline-block;
|
||||
@@ -149,7 +148,7 @@ export const ExceptionMessage = observer<Props>(function ExceptionMessage({ exce
|
||||
{!icon && (
|
||||
<>
|
||||
<error-data>
|
||||
<error-name as="h2">
|
||||
<error-name>
|
||||
<span>{translate('core_blocks_exception_message_error_title')}</span>
|
||||
</error-name>
|
||||
<error-message>
|
||||
|
||||
@@ -32,7 +32,7 @@ export const BASE_DROPDOWN_STYLES = css`
|
||||
outline: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
gap: 16px;
|
||||
gap: 8px;
|
||||
|
||||
& item-icon,
|
||||
& item-title {
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
.checkbox {
|
||||
}
|
||||
@@ -10,10 +10,7 @@ import styled, { css } from 'reshadow';
|
||||
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { s } from '../../s';
|
||||
import { useS } from '../../useS';
|
||||
import { useStyles } from '../../useStyles';
|
||||
import CheckboxMarkupStyles from './CheckboxMarkup.m.css';
|
||||
|
||||
export type CheckboxMod = 'primary' | 'surface' | 'small';
|
||||
|
||||
@@ -98,8 +95,6 @@ interface ICheckboxMarkupProps extends Omit<React.InputHTMLAttributes<HTMLInputE
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
export { CheckboxMarkupStyles };
|
||||
|
||||
export const CheckboxMarkup: React.FC<ICheckboxMarkupProps> = function CheckboxMarkup({
|
||||
id,
|
||||
label,
|
||||
@@ -112,7 +107,6 @@ export const CheckboxMarkup: React.FC<ICheckboxMarkupProps> = function CheckboxM
|
||||
readOnly,
|
||||
...rest
|
||||
}) {
|
||||
const styles = useS(CheckboxMarkupStyles);
|
||||
const checkboxRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
@@ -131,7 +125,7 @@ export const CheckboxMarkup: React.FC<ICheckboxMarkupProps> = function CheckboxM
|
||||
),
|
||||
)(
|
||||
<checkbox-container className={className} title={title}>
|
||||
<checkbox className={s(styles, { checkbox: true })}>
|
||||
<checkbox>
|
||||
<checkbox-input ref={checkboxRef} as="input" type="checkbox" {...rest} disabled={rest.disabled || readOnly} id={id || rest.name} />
|
||||
<checkbox-background>
|
||||
<checkbox-checkmark as="svg" viewBox="0 0 24 24">
|
||||
|
||||
@@ -13,11 +13,13 @@ import { isControlPresented } from '../isControlPresented';
|
||||
import { Checkbox, CheckboxBaseProps, CheckboxType, ICheckboxControlledProps, ICheckboxObjectProps } from './Checkbox';
|
||||
|
||||
const style = css`
|
||||
Checkbox {
|
||||
margin: -10px;
|
||||
}
|
||||
field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: pre-wrap;
|
||||
height: 32px;
|
||||
|
||||
& field-label {
|
||||
cursor: pointer;
|
||||
|
||||
@@ -82,7 +82,7 @@ const styles = css`
|
||||
outline: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
gap: 16px;
|
||||
gap: 8px;
|
||||
|
||||
& item-icon,
|
||||
& item-title {
|
||||
@@ -193,7 +193,7 @@ export const Combobox: ComboboxType = observer(function Combobox({
|
||||
iconSelector,
|
||||
titleSelector,
|
||||
isDisabled,
|
||||
onChange = () => {},
|
||||
onChange = () => { },
|
||||
onSelect,
|
||||
onSwitch,
|
||||
...rest
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
header {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
height: 32px;
|
||||
height: 48px;
|
||||
padding: 0 16px;
|
||||
background-color: #2a7cb4;
|
||||
display: flex;
|
||||
@@ -21,8 +21,8 @@
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 24px;
|
||||
width: 120px;
|
||||
height: 34px;
|
||||
width: 154px;
|
||||
}
|
||||
|
||||
.content {
|
||||
@@ -45,12 +45,16 @@
|
||||
|
||||
<div class="content">
|
||||
<div class="message">
|
||||
<p>Authorization was successful.</p>
|
||||
<p>Now you can close this page.</p>
|
||||
<p>
|
||||
Authorization was successful.
|
||||
</p>
|
||||
<p>
|
||||
Now you can close this page.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.close();
|
||||
</script>
|
||||
</script>
|
||||
@@ -31,7 +31,7 @@ const styles = css`
|
||||
IconOrImage {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 8px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ export const ITEM_LIST_STYLES = css`
|
||||
composes: theme-background-surface theme-text-on-surface theme-typography--body1 from global;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding: 8px 16px;
|
||||
padding: 16px 24px;
|
||||
z-index: 1;
|
||||
|
||||
& input-box {
|
||||
@@ -83,13 +83,13 @@ export const ITEM_LIST_STYLES = css`
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
list-item-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: 4px;
|
||||
padding: 8px;
|
||||
|
||||
& StaticImage {
|
||||
box-sizing: border-box;
|
||||
@@ -101,7 +101,7 @@ export const ITEM_LIST_STYLES = css`
|
||||
box-sizing: border-box;
|
||||
font-weight: 500;
|
||||
min-width: 250px;
|
||||
padding: 4px 16px 4px 8px;
|
||||
padding: 8px 24px 8px 8px;
|
||||
border-right: 1px solid;
|
||||
|
||||
white-space: nowrap;
|
||||
@@ -112,7 +112,7 @@ export const ITEM_LIST_STYLES = css`
|
||||
composes: theme-typography--body2 from global;
|
||||
box-sizing: border-box;
|
||||
max-width: 460px;
|
||||
padding: 4px 8px 4px 16px;
|
||||
padding: 8px 8px 8px 24px;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
.menuButton {
|
||||
composes: theme-ripple from global;
|
||||
}
|
||||
.menuButton {
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none !important;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
|
||||
& .box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
height: inherit;
|
||||
}
|
||||
}
|
||||
.menuButtonLink {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
@@ -8,22 +8,25 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { forwardRef, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { MenuButton, MenuInitialState, useMenuState } from 'reakit/Menu';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { ErrorBoundary } from '../ErrorBoundary';
|
||||
import { s } from '../s';
|
||||
import { useCombinedRef } from '../useCombinedRef';
|
||||
import { useObjectRef } from '../useObjectRef';
|
||||
import { useS } from '../useS';
|
||||
import style from './Menu.m.css';
|
||||
import { useStyles } from '../useStyles';
|
||||
import { MenuPanel } from './MenuPanel';
|
||||
import { menuPanelStyles } from './menuPanelStyles';
|
||||
import { IMenuState, MenuStateContext } from './MenuStateContext';
|
||||
import type { IMouseContextMenu } from './useMouseContextMenu';
|
||||
|
||||
interface IMenuProps extends React.ButtonHTMLAttributes<any> {
|
||||
interface IMenuProps extends Omit<React.ButtonHTMLAttributes<any>, 'style'> {
|
||||
mouseContextMenu?: IMouseContextMenu;
|
||||
label: string;
|
||||
items: React.ReactNode | (() => React.ReactNode);
|
||||
menuRef?: React.RefObject<IMenuState | undefined>;
|
||||
style?: ComponentStyle;
|
||||
disclosure?: boolean;
|
||||
placement?: MenuInitialState['placement'];
|
||||
submenu?: boolean;
|
||||
@@ -45,6 +48,7 @@ export const Menu = observer<IMenuProps, HTMLButtonElement>(
|
||||
menuRef,
|
||||
disclosure,
|
||||
children,
|
||||
style,
|
||||
placement,
|
||||
visible,
|
||||
hasBindings,
|
||||
@@ -54,7 +58,6 @@ export const Menu = observer<IMenuProps, HTMLButtonElement>(
|
||||
modal,
|
||||
submenu,
|
||||
rtl,
|
||||
className,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
@@ -71,7 +74,7 @@ export const Menu = observer<IMenuProps, HTMLButtonElement>(
|
||||
visible,
|
||||
rtl,
|
||||
});
|
||||
const styles = useS(style);
|
||||
const styles = useStyles(menuPanelStyles, style);
|
||||
|
||||
if (menuRef) {
|
||||
//@ts-expect-error Ref mutation
|
||||
@@ -127,24 +130,17 @@ export const Menu = observer<IMenuProps, HTMLButtonElement>(
|
||||
const MenuButtonLink = MenuButton;
|
||||
|
||||
if (React.isValidElement(children) && disclosure) {
|
||||
return (
|
||||
return styled(styles)(
|
||||
<ErrorBoundary>
|
||||
<MenuStateContext.Provider value={menu}>
|
||||
<MenuButton
|
||||
key={relativePosition ? 'link' : 'main'}
|
||||
ref={combinedRef}
|
||||
className={s(styles, { menuButton: true }, className)}
|
||||
{...menu}
|
||||
visible={menuVisible}
|
||||
{...props}
|
||||
{...children.props}
|
||||
>
|
||||
<MenuButton key={relativePosition ? 'link' : 'main'} ref={combinedRef} {...menu} visible={menuVisible} {...props} {...children.props}>
|
||||
{disclosureProps => React.cloneElement(children, { ...disclosureProps, ...children.props })}
|
||||
</MenuButton>
|
||||
<MenuPanel
|
||||
ref={menuPanelRef}
|
||||
label={label}
|
||||
menu={menu}
|
||||
style={style}
|
||||
rtl={rtl}
|
||||
submenu={submenu}
|
||||
panelAvailable={panelAvailable}
|
||||
@@ -153,31 +149,23 @@ export const Menu = observer<IMenuProps, HTMLButtonElement>(
|
||||
>
|
||||
{items}
|
||||
</MenuPanel>
|
||||
{relativePosition && (
|
||||
<MenuButtonLink ref={menuButtonLinkRef} className={s(styles, { menuButtonLink: true })} {...menu} visible={menuVisible} />
|
||||
)}
|
||||
{relativePosition && <MenuButtonLink ref={menuButtonLinkRef} {...menu} visible={menuVisible} />}
|
||||
</MenuStateContext.Provider>
|
||||
</ErrorBoundary>
|
||||
</ErrorBoundary>,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
return styled(styles)(
|
||||
<ErrorBoundary>
|
||||
<MenuStateContext.Provider value={menu}>
|
||||
<MenuButton
|
||||
key={relativePosition ? 'link' : 'main'}
|
||||
ref={combinedRef}
|
||||
className={s(styles, { menuButton: true }, className)}
|
||||
{...menu}
|
||||
visible={menuVisible}
|
||||
{...props}
|
||||
>
|
||||
<div className={s(styles, { box: true }, className)}>{children}</div>
|
||||
<MenuButton key={relativePosition ? 'link' : 'main'} ref={combinedRef} {...menu} visible={menuVisible} {...props}>
|
||||
<box>{children}</box>
|
||||
</MenuButton>
|
||||
<MenuPanel
|
||||
ref={menuPanelRef}
|
||||
label={label}
|
||||
menu={menu}
|
||||
style={style}
|
||||
rtl={rtl}
|
||||
submenu={submenu}
|
||||
panelAvailable={panelAvailable}
|
||||
@@ -186,11 +174,9 @@ export const Menu = observer<IMenuProps, HTMLButtonElement>(
|
||||
>
|
||||
{items}
|
||||
</MenuPanel>
|
||||
{relativePosition && (
|
||||
<MenuButtonLink ref={menuButtonLinkRef} className={s(styles, { menuButtonLink: true })} {...menu} visible={menuVisible} />
|
||||
)}
|
||||
{relativePosition && <MenuButtonLink ref={menuButtonLinkRef} {...menu} visible={menuVisible} />}
|
||||
</MenuStateContext.Provider>
|
||||
</ErrorBoundary>
|
||||
</ErrorBoundary>,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -6,19 +6,29 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { useTranslate } from '../localization/useTranslate';
|
||||
import { useStyles } from '../useStyles';
|
||||
import { MenuItem } from './MenuItem';
|
||||
import { MenuItemElement } from './MenuItemElement';
|
||||
import { menuPanelStyles } from './menuPanelStyles';
|
||||
|
||||
export const MenuEmptyItem = observer(function MenuEmptyItem() {
|
||||
interface Props extends Omit<React.ButtonHTMLAttributes<any>, 'style'> {
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
export const MenuEmptyItem = observer<Props>(function MenuEmptyItem({ style }) {
|
||||
const styles = useStyles(menuPanelStyles, style);
|
||||
const translate = useTranslate();
|
||||
|
||||
const label = translate('core_ui_empty');
|
||||
|
||||
return (
|
||||
<MenuItem id="empty" aria-label={label} disabled>
|
||||
<MenuItemElement label={label} />
|
||||
</MenuItem>
|
||||
return styled(styles)(
|
||||
<MenuItem id="empty" label={label} disabled>
|
||||
<MenuItemElement label={label} style={style} />
|
||||
</MenuItem>,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
.menuItem {
|
||||
composes: theme-ripple from global;
|
||||
composes: menuItem from './MenuItemElement.m.css';
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: none;
|
||||
text-align: left;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:global([aria-selected='true']) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
@@ -8,44 +8,46 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useContext } from 'react';
|
||||
import { MenuItem as ReakitMenuItem } from 'reakit/Menu';
|
||||
import type { CompositeItemOptions } from 'reakit/ts';
|
||||
import styled, { use } from 'reshadow';
|
||||
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { s } from '../s';
|
||||
import { useCombinedHandler } from '../useCombinedHandler';
|
||||
import { useS } from '../useS';
|
||||
import style from './MenuItem.m.css';
|
||||
import { useStyles } from '../useStyles';
|
||||
import { menuPanelStyles } from './menuPanelStyles';
|
||||
import { MenuStateContext } from './MenuStateContext';
|
||||
import type { ReakitProxyComponent, ReakitProxyComponentOptions } from './ReakitProxyComponent';
|
||||
|
||||
export type MenuItemOptions = CompositeItemOptions & {
|
||||
export interface IMenuItemProps extends Omit<React.ButtonHTMLAttributes<any>, 'style'> {
|
||||
label: string;
|
||||
hidden?: boolean;
|
||||
selected?: boolean;
|
||||
close?: boolean;
|
||||
};
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
export const MenuItem: ReakitProxyComponent<'button', MenuItemOptions> = observer<ReakitProxyComponentOptions<'button', MenuItemOptions>>(
|
||||
function MenuItem({ children, hidden, selected, close, onClick, className, ...rest }) {
|
||||
const menu = useContext(MenuStateContext);
|
||||
const styles = useS(style);
|
||||
export const MenuItem = observer<IMenuItemProps>(function MenuItem({ label, children, hidden, selected, close, style, onClick, ...rest }) {
|
||||
const menu = useContext(MenuStateContext);
|
||||
const styles = useStyles(menuPanelStyles, style);
|
||||
|
||||
const handleClick = useCombinedHandler<[React.MouseEvent<HTMLButtonElement>]>(onClick, function handleClick() {
|
||||
if (close) {
|
||||
menu?.hide();
|
||||
}
|
||||
});
|
||||
const handleClick = useCombinedHandler<[React.MouseEvent<HTMLButtonElement>]>(onClick, function handleClick() {
|
||||
if (close) {
|
||||
menu?.hide();
|
||||
}
|
||||
});
|
||||
|
||||
const MenuItem = ReakitMenuItem;
|
||||
const MenuItem = ReakitMenuItem;
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
{...menu}
|
||||
aria-selected={selected}
|
||||
{...rest}
|
||||
className={s(styles, { menuItem: true, hidden }, className)}
|
||||
disabled={selected || rest.disabled}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{children}
|
||||
</MenuItem>
|
||||
);
|
||||
},
|
||||
);
|
||||
return styled(styles)(
|
||||
<MenuItem
|
||||
{...menu}
|
||||
{...use({ hidden: hidden })}
|
||||
aria-label={label}
|
||||
aria-selected={selected}
|
||||
{...rest}
|
||||
disabled={selected || rest.disabled}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{children}
|
||||
</MenuItem>,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* 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 { observer } from 'mobx-react-lite';
|
||||
import { useContext } from 'react';
|
||||
import { MenuItemOptions, MenuItemCheckbox as ReakitMenuItemCheckbox } from 'reakit/Menu';
|
||||
import type { CheckboxOptions } from 'reakit/ts';
|
||||
|
||||
import { s } from '../s';
|
||||
import { useS } from '../useS';
|
||||
import style from './MenuItem.m.css';
|
||||
import { MenuStateContext } from './MenuStateContext';
|
||||
import type { ReakitProxyComponent, ReakitProxyComponentOptions } from './ReakitProxyComponent';
|
||||
|
||||
type Options = CheckboxOptions & MenuItemOptions;
|
||||
|
||||
export const MenuItemCheckbox: ReakitProxyComponent<'button', Options> = observer<ReakitProxyComponentOptions<'button', Options>>(
|
||||
function MenuItemCheckbox({ hidden, children, className, ...rest }) {
|
||||
const menu = useContext(MenuStateContext);
|
||||
const styles = useS(style);
|
||||
|
||||
const MenuItemCheckbox = ReakitMenuItemCheckbox;
|
||||
|
||||
return (
|
||||
<MenuItemCheckbox {...menu} className={s(styles, { menuItem: true, hidden }, className)} {...rest}>
|
||||
{children}
|
||||
</MenuItemCheckbox>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -1,70 +0,0 @@
|
||||
.menuPanelItem {
|
||||
composes: theme-ripple theme-border-color-background from global;
|
||||
|
||||
flex: 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
height: 26px;
|
||||
padding: 0 4px;
|
||||
|
||||
& .menuItemText,
|
||||
& .menuItemBinding {
|
||||
display: block;
|
||||
padding: 0 4px;
|
||||
}
|
||||
& .menuItemBinding {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
padding-left: 32px;
|
||||
width: 80px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
& .menuItemIcon,
|
||||
& .menuItemContent {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
& .menuItemContent .icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
& .iconOrImage {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
object-fit: contain;
|
||||
}
|
||||
& .loader {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.menuItemBinding {
|
||||
composes: theme-typography--caption from global;
|
||||
}
|
||||
|
||||
.menuBox:not([data-s-has-bindings='true']) .menuItemBinding {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.menuBox[dir='rtl'] {
|
||||
& .menuItemText {
|
||||
text-align: right;
|
||||
}
|
||||
& .menuItemContent .icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.menuItem:hover,
|
||||
.menuItem:global([aria-expanded='true']) {
|
||||
& .menuItemContent .icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -6,15 +6,17 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { Icon } from '../Icon';
|
||||
import { IconOrImage } from '../IconOrImage';
|
||||
import { Loader } from '../Loader/Loader';
|
||||
import { useTranslate } from '../localization/useTranslate';
|
||||
import { s } from '../s';
|
||||
import { useS } from '../useS';
|
||||
import { useStateDelay } from '../useStateDelay';
|
||||
import style from './MenuItemElement.m.css';
|
||||
import { useStyles } from '../useStyles';
|
||||
import { menuPanelStyles } from './menuPanelStyles';
|
||||
|
||||
interface IMenuItemElementProps {
|
||||
label: string;
|
||||
@@ -26,6 +28,7 @@ interface IMenuItemElementProps {
|
||||
menu?: boolean;
|
||||
loading?: boolean;
|
||||
panelAvailable?: boolean;
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
export const MenuItemElement = observer<IMenuItemElementProps>(function MenuItemElement({
|
||||
@@ -37,34 +40,22 @@ export const MenuItemElement = observer<IMenuItemElementProps>(function MenuItem
|
||||
menu,
|
||||
panelAvailable,
|
||||
loading = false,
|
||||
style = [],
|
||||
}) {
|
||||
const styles = useS(style);
|
||||
const translate = useTranslate();
|
||||
|
||||
const title = translate(label);
|
||||
loading = useStateDelay(loading, 100);
|
||||
|
||||
return (
|
||||
<div className={s(styles, { menuPanelItem: true })} title={tooltip ? translate(tooltip) : title}>
|
||||
<div className={s(styles, { menuItemIcon: true })}>
|
||||
<Loader className={s(styles, { loader: true })} suspense small fullSize>
|
||||
{typeof icon === 'string' ? <IconOrImage className={s(styles, { iconOrImage: true })} icon={icon} /> : icon}
|
||||
</Loader>
|
||||
</div>
|
||||
{displayLabel ? (
|
||||
<div className={s(styles, { menuItemText: true })} title={title}>
|
||||
{title}
|
||||
</div>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
<div className={s(styles, { menuItemBinding: true })} title={binding}>
|
||||
{binding}
|
||||
</div>
|
||||
<div className={s(styles, { menuItemContent: true })}>
|
||||
{loading && <Loader className={s(styles, { loader: true })} small fullSize />}
|
||||
{panelAvailable !== false && menu && !loading && <Icon name="context-menu-submenu" viewBox="0 0 6 7" className={s(styles, { icon: true })} />}
|
||||
</div>
|
||||
</div>
|
||||
return styled(useStyles(menuPanelStyles, style))(
|
||||
<menu-panel-item title={tooltip ? translate(tooltip) : title}>
|
||||
<menu-item-icon>{typeof icon === 'string' ? <IconOrImage icon={icon} /> : icon}</menu-item-icon>
|
||||
{displayLabel ? <menu-item-text title={title}>{title}</menu-item-text> : <padding />}
|
||||
<menu-item-binding title={binding}>{binding}</menu-item-binding>
|
||||
<menu-item-content>
|
||||
{loading && <Loader small fullSize />}
|
||||
{panelAvailable !== false && menu && !loading && <Icon name="context-menu-submenu" viewBox="0 0 6 7" />}
|
||||
</menu-item-content>
|
||||
</menu-panel-item>,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* 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 { observer } from 'mobx-react-lite';
|
||||
import { useContext } from 'react';
|
||||
import { MenuItemOptions, MenuItemRadio as ReakitMenuItemRadio } from 'reakit/Menu';
|
||||
import type { RadioOptions } from 'reakit/ts';
|
||||
|
||||
import { s } from '../s';
|
||||
import { useS } from '../useS';
|
||||
import style from './MenuItem.m.css';
|
||||
import { MenuStateContext } from './MenuStateContext';
|
||||
import type { ReakitProxyComponent, ReakitProxyComponentOptions } from './ReakitProxyComponent';
|
||||
|
||||
type Options = RadioOptions & MenuItemOptions;
|
||||
|
||||
export const MenuItemRadio: ReakitProxyComponent<'button', Options> = observer<ReakitProxyComponentOptions<'button', Options>>(
|
||||
function MenuItemRadio({ hidden, children, className, ...rest }) {
|
||||
const menu = useContext(MenuStateContext);
|
||||
const styles = useS(style);
|
||||
|
||||
const MenuItemRadio = ReakitMenuItemRadio;
|
||||
|
||||
return (
|
||||
<MenuItemRadio {...menu} className={s(styles, { menuItem: true, hidden }, className)} {...rest}>
|
||||
{children}
|
||||
</MenuItemRadio>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -1,27 +0,0 @@
|
||||
.menu {
|
||||
outline: none;
|
||||
z-index: 999;
|
||||
}
|
||||
.menuBox {
|
||||
composes: theme-background-surface theme-text-on-surface theme-group-element-radius theme-typography--caption theme-elevation-z5 from global;
|
||||
composes: menuBox from './MenuItemElement.m.css';
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 140px;
|
||||
padding: 6px 0;
|
||||
|
||||
& .menuBox {
|
||||
margin-top: -6px;
|
||||
}
|
||||
}
|
||||
|
||||
.menu.modal .menuBox {
|
||||
overflow: auto;
|
||||
max-height: 80vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.menu.submenu {
|
||||
margin-top: -6px;
|
||||
}
|
||||
@@ -8,13 +8,15 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Children, forwardRef } from 'react';
|
||||
import { Menu, MenuStateReturn } from 'reakit/Menu';
|
||||
import styled, { use } from 'reshadow';
|
||||
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { ErrorBoundary } from '../ErrorBoundary';
|
||||
import { getComputed } from '../getComputed';
|
||||
import { s } from '../s';
|
||||
import { useS } from '../useS';
|
||||
import { useStyles } from '../useStyles';
|
||||
import { MenuEmptyItem } from './MenuEmptyItem';
|
||||
import style from './MenuPanel.m.css';
|
||||
import { menuPanelStyles } from './menuPanelStyles';
|
||||
|
||||
export interface IMenuPanelProps {
|
||||
label: string;
|
||||
@@ -25,12 +27,13 @@ export interface IMenuPanelProps {
|
||||
children: React.ReactNode | (() => React.ReactNode);
|
||||
rtl?: boolean;
|
||||
submenu?: boolean;
|
||||
style?: ComponentStyle;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const MenuPanel = observer<IMenuPanelProps, HTMLDivElement>(
|
||||
forwardRef(function MenuPanel({ label, menu, submenu, panelAvailable = true, rtl, getHasBindings, hasBindings, children, className }, ref) {
|
||||
const styles = useS(style);
|
||||
forwardRef(function MenuPanel({ label, menu, panelAvailable = true, rtl, getHasBindings, hasBindings, children, style, className }, ref) {
|
||||
const styles = useStyles(menuPanelStyles, style);
|
||||
const visible = menu.visible;
|
||||
|
||||
if (!visible) {
|
||||
@@ -45,21 +48,15 @@ export const MenuPanel = observer<IMenuPanelProps, HTMLDivElement>(
|
||||
renderedChildren = typeof children === 'function' ? children() : children;
|
||||
}
|
||||
|
||||
return (
|
||||
return styled(styles)(
|
||||
<ErrorBoundary>
|
||||
<Menu
|
||||
ref={ref}
|
||||
className={s(styles, { menu: true, modal: menu.modal, submenu }, className)}
|
||||
{...menu}
|
||||
aria-label={label}
|
||||
visible={panelAvailable}
|
||||
>
|
||||
<div dir={rtl ? 'rtl' : undefined} data-s-has-bindings={hasBindings} className={s(styles, { menuBox: true })}>
|
||||
{Children.count(renderedChildren) === 0 && <MenuEmptyItem />}
|
||||
<Menu ref={ref} {...menu} aria-label={label} className={className} visible={panelAvailable}>
|
||||
<menu-box dir={rtl ? 'rtl' : undefined} {...use({ hasBindings })}>
|
||||
{Children.count(renderedChildren) === 0 && <MenuEmptyItem style={style} />}
|
||||
{renderedChildren}
|
||||
</div>
|
||||
</menu-box>
|
||||
</Menu>
|
||||
</ErrorBoundary>
|
||||
</ErrorBoundary>,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
.menuSeparator {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin: 4px 0;
|
||||
|
||||
&:first-child,
|
||||
&:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* 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 { MenuSeparator as MenuSeparatorBase } from 'reakit/Menu';
|
||||
import type { SeparatorOptions } from 'reakit/ts';
|
||||
|
||||
import { s } from '../s';
|
||||
import { useS } from '../useS';
|
||||
import style from './MenuSeparator.m.css';
|
||||
import type { ReakitProxyComponentOptions } from './ReakitProxyComponent';
|
||||
|
||||
export const MenuSeparator = function MenuSeparator({ className, ...props }: ReakitProxyComponentOptions<'hr', SeparatorOptions>) {
|
||||
const styles = useS(style);
|
||||
return <MenuSeparatorBase {...props} className={s(styles, { menuSeparator: true }, className)} />;
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* 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 type { ReactElement } from 'react';
|
||||
import type { As, PropsWithAs } from 'reakit-utils/types';
|
||||
|
||||
export type ReakitProxyPropsWithAs<O, T extends As> = Omit<PropsWithAs<O, T>, 'children'> & {
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
export type ReakitProxyComponentOptions<T extends As, O> = ReakitProxyPropsWithAs<O, T>;
|
||||
|
||||
export type ReakitProxyComponent<T extends As, O> = {
|
||||
<TT extends As = T>(
|
||||
props: ReakitProxyPropsWithAs<O, TT> & {
|
||||
as: TT;
|
||||
},
|
||||
context?: any,
|
||||
): ReactElement<any, any> | null;
|
||||
(props: ReakitProxyPropsWithAs<O, T>, context?: any): ReactElement<any, any> | null;
|
||||
};
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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 { css } from 'reshadow';
|
||||
|
||||
export const menuPanelStyles = css`
|
||||
menu-box {
|
||||
composes: theme-background-surface theme-text-on-surface from global;
|
||||
}
|
||||
MenuItem,
|
||||
MenuItemCheckbox,
|
||||
MenuItemRadio,
|
||||
MenuButton,
|
||||
MenuItemElement {
|
||||
composes: theme-ripple from global;
|
||||
}
|
||||
menu-panel-item,
|
||||
MenuSeparator {
|
||||
composes: theme-border-color-background from global;
|
||||
}
|
||||
MenuButton {
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none !important;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
& box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
height: inherit;
|
||||
}
|
||||
}
|
||||
MenuButtonLink {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
Menu {
|
||||
outline: none;
|
||||
z-index: 999;
|
||||
}
|
||||
MenuPanel[submenu] {
|
||||
margin-top: -6px;
|
||||
}
|
||||
menu-box {
|
||||
composes: theme-group-element-radius theme-typography--body2 theme-elevation-z5 from global;
|
||||
min-width: 140px;
|
||||
padding: 6px 0;
|
||||
|
||||
& menu-box {
|
||||
margin-top: -6px;
|
||||
}
|
||||
}
|
||||
|
||||
menu-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&:not([|hasBindings]) menu-item-binding {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
Menu[modal] menu-box {
|
||||
overflow: auto;
|
||||
max-height: 80vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
MenuItem,
|
||||
MenuItemCheckbox,
|
||||
MenuItemRadio {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: none;
|
||||
text-align: left;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
|
||||
&[use|hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:global([aria-selected='true']) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:global([aria-expanded='true']) {
|
||||
& Icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin: 6px 0;
|
||||
border: none !important;
|
||||
border-bottom: 1px solid !important;
|
||||
|
||||
&:first-child,
|
||||
&:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
menu-item-binding {
|
||||
composes: theme-typography--caption from global;
|
||||
}
|
||||
|
||||
menu-panel-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
padding: 0 4px;
|
||||
|
||||
& menu-item-text,
|
||||
& menu-item-binding {
|
||||
display: block;
|
||||
padding: 0 4px;
|
||||
}
|
||||
& menu-item-binding {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
padding-left: 32px;
|
||||
width: 80px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
& menu-item-icon,
|
||||
& menu-item-content {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
& menu-item-content Icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
& Icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
& IconOrImage {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
object-fit: contain;
|
||||
}
|
||||
& Loader {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
Menu[rtl] {
|
||||
& menu-item-text {
|
||||
text-align: right;
|
||||
}
|
||||
& menu-panel-item menu-item-content Icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -60,7 +60,7 @@ export const OVERLAY_BASE_STYLES = css`
|
||||
message,
|
||||
actions {
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
message {
|
||||
|
||||
@@ -37,7 +37,7 @@ const styles = css`
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
padding: 2px 0;
|
||||
padding: 4px 0;
|
||||
|
||||
& ShadowInput {
|
||||
height: 24px;
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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 React, { createContext } from 'react';
|
||||
|
||||
import type { IStyleRegistry, Style } from '@cloudbeaver/core-theming';
|
||||
|
||||
type Registry = Map<Style, IStyleRegistry[]>;
|
||||
|
||||
export type StyleRegistry = [Style, IStyleRegistry][];
|
||||
|
||||
export const SContextReact = createContext<Registry>(new Map());
|
||||
|
||||
type Props = {
|
||||
registry: StyleRegistry;
|
||||
};
|
||||
|
||||
export const SContext: React.FC<React.PropsWithChildren<Props>> = function SContext({ registry, children }) {
|
||||
const context = React.useContext(SContextReact);
|
||||
const state = new Map(context.entries());
|
||||
|
||||
for (const [style, styles] of registry) {
|
||||
state.set(style, [...(state.get(style) || []), styles]);
|
||||
}
|
||||
|
||||
return <SContextReact.Provider value={state}>{children}</SContextReact.Provider>;
|
||||
};
|
||||
@@ -1,123 +0,0 @@
|
||||
.container {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
|
||||
/* pointer outline */
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
composes: theme-button theme-button_unelevated theme-button_background theme-elevation-z3 from global;
|
||||
opacity: 0;
|
||||
transition-property: opacity, box-shadow !important;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.ripple {
|
||||
composes: theme-button_ripple from global;
|
||||
}
|
||||
|
||||
.container:hover .button {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.container[data-s-split='vertical'] {
|
||||
flex-direction: column;
|
||||
cursor: ew-resize;
|
||||
height: 100%;
|
||||
width: 5px;
|
||||
left: -2px;
|
||||
|
||||
&[data-s-mode='minimize'] {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&[data-s-mode='maximize'] {
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
& .button {
|
||||
margin: 8px 0;
|
||||
min-width: 16px;
|
||||
|
||||
&::after {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-s-mode='maximize'] .button,
|
||||
&[data-s-mode='minimize'] .button {
|
||||
min-height: 64px;
|
||||
}
|
||||
|
||||
& .button.primary::after {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
.container[data-s-split='vertical'].inverse {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.container[data-s-split='horizontal'] {
|
||||
flex-direction: row;
|
||||
cursor: ns-resize;
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
top: -2px;
|
||||
|
||||
&[data-s-mode='minimize'] {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&[data-s-mode='maximize'] {
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
& .button {
|
||||
margin: 0 8px;
|
||||
min-width: 28px;
|
||||
height: 16px;
|
||||
|
||||
&::after {
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-s-mode='maximize'] .button,
|
||||
&[data-s-mode='minimize'] .button {
|
||||
min-width: 64px;
|
||||
}
|
||||
|
||||
& .button.primary::after {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.container[data-s-split='horizontal'].inverse {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 0 !important;
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
content: '';
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 3px 4px 3px 0;
|
||||
border-color: transparent #000 transparent transparent;
|
||||
margin: -3px -2px;
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,14 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { s } from '../s';
|
||||
import styled, { use } from 'reshadow';
|
||||
|
||||
import { useObjectRef } from '../useObjectRef';
|
||||
import { useS } from '../useS';
|
||||
import SplitControlsStyles from './SplitControls.m.css';
|
||||
import { buttonStyles } from './splitButtonStyles';
|
||||
import { useSplit } from './useSplit';
|
||||
|
||||
export const SplitControls: React.FC = function SplitControls() {
|
||||
const split = useSplit();
|
||||
const styles = useS(SplitControlsStyles);
|
||||
|
||||
const inverse = split.state.isMainSecond();
|
||||
|
||||
@@ -52,27 +51,25 @@ export const SplitControls: React.FC = function SplitControls() {
|
||||
['handleCollapse', 'handleExpand'],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-s-split={split.state.split}
|
||||
data-s-mode={inverseMode}
|
||||
className={s(styles, { container: true, inverse })}
|
||||
return styled(buttonStyles)(
|
||||
<container
|
||||
onMouseDown={split.state.onMouseDown}
|
||||
onTouchStart={split.state.onTouchStart}
|
||||
onTouchEnd={split.state.onTouchEnd}
|
||||
onClick={split.state.onClick}
|
||||
onDoubleClick={split.state.onDoubleClick}
|
||||
{...use({ split: split.state.split, inverse, mode: inverseMode })}
|
||||
>
|
||||
{split.state.mode !== 'minimize' && (
|
||||
<button className={s(styles, { button: true, primary: !inverse })} type="button" onClick={handlers.handleCollapse}>
|
||||
<div className={s(styles, { ripple: true })} />
|
||||
<button type="button" {...use({ isPrimary: !inverse })} onClick={handlers.handleCollapse}>
|
||||
<ripple />
|
||||
</button>
|
||||
)}
|
||||
{split.state.mode !== 'maximize' && (
|
||||
<button className={s(styles, { button: true, primary: inverse })} type="button" onClick={handlers.handleExpand}>
|
||||
<div className={s(styles, { ripple: true })} />
|
||||
<button type="button" {...use({ isPrimary: inverse })} onClick={handlers.handleExpand}>
|
||||
<ripple />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</container>,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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 { css } from 'reshadow';
|
||||
|
||||
export const buttonStyles = css`
|
||||
container {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
|
||||
/* pointer outline */
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
composes: theme-button theme-button_unelevated theme-button_background theme-elevation-z3 from global;
|
||||
opacity: 0;
|
||||
transition-property: opacity, box-shadow !important;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
ripple {
|
||||
composes: theme-button_ripple from global;
|
||||
}
|
||||
|
||||
container:hover button {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
container[|split='vertical'] {
|
||||
flex-direction: column;
|
||||
cursor: ew-resize;
|
||||
height: 100%;
|
||||
width: 5px;
|
||||
left: -2px;
|
||||
|
||||
&[|mode='minimize'] {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&[|mode='maximize'] {
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
& button {
|
||||
margin: 8px 0;
|
||||
min-width: 16px;
|
||||
|
||||
&::after {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
&[|mode='maximize'] button,
|
||||
&[|mode='minimize'] button {
|
||||
min-height: 64px;
|
||||
}
|
||||
|
||||
& button[|isPrimary]::after {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
container[|split='vertical'][|inverse] {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
container[|split='horizontal'] {
|
||||
flex-direction: row;
|
||||
cursor: ns-resize;
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
top: -2px;
|
||||
|
||||
&[|mode='minimize'] {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&[|mode='maximize'] {
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
& button {
|
||||
margin: 0 8px;
|
||||
min-width: 28px;
|
||||
height: 16px;
|
||||
|
||||
&::after {
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
}
|
||||
|
||||
&[|mode='maximize'] button,
|
||||
&[|mode='minimize'] button {
|
||||
min-width: 64px;
|
||||
}
|
||||
|
||||
& button[|isPrimary]::after {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
container[|split='horizontal'][|inverse] {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
button {
|
||||
padding: 0 !important;
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
content: '';
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 3px 4px 3px 0;
|
||||
border-color: transparent #000 transparent transparent;
|
||||
margin: -3px -2px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -35,15 +35,15 @@ export const BASE_TABLE_STYLES = css`
|
||||
|
||||
&[|size='big'] {
|
||||
& > thead > tr > th {
|
||||
height: 36px;
|
||||
height: 54px;
|
||||
|
||||
&[|min] {
|
||||
width: 32px;
|
||||
width: 46px;
|
||||
}
|
||||
}
|
||||
|
||||
& > tbody > tr > td {
|
||||
height: 32px;
|
||||
height: 46px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export const BASE_TABLE_STYLES = css`
|
||||
th {
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
padding: 8px;
|
||||
padding: 16px;
|
||||
height: 36px;
|
||||
padding-top: unset;
|
||||
padding-bottom: unset;
|
||||
@@ -114,7 +114,7 @@ export const BASE_TABLE_STYLES = css`
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
height: 28px;
|
||||
padding: 0 8px;
|
||||
padding: 0 16px;
|
||||
transition: padding ease-in-out 0.24s;
|
||||
|
||||
&[|ellipsis] {
|
||||
|
||||
@@ -26,8 +26,8 @@ interface Props {
|
||||
|
||||
const checkboxStyles = css`
|
||||
Checkbox {
|
||||
margin-left: -6px;
|
||||
margin-right: -6px;
|
||||
margin-left: -10px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ interface Props {
|
||||
|
||||
const styles = css`
|
||||
Checkbox {
|
||||
margin-left: -6px;
|
||||
margin-right: -6px;
|
||||
margin-left: -10px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const style = css`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
gap: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import styled, { css } from 'reshadow';
|
||||
|
||||
const styles = css`
|
||||
container {
|
||||
height: 30px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -59,10 +59,6 @@ export const TREE_NODE_STYLES = css`
|
||||
margin-right: 0;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
& TreeNodeName {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +95,6 @@ export const TREE_NODE_STYLES = css`
|
||||
padding-right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
TreeNodeFilter {
|
||||
@@ -147,18 +142,21 @@ export const TREE_NODE_STYLES = css`
|
||||
}
|
||||
|
||||
node TreeNodeControl[big] {
|
||||
height: 32px;
|
||||
padding: 0 8px;
|
||||
height: 46px;
|
||||
padding: 2 16px;
|
||||
|
||||
& TreeNodeSelect {
|
||||
margin: 3px;
|
||||
}
|
||||
& TreeNodeName {
|
||||
margin: 0;
|
||||
padding-right: 4px;
|
||||
padding-left: 4px;
|
||||
padding-right: 16px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
& TreeNodeIcon,
|
||||
& TreeNodeExpand {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
& TreeNodeExpand [|size='small'] {
|
||||
@@ -170,16 +168,12 @@ export const TREE_NODE_STYLES = css`
|
||||
}
|
||||
|
||||
& > * {
|
||||
margin-right: 4px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
& TreeNodeSelect {
|
||||
margin: 0;
|
||||
margin-right: 11px;
|
||||
margin-left: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
node TreeNodeControl[big] + TreeNodeNested {
|
||||
padding-left: 16px;
|
||||
padding-left: 46px;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -23,23 +23,15 @@ export * from './localization/Translate';
|
||||
export * from './localization/useTranslate';
|
||||
|
||||
export * from './ConnectionImageWithMask/ConnectionImageWithMask';
|
||||
export { default as ConnectionImageWithMaskSvgStyles } from './ConnectionImageWithMask/ConnectionImageWithMaskSvg.m.css';
|
||||
|
||||
export * from './Menu/ACTION_ICON_BUTTON_STYLES';
|
||||
export * from './Menu/Menu';
|
||||
export { default as MenuStyles } from './Menu/Menu.m.css';
|
||||
export * from './Menu/MenuBarSmallItem';
|
||||
export * from './Menu/MenuEmptyItem';
|
||||
export * from './Menu/MenuItem';
|
||||
export { default as MenuItemStyles } from './Menu/MenuItem.m.css';
|
||||
export * from './Menu/MenuItemCheckbox';
|
||||
export * from './Menu/MenuItemElement';
|
||||
export { default as MenuItemElementStyles } from './Menu/MenuItemElement.m.css';
|
||||
export * from './Menu/MenuItemRadio';
|
||||
export * from './Menu/MenuPanel';
|
||||
export { default as MenuPanelStyles } from './Menu/MenuPanel.m.css';
|
||||
export * from './Menu/MenuSeparator';
|
||||
export { default as MenuSeparatorStyles } from './Menu/MenuSeparator.m.css';
|
||||
export * from './Menu/menuPanelStyles';
|
||||
export * from './Menu/MenuStateContext';
|
||||
export * from './Menu/useMouseContextMenu';
|
||||
|
||||
@@ -164,8 +156,6 @@ export * from './ExceptionMessage';
|
||||
export * from './getComputed';
|
||||
export * from './IconButton';
|
||||
export * from './IconOrImage';
|
||||
export * from './s';
|
||||
export * from './SContext';
|
||||
export * from './StaticImage';
|
||||
export * from './TextPlaceholder';
|
||||
export * from './TimerIcon';
|
||||
@@ -187,7 +177,6 @@ export * from './useObjectRef';
|
||||
export * from './useObservableRef';
|
||||
export * from './usePermission';
|
||||
export * from './usePromiseState';
|
||||
export * from './useS';
|
||||
export * from './useStateDelay';
|
||||
export * from './useErrorDetails';
|
||||
export * from './useActivationDelay';
|
||||
|
||||
@@ -14,13 +14,13 @@ const styles = css`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
padding: 0 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
IconOrImage {
|
||||
height: 24px;
|
||||
width: 120px;
|
||||
height: 32px;
|
||||
width: 154px;
|
||||
&[|small] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export const topAppBarStyles = css`
|
||||
composes: theme-text-on-primary theme-typography--body2 from global;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
height: 48px;
|
||||
padding: 0 8px;
|
||||
background-color: #2a7cb4;
|
||||
z-index: 1;
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* 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 { type ClassValue, clsx } from '@cloudbeaver/core-utils';
|
||||
|
||||
export function s<T extends Record<string, string>>(styles: T, options?: Partial<Record<keyof T, boolean>>, ...inputs: ClassValue[]): string {
|
||||
return clsx(...Object.entries(options ?? {}).map(([key, active]) => active && styles[key]), ...inputs);
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* 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 { useContext, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { BaseStyles, ComponentStyle, Style, ThemeService } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { SContextReact } from './SContext';
|
||||
import { useExecutor } from './useExecutor';
|
||||
|
||||
type Intersect<T> = (T extends any ? (x: T) => 0 : never) extends (x: infer R) => 0
|
||||
? R extends Record<string, string>
|
||||
? BaseStyles<R>
|
||||
: never
|
||||
: never;
|
||||
|
||||
type ExtractStyles<T extends ComponentStyle[]> = Intersect<
|
||||
T extends (infer U)[]
|
||||
? U extends BaseStyles
|
||||
? U
|
||||
: U extends (theme: string) => Promise<infer A>
|
||||
? A extends BaseStyles
|
||||
? A
|
||||
: never
|
||||
: never
|
||||
: never
|
||||
>;
|
||||
|
||||
/**
|
||||
* Changes styles depending on theme
|
||||
*
|
||||
* @param componentStyles styles array
|
||||
*/
|
||||
export function useS<T extends ComponentStyle[]>(...componentStyles: [...T]): ExtractStyles<T> {
|
||||
// todo do you understand that we store ALL STYLES in each component that uses this hook?
|
||||
|
||||
const context = useContext(SContextReact);
|
||||
const stylesRef = useRef<ComponentStyle[]>([]);
|
||||
const [patch, forceUpdate] = useState(0);
|
||||
const loadedStyles = useRef<BaseStyles[]>([]);
|
||||
const themeService = useService(ThemeService);
|
||||
const [currentThemeId, setCurrentThemeId] = useState(() => themeService.currentThemeId);
|
||||
const lastThemeRef = useRef<string>(currentThemeId);
|
||||
const filteredStyles = themeService.mapStyles(componentStyles.flat(Infinity).filter(Boolean) as Style[], context);
|
||||
const trackTheme = filteredStyles.some(style => typeof style === 'function');
|
||||
|
||||
useExecutor({
|
||||
executor: themeService.onChange,
|
||||
handlers: [
|
||||
function updateThemeId(theme) {
|
||||
if (currentThemeId !== themeService.currentThemeId && trackTheme) {
|
||||
setCurrentThemeId(theme.id);
|
||||
}
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let changed = lastThemeRef.current !== currentThemeId || filteredStyles.length !== stylesRef.current.length;
|
||||
for (let i = 0; !changed && i < filteredStyles.length; i++) {
|
||||
changed = stylesRef.current[i] !== filteredStyles[i];
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
stylesRef.current = filteredStyles;
|
||||
lastThemeRef.current = currentThemeId;
|
||||
const staticStyles: BaseStyles[] = [];
|
||||
const themedStyles: Array<Promise<undefined | BaseStyles | BaseStyles[]>> = [];
|
||||
|
||||
for (const style of filteredStyles) {
|
||||
const data = typeof style === 'object' ? style : style(currentThemeId);
|
||||
|
||||
if (data instanceof Promise) {
|
||||
themedStyles.push(data);
|
||||
} else {
|
||||
staticStyles.push(data);
|
||||
}
|
||||
}
|
||||
loadedStyles.current = staticStyles.flat(Infinity);
|
||||
|
||||
if (themedStyles.length > 0) {
|
||||
Promise.all(themedStyles).then(styles => {
|
||||
loadedStyles.current = [staticStyles, styles].flat(Infinity).filter(Boolean) as BaseStyles[];
|
||||
forceUpdate(patch + 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const styles = useMemo(() => combineStyles(loadedStyles.current), [patch, loadedStyles.current]);
|
||||
|
||||
return styles as ExtractStyles<T>; // todo this method is called in each rerender
|
||||
}
|
||||
|
||||
export function combineStyles(styles: BaseStyles[]): BaseStyles {
|
||||
const combined: BaseStyles = {};
|
||||
|
||||
for (const style of styles) {
|
||||
for (const [key, value] of Object.entries(style)) {
|
||||
combined[key] = [combined[key] || '', value].join(' ').trim();
|
||||
}
|
||||
}
|
||||
|
||||
return combined;
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import { create } from 'reshadow';
|
||||
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { BaseStyles, ComponentStyle, Style, ThemeService } from '@cloudbeaver/core-theming';
|
||||
import { flat } from '@cloudbeaver/core-utils';
|
||||
|
||||
import { useExecutor } from './useExecutor';
|
||||
|
||||
@@ -27,8 +28,7 @@ export function useStyles(...componentStyles: ComponentStyle[]): Record<string,
|
||||
const themeService = useService(ThemeService);
|
||||
const [currentThemeId, setCurrentThemeId] = useState(() => themeService.currentThemeId);
|
||||
const lastThemeRef = useRef<string>(currentThemeId);
|
||||
//@ts-ignore
|
||||
const filteredStyles = componentStyles.flat(Infinity).filter(Boolean) as Style[];
|
||||
const filteredStyles = flat(componentStyles).filter(Boolean) as Style[];
|
||||
const trackTheme = filteredStyles.some(style => typeof style === 'function');
|
||||
|
||||
useExecutor({
|
||||
@@ -62,11 +62,11 @@ export function useStyles(...componentStyles: ComponentStyle[]): Record<string,
|
||||
staticStyles.push(data);
|
||||
}
|
||||
}
|
||||
loadedStyles.current = staticStyles.flat(Infinity);
|
||||
loadedStyles.current = flat(staticStyles);
|
||||
|
||||
if (themedStyles.length > 0) {
|
||||
Promise.all(themedStyles).then(styles => {
|
||||
loadedStyles.current = [staticStyles, styles].flat(Infinity).filter(Boolean) as BaseStyles[];
|
||||
loadedStyles.current = flat([staticStyles, flat(styles)]).filter(Boolean) as BaseStyles[];
|
||||
forceUpdate(patch + 1);
|
||||
});
|
||||
}
|
||||
@@ -78,6 +78,5 @@ export function useStyles(...componentStyles: ComponentStyle[]): Record<string,
|
||||
}
|
||||
|
||||
export function joinStyles(...styles: ComponentStyle[]): ComponentStyle {
|
||||
//@ts-ignore
|
||||
return styles.flat(Infinity) as ComponentStyle;
|
||||
return styles.flat(2);
|
||||
}
|
||||
|
||||
@@ -55,13 +55,14 @@ module.exports = {
|
||||
exclude: ['transform-async-to-generator', 'transform-regenerator'],
|
||||
},
|
||||
],
|
||||
['@babel/preset-typescript', { isTSX: true, allExtensions: true, onlyRemoveTypeImports: true }],
|
||||
[
|
||||
'@babel/preset-react',
|
||||
{
|
||||
runtime: 'automatic',
|
||||
'runtime': 'automatic',
|
||||
// "importSource": "preact-jsx-runtime"
|
||||
},
|
||||
],
|
||||
['@babel/preset-typescript', { isTSX: true, allExtensions: true, onlyRemoveTypeImports: true }],
|
||||
],
|
||||
plugins: [
|
||||
'babel-plugin-transform-typescript-metadata',
|
||||
@@ -72,8 +73,8 @@ module.exports = {
|
||||
['@babel/plugin-proposal-class-properties'],
|
||||
['@babel/plugin-proposal-object-rest-spread', { useBuiltIns: true }],
|
||||
[testingAttributes, {}],
|
||||
[require('@reshadow/babel'), {}],
|
||||
/*devMode &&*/ [
|
||||
[require('@reshadow/babel'), { }],
|
||||
/*devMode &&*/[
|
||||
'babel-plugin-module-resolver',
|
||||
{
|
||||
alias: {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
const { resolve } = require('path');
|
||||
const { resolve, join } = require('path');
|
||||
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
||||
const ModuleDependencyWarning = require('webpack/lib/ModuleDependencyWarning');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
const threadLoader = require('thread-loader');
|
||||
|
||||
const excludedFromVendor = require('./excludedFromVendor.js');
|
||||
// const ESLintPlugin = require('eslint-webpack-plugin');
|
||||
@@ -33,8 +32,6 @@ const nodeModules = [
|
||||
resolve('../../node_modules/@cloudbeaver/core-cli/node_modules'), // core-cli
|
||||
];
|
||||
|
||||
threadLoader.warmup({}, ['babel-loader', 'css-loader', 'sass-loader', 'postcss-loader', 'style-loader', 'json5-loader', MiniCssExtractPlugin.loader]);
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
process.env.NODE_ENV = argv.mode;
|
||||
const devTool = 'source-map' in env && 'source-map';
|
||||
@@ -52,7 +49,7 @@ module.exports = (env, argv) => {
|
||||
return loaders;
|
||||
}
|
||||
|
||||
function generateStyleLoaders(options = { hasModule: false, disable: false, pure: false }) {
|
||||
function generateStyleLoaders(options = { hasModule: false, disable: false }) {
|
||||
const moduleScope = options.hasModule ? 'local' : 'global';
|
||||
let modules = {
|
||||
mode: moduleScope,
|
||||
@@ -63,11 +60,7 @@ module.exports = (env, argv) => {
|
||||
modules = false;
|
||||
}
|
||||
|
||||
const postCssPlugins = [require('postcss-preset-env')({ stage: 0 })];
|
||||
|
||||
if (!options.pure) {
|
||||
postCssPlugins.push(require('@reshadow/postcss')({ scopeBehaviour: moduleScope }));
|
||||
}
|
||||
const postCssPlugins = [require('postcss-preset-env')({ stage: 0 }), require('@reshadow/postcss')({ scopeBehaviour: moduleScope })];
|
||||
|
||||
return [
|
||||
...getBaseStyleLoaders(),
|
||||
@@ -75,27 +68,26 @@ module.exports = (env, argv) => {
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
modules: modules,
|
||||
sourceMap: devMode,
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
sourceMap: devMode,
|
||||
postcssOptions: {
|
||||
plugins: postCssPlugins,
|
||||
sourceMap: devMode,
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: devMode,
|
||||
sourceMap: true,
|
||||
sassOptions: {
|
||||
implementation: require('sass'),
|
||||
includePaths: nodeModules,
|
||||
quietDeps: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -105,8 +97,7 @@ module.exports = (env, argv) => {
|
||||
var babelLoader = {
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
root: __dirname,
|
||||
cacheDirectory: true,
|
||||
configFile: join(__dirname, 'babel.config.js'),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -208,16 +199,11 @@ module.exports = (env, argv) => {
|
||||
},
|
||||
{
|
||||
test: /\.(css|s[ac]ss)$/,
|
||||
exclude: /node_modules/,
|
||||
oneOf: [
|
||||
{
|
||||
test: /\.module\.(css|s[ac]ss)$/,
|
||||
use: generateStyleLoaders({ hasModule: true }),
|
||||
},
|
||||
{
|
||||
test: /\.m\.(css|s[ac]ss)$/,
|
||||
use: generateStyleLoaders({ hasModule: true, pure: true }),
|
||||
},
|
||||
{
|
||||
include: /node_modules/,
|
||||
use: [...getBaseStyleLoaders(), 'css-loader', 'sass-loader'],
|
||||
|
||||
@@ -43,8 +43,7 @@ module.exports = (env, argv) => {
|
||||
sso,
|
||||
},
|
||||
optimization: {
|
||||
minimize: false,
|
||||
moduleIds: 'named',
|
||||
minimize: true,
|
||||
},
|
||||
devServer: {
|
||||
allowedHosts: 'all',
|
||||
@@ -72,7 +71,10 @@ module.exports = (env, argv) => {
|
||||
httpProxy.createProxyServer({ target:`http://127.0.0.1:${port}` }).listen(8080);
|
||||
},
|
||||
},
|
||||
devtool: 'source-map',
|
||||
devtool: 'eval-source-map',
|
||||
// optimization: {
|
||||
// moduleIds: 'named',
|
||||
// },
|
||||
plugins: [
|
||||
new CopyWebpackPlugin({
|
||||
patterns: getAssets(package, ''),
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
"files": [
|
||||
"/package.json",
|
||||
"/tsconfig.json",
|
||||
"/global.d.ts",
|
||||
"/src",
|
||||
"/configs",
|
||||
"/bin",
|
||||
@@ -72,6 +71,8 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/jest": "~29.x.x",
|
||||
"@types/react": "~18.x.x",
|
||||
"@types/react-dom": "~18.x.x",
|
||||
"babel-jest": "~29.x.x",
|
||||
"jest": "~29.x.x",
|
||||
"jest-environment-jsdom": "~29.x.x"
|
||||
|
||||
@@ -91,7 +91,7 @@ export const commonDialogBaseStyle = [
|
||||
dialog-body,
|
||||
footer {
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
padding: 24px;
|
||||
|
||||
&[|no-padding] {
|
||||
padding: 0px;
|
||||
|
||||
@@ -26,7 +26,7 @@ export const dialogStyles = css`
|
||||
inner-box {
|
||||
display: flex;
|
||||
margin: auto;
|
||||
padding: 16px;
|
||||
padding: 24px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
|
||||
+1
-1
@@ -29,6 +29,6 @@ export const styles = css`
|
||||
CommonDialogFooter {
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 16px;
|
||||
gap: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -7,10 +7,8 @@
|
||||
*/
|
||||
import type { ClassCollection } from './themeUtils';
|
||||
|
||||
export type BaseStyles<T extends Record<string, string> = Record<string, string>> = ClassCollection<T>;
|
||||
export type ThemeSelector<T extends Record<string, string> = Record<string, string>> = (
|
||||
theme: string,
|
||||
) => Promise<undefined | BaseStyles<T> | BaseStyles<T>[]>;
|
||||
export type Style<T extends Record<string, string> = Record<string, string>> = BaseStyles<T> | ThemeSelector<T>;
|
||||
export type DynamicStyle<T extends Record<string, string> = Record<string, string>> = Style<T> | boolean | undefined;
|
||||
export type ComponentStyle<T extends Record<string, string> = Record<string, string>> = DynamicStyle<T> | ComponentStyle<T>[];
|
||||
export type BaseStyles = ClassCollection;
|
||||
export type ThemeSelector = (theme: string) => Promise<undefined | BaseStyles | BaseStyles[]>;
|
||||
export type Style = BaseStyles | ThemeSelector;
|
||||
export type DynamicStyle = Style | boolean | undefined;
|
||||
export type ComponentStyle = DynamicStyle | DynamicStyle[] | DynamicStyle[][];
|
||||
|
||||
@@ -13,7 +13,6 @@ import { ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor';
|
||||
import { ServerConfigResource } from '@cloudbeaver/core-root';
|
||||
import { SettingsService } from '@cloudbeaver/core-settings';
|
||||
|
||||
import type { Style } from './ComponentStyle';
|
||||
import './styles/main/base.pure.css';
|
||||
import './styles/main/color.pure.scss';
|
||||
import './styles/main/elevation.pure.scss';
|
||||
@@ -38,11 +37,6 @@ interface ISettings {
|
||||
currentThemeId: string;
|
||||
}
|
||||
|
||||
export interface IStyleRegistry {
|
||||
mode: 'replace' | 'append';
|
||||
styles: Style[];
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ThemeService extends Bootstrap {
|
||||
get themes(): ITheme[] {
|
||||
@@ -73,7 +67,6 @@ export class ThemeService extends Bootstrap {
|
||||
|
||||
readonly onChange: ISyncExecutor<ITheme>;
|
||||
|
||||
private readonly stylesRegistry: Map<Style, IStyleRegistry[]> = new Map();
|
||||
private readonly themeMap: Map<string, ITheme> = new Map();
|
||||
private readonly settings: ISettings;
|
||||
|
||||
@@ -102,37 +95,6 @@ export class ThemeService extends Bootstrap {
|
||||
this.loadAllThemes();
|
||||
}
|
||||
|
||||
addStyleRegistry<T extends Record<string, string>>(style: Style<T>, mode: 'replace' | 'append', styles: Style<T>[]): void {
|
||||
if (!this.stylesRegistry.has(style)) {
|
||||
this.stylesRegistry.set(style, []);
|
||||
}
|
||||
|
||||
this.stylesRegistry.get(style)!.push({ mode, styles });
|
||||
}
|
||||
|
||||
mapStyles<T extends Record<string, string>>(styles: Style<T>[], context?: Map<Style, IStyleRegistry[]>): Style<T>[] {
|
||||
return styles
|
||||
.map(style => {
|
||||
const registries = this.stylesRegistry.get(style) ?? context?.get(style);
|
||||
|
||||
if (!registries) {
|
||||
return [style];
|
||||
}
|
||||
|
||||
return registries.reduce(
|
||||
(acc, registry) => {
|
||||
if (registry.mode === 'replace') {
|
||||
acc = acc.filter(s => s !== style);
|
||||
}
|
||||
|
||||
return [...acc, ...this.mapStyles(registry.styles, context)] as Style<T>[];
|
||||
},
|
||||
[style] as Style<T>[],
|
||||
);
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
await this.serverConfigResource.load();
|
||||
this.setCurrentThemeId(this.defaultThemeId); // set default app theme
|
||||
|
||||
@@ -8,12 +8,10 @@
|
||||
|
||||
@mixin button-core-styles() {
|
||||
$mdc-button-disabled-container-fill-color: #cccccc;
|
||||
$mdc-button-density-scale: -3;
|
||||
$mdc-density-default-scale: -3;
|
||||
$mdc-button-density-scale: -2;
|
||||
$query: mdc-feature-all();
|
||||
@include mdc-button-core-styles;
|
||||
@include mdc-button-theme-baseline;
|
||||
@include mdc-button-density(-3);
|
||||
|
||||
.theme-button {
|
||||
@extend .mdc-button;
|
||||
@@ -86,4 +84,4 @@
|
||||
@include mdc-states($color: on-secondary, $query: $query, $ripple-target: $mdc-button-ripple-target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,11 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
@import '@material/checkbox/mixins';
|
||||
@import "@material/checkbox/mixins";
|
||||
$mdc-checkbox-icon-size: 16px;
|
||||
$mdc-checkbox-density-scale: -3;
|
||||
$mdc-density-default-scale: -3;
|
||||
|
||||
@mixin checkbox-core-styles($query: mdc-feature-all()) {
|
||||
@include mdc-checkbox-core-styles($query);
|
||||
@include mdc-checkbox-density(-3);
|
||||
|
||||
.theme-checkbox {
|
||||
@extend .mdc-checkbox;
|
||||
@@ -51,14 +48,24 @@ $mdc-density-default-scale: -3;
|
||||
$mdc-checkbox-mark-color: $mdc-theme-on-primary;
|
||||
@include mdc-checkbox-ink-color($mdc-checkbox-mark-color, $query);
|
||||
@include mdc-checkbox-disabled-ink-color(rgba($mdc-checkbox-mark-color, 0.5), $query);
|
||||
@include mdc-checkbox-container-colors($mdc-checkbox-border-color, transparent, $mdc-theme-primary, $mdc-theme-primary);
|
||||
@include mdc-checkbox-container-colors(
|
||||
$mdc-checkbox-border-color,
|
||||
transparent,
|
||||
$mdc-theme-primary,
|
||||
$mdc-theme-primary
|
||||
);
|
||||
}
|
||||
|
||||
.theme-checkbox_surface {
|
||||
$mdc-checkbox-mark-color: $mdc-theme-primary;
|
||||
@include mdc-checkbox-ink-color($mdc-checkbox-mark-color, $query);
|
||||
@include mdc-checkbox-disabled-ink-color(rgba($mdc-checkbox-mark-color, 0.5), $query);
|
||||
@include mdc-checkbox-container-colors($mdc-theme-surface, transparent, $mdc-theme-surface, $mdc-theme-surface);
|
||||
@include mdc-checkbox-container-colors(
|
||||
$mdc-theme-surface,
|
||||
transparent,
|
||||
$mdc-theme-surface,
|
||||
$mdc-theme-surface
|
||||
);
|
||||
}
|
||||
|
||||
.theme-checkbox_small {
|
||||
|
||||
@@ -6,15 +6,10 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
@import '@material/radio/mixins';
|
||||
$mdc-radio-density-scale: -3;
|
||||
$mdc-density-default-scale: -3;
|
||||
$mdc-radio-touch-area: 32px !default;
|
||||
$mdc-radio-ui-size: 16px !default;
|
||||
@import "@material/radio/mixins";
|
||||
|
||||
@mixin radio-core-styles($query: mdc-feature-all()) {
|
||||
@include mdc-radio-core-styles;
|
||||
@include mdc-radio-density(-3);
|
||||
|
||||
.theme-radio {
|
||||
@extend .mdc-radio;
|
||||
@@ -69,4 +64,4 @@ $mdc-radio-ui-size: 16px !default;
|
||||
.theme-radio_no-ripple {
|
||||
@include mdc-radio-ripple-size(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,10 @@
|
||||
*/
|
||||
|
||||
@import '@material/switch/mixins';
|
||||
$mdc-switch-density-scale: -3;
|
||||
$mdc-density-default-scale: -3;
|
||||
|
||||
@mixin switch-core-styles($query: mdc-feature-all()) {
|
||||
@include mdc-switch-core-styles($query);
|
||||
|
||||
|
||||
.theme-switch {
|
||||
@extend .mdc-switch;
|
||||
}
|
||||
@@ -43,8 +41,7 @@ $mdc-density-default-scale: -3;
|
||||
@include switch-core-styles-dense($query);
|
||||
}
|
||||
|
||||
.theme-switch_dense .theme-switch_primary,
|
||||
.theme-switch_primary {
|
||||
.theme-switch_dense .theme-switch_primary, .theme-switch_primary {
|
||||
$mdc-switch-baseline-theme-color: primary;
|
||||
|
||||
@include mdc-switch-toggled-on-track-color($mdc-switch-baseline-theme-color, $query);
|
||||
@@ -74,7 +71,8 @@ $mdc-density-default-scale: -3;
|
||||
|
||||
$mdc-switch-thumb-offset: 2px !global;
|
||||
|
||||
$mdc-switch-thumb-active-margin: $mdc-switch-track-width - $mdc-switch-thumb-diameter + $mdc-switch-thumb-offset * 2 !global;
|
||||
$mdc-switch-thumb-active-margin:
|
||||
$mdc-switch-track-width - $mdc-switch-thumb-diameter + $mdc-switch-thumb-offset * 2 !global;
|
||||
|
||||
@include mdc-switch-core-styles($query);
|
||||
}
|
||||
}
|
||||
@@ -20,5 +20,5 @@
|
||||
@include form-controls;
|
||||
@include link;
|
||||
@include placeholder;
|
||||
@include delimiter($mdc-theme-background);
|
||||
@include delimiter($mdc-theme-secondary);
|
||||
@include branding-overflow;
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
@include form-controls;
|
||||
@include link;
|
||||
@include placeholder;
|
||||
@include delimiter($mdc-theme-background);
|
||||
@include delimiter($mdc-theme-secondary);
|
||||
@include branding-overflow;
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
export type ClassCollection<T extends Record<string, string> = Record<string, string>> = {
|
||||
[Key in keyof T]: string;
|
||||
};
|
||||
export interface ClassCollection {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export class Composes {
|
||||
composes: ClassCollection;
|
||||
|
||||
@@ -8,8 +8,20 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { ButtonHTMLAttributes, forwardRef, useRef, useState } from 'react';
|
||||
import type { MenuInitialState } from 'reakit/Menu';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import { getComputed, IMenuState, IMouseContextMenu, Menu, useAutoLoad, useObjectRef, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import {
|
||||
getComputed,
|
||||
IMenuState,
|
||||
IMouseContextMenu,
|
||||
Menu,
|
||||
menuPanelStyles,
|
||||
useAutoLoad,
|
||||
useObjectRef,
|
||||
useStyles,
|
||||
useTranslate,
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
import { IMenuData, MenuActionItem } from '@cloudbeaver/core-view';
|
||||
|
||||
import { MenuItemRenderer } from './MenuItemRenderer';
|
||||
@@ -21,9 +33,10 @@ interface IMenuProps extends React.PropsWithChildren {
|
||||
|
||||
type ContextMenuRenderingChildren = (props: IMenuProps) => React.ReactNode | React.ReactElement;
|
||||
|
||||
interface IContextMenuProps extends Omit<ButtonHTMLAttributes<any>, 'children'> {
|
||||
interface IContextMenuProps extends Omit<ButtonHTMLAttributes<any>, 'style' | 'children'> {
|
||||
mouseContextMenu?: IMouseContextMenu;
|
||||
menu: IMenuData;
|
||||
style?: ComponentStyle;
|
||||
disclosure?: boolean;
|
||||
placement?: MenuInitialState['placement'];
|
||||
modal?: boolean;
|
||||
@@ -35,7 +48,7 @@ interface IContextMenuProps extends Omit<ButtonHTMLAttributes<any>, 'children'>
|
||||
|
||||
export const ContextMenu = observer<IContextMenuProps, HTMLButtonElement>(
|
||||
forwardRef(function ContextMenu(
|
||||
{ mouseContextMenu, menu: menuData, disclosure, children, placement, visible, onVisibleSwitch, modal, rtl, ...props },
|
||||
{ mouseContextMenu, menu: menuData, disclosure, children, style, placement, visible, onVisibleSwitch, modal, rtl, ...props },
|
||||
ref,
|
||||
) {
|
||||
const translate = useTranslate();
|
||||
@@ -48,6 +61,7 @@ export const ContextMenu = observer<IContextMenuProps, HTMLButtonElement>(
|
||||
const lazy = getComputed(() => !menuData.available || hidden);
|
||||
|
||||
const menu = useRef<IMenuState>();
|
||||
const styles = useStyles(menuPanelStyles, style);
|
||||
|
||||
useAutoLoad(menuData.loaders, !lazy, menuVisible);
|
||||
|
||||
@@ -78,7 +92,7 @@ export const ContextMenu = observer<IContextMenuProps, HTMLButtonElement>(
|
||||
|
||||
const renderingChildren: React.ReactNode = typeof children === 'function' ? children({ loading, disabled }) : children;
|
||||
|
||||
return (
|
||||
return styled(styles)(
|
||||
<Menu
|
||||
ref={ref}
|
||||
label={translate(menuData.menu.label)}
|
||||
@@ -87,7 +101,16 @@ export const ContextMenu = observer<IContextMenuProps, HTMLButtonElement>(
|
||||
menuData.items.map(
|
||||
item =>
|
||||
menu.current && (
|
||||
<MenuItemRenderer key={item.id} item={item} menuData={menuData} rtl={rtl} modal={modal} onItemClose={handlers.handleItemClose} />
|
||||
<MenuItemRenderer
|
||||
key={item.id}
|
||||
item={item}
|
||||
menuData={menuData}
|
||||
rtl={rtl}
|
||||
menu={menu.current}
|
||||
modal={modal}
|
||||
style={style}
|
||||
onItemClose={handlers.handleItemClose}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -99,12 +122,13 @@ export const ContextMenu = observer<IContextMenuProps, HTMLButtonElement>(
|
||||
placement={placement}
|
||||
disabled={disabled}
|
||||
disclosure={disclosure}
|
||||
style={style}
|
||||
getHasBindings={handlers.hasBindings}
|
||||
onVisibleSwitch={handlers.handleVisibleSwitch}
|
||||
{...props}
|
||||
>
|
||||
{renderingChildren}
|
||||
</Menu>
|
||||
</Menu>,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -5,10 +5,15 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import type { MenuStateReturn } from 'reakit/ts';
|
||||
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
import type { IMenuData, IMenuItem } from '@cloudbeaver/core-view';
|
||||
|
||||
export interface IContextMenuItemProps {
|
||||
item: IMenuItem;
|
||||
menu: MenuStateReturn;
|
||||
menuData: IMenuData;
|
||||
style?: ComponentStyle;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React from 'react';
|
||||
import { MenuItem, MenuItemCheckbox, MenuItemRadio } from 'reakit/Menu';
|
||||
import styled, { use } from 'reshadow';
|
||||
|
||||
import { Checkbox, MenuItem, MenuItemCheckbox, MenuItemElement, MenuItemRadio, Radio } from '@cloudbeaver/core-blocks';
|
||||
import { Checkbox, MenuItemElement, menuPanelStyles, Radio, useStyles } from '@cloudbeaver/core-blocks';
|
||||
import type { IMenuActionItem } from '@cloudbeaver/core-view';
|
||||
|
||||
import type { IContextMenuItemProps } from './IContextMenuItemProps';
|
||||
@@ -17,7 +19,8 @@ interface IMenuActionElementProps extends IContextMenuItemProps {
|
||||
item: IMenuActionItem;
|
||||
}
|
||||
|
||||
export const MenuActionElement = observer<IMenuActionElementProps>(function MenuActionElement({ item, onClick }) {
|
||||
export const MenuActionElement = observer<IMenuActionElementProps>(function MenuActionElement({ item, menu, style, onClick }) {
|
||||
const styles = useStyles(menuPanelStyles, style);
|
||||
const actionInfo = item.action.actionInfo;
|
||||
const loading = item.action.isLoading();
|
||||
|
||||
@@ -28,9 +31,10 @@ export const MenuActionElement = observer<IMenuActionElementProps>(function Menu
|
||||
|
||||
if (actionInfo.type === 'select') {
|
||||
const checked = item.action.isChecked();
|
||||
return (
|
||||
return styled(styles)(
|
||||
<MenuItemRadio
|
||||
hidden={item.hidden}
|
||||
{...menu}
|
||||
{...use({ hidden: item.hidden })}
|
||||
id={item.id}
|
||||
aria-label={actionInfo.label}
|
||||
disabled={item.disabled}
|
||||
@@ -44,16 +48,18 @@ export const MenuActionElement = observer<IMenuActionElementProps>(function Menu
|
||||
icon={<Radio checked={checked} mod={['primary', 'menu']} ripple={false} />}
|
||||
tooltip={actionInfo.tooltip}
|
||||
loading={loading}
|
||||
style={style}
|
||||
/>
|
||||
</MenuItemRadio>
|
||||
</MenuItemRadio>,
|
||||
);
|
||||
}
|
||||
|
||||
if (actionInfo.type === 'checkbox') {
|
||||
const checked = item.action.isChecked();
|
||||
return (
|
||||
return styled(styles)(
|
||||
<MenuItemCheckbox
|
||||
hidden={item.hidden}
|
||||
{...menu}
|
||||
{...use({ hidden: item.hidden })}
|
||||
id={item.id}
|
||||
aria-label={actionInfo.label}
|
||||
disabled={item.disabled}
|
||||
@@ -64,23 +70,25 @@ export const MenuActionElement = observer<IMenuActionElementProps>(function Menu
|
||||
>
|
||||
<MenuItemElement
|
||||
label={actionInfo.label}
|
||||
icon={<Checkbox checked={checked} mod={['primary', 'small']} ripple={false} />}
|
||||
icon={<Checkbox checked={checked} mod={['primary', 'small']} style={style} ripple={false} />}
|
||||
tooltip={actionInfo.tooltip}
|
||||
loading={loading}
|
||||
style={style}
|
||||
/>
|
||||
</MenuItemCheckbox>
|
||||
</MenuItemCheckbox>,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuItem hidden={item.hidden} id={item.id} aria-label={actionInfo.label} disabled={item.disabled} onClick={handleClick}>
|
||||
return styled(styles)(
|
||||
<MenuItem {...menu} {...use({ hidden: item.hidden })} id={item.id} aria-label={actionInfo.label} disabled={item.disabled} onClick={handleClick}>
|
||||
<MenuItemElement
|
||||
label={actionInfo.label}
|
||||
icon={actionInfo.icon}
|
||||
binding={item.action.binding?.binding.label}
|
||||
tooltip={actionInfo.tooltip}
|
||||
loading={loading}
|
||||
style={style}
|
||||
/>
|
||||
</MenuItem>
|
||||
</MenuItem>,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 { css } from 'reshadow';
|
||||
|
||||
export const MENU_BAR_DEFAULT_STYLES = css`
|
||||
menu-bar {
|
||||
composes: theme-background-secondary theme-text-on-secondary theme-border-color-background theme-typography--body2 from global;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
menu-bar-item {
|
||||
composes: theme-ripple from global;
|
||||
padding: 0 16px;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
|
||||
& menu-bar-item-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&[use|hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& menu-bar-item-icon {
|
||||
display: flex;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
& Loader,
|
||||
& IconOrImage {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
& menu-bar-item-label {
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
& menu-bar-item-icon + menu-bar-item-label {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
& menu-bar-item-mark {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {
|
||||
composes: theme-border-color-background from global;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
border: 1px solid !important;
|
||||
|
||||
&:first-child,
|
||||
&:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 { css } from 'reshadow';
|
||||
|
||||
export const MENU_BAR_SMALL_STYLES = css`
|
||||
menu-bar-item {
|
||||
padding: 0 12px;
|
||||
|
||||
& menu-bar-item-icon {
|
||||
display: flex;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
& menu-bar-item-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
& Loader,
|
||||
& IconOrImage {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
menu-bar {
|
||||
height: 32px;
|
||||
}
|
||||
`;
|
||||
@@ -1,19 +0,0 @@
|
||||
.menuBar {
|
||||
height: 32px;
|
||||
display: flex;
|
||||
|
||||
& > .menuSeparator {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
border: 1px solid !important;
|
||||
|
||||
&:first-child,
|
||||
&:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.floating.menuBar {
|
||||
height: 28px;
|
||||
}
|
||||
@@ -7,9 +7,11 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { forwardRef, useCallback } from 'react';
|
||||
import type { MenuInitialState } from 'reakit';
|
||||
import { MenuInitialState, MenuSeparator } from 'reakit';
|
||||
import styled, { use } from 'reshadow';
|
||||
|
||||
import { getComputed, MenuSeparator, MenuSeparatorStyles, s, SContext, StyleRegistry, useAutoLoad, useS } from '@cloudbeaver/core-blocks';
|
||||
import { getComputed, Loader, useAutoLoad, useStyles } from '@cloudbeaver/core-blocks';
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
import {
|
||||
DATA_CONTEXT_MENU_NESTED,
|
||||
DATA_CONTEXT_SUBMENU_ITEM,
|
||||
@@ -24,32 +26,22 @@ import {
|
||||
} from '@cloudbeaver/core-view';
|
||||
|
||||
import { ContextMenu } from '../ContextMenu';
|
||||
import style from './MenuBar.m.css';
|
||||
import { MenuBarItem } from './MenuBarItem';
|
||||
|
||||
interface INestedMenuSettings extends MenuInitialState {
|
||||
onVisibleSwitch?: (visible: boolean) => void;
|
||||
}
|
||||
|
||||
interface IMenuBarProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
interface IMenuBarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style'> {
|
||||
menu: IMenuData;
|
||||
nestedMenuSettings?: INestedMenuSettings;
|
||||
style?: ComponentStyle;
|
||||
rtl?: boolean;
|
||||
}
|
||||
|
||||
const registry: StyleRegistry = [
|
||||
[
|
||||
MenuSeparatorStyles,
|
||||
{
|
||||
mode: 'append',
|
||||
styles: [style],
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
export const MenuBar = observer<IMenuBarProps, HTMLDivElement>(
|
||||
forwardRef(function MenuBar({ menu, nestedMenuSettings, rtl, className, ...props }, ref) {
|
||||
const styles = useS(style);
|
||||
forwardRef(function MenuBar({ menu, nestedMenuSettings, style, rtl, ...props }, ref) {
|
||||
const styles = useStyles(style);
|
||||
const items = menu.items;
|
||||
useAutoLoad(menu.loaders);
|
||||
|
||||
@@ -57,14 +49,12 @@ export const MenuBar = observer<IMenuBarProps, HTMLDivElement>(
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SContext registry={registry}>
|
||||
<div ref={ref} className={s(styles, { menuBar: true }, className)} {...props}>
|
||||
{items.map(item => (
|
||||
<MenuBarElement key={item.id} item={item} menuData={menu} nestedMenuSettings={nestedMenuSettings} rtl={rtl} />
|
||||
))}
|
||||
</div>
|
||||
</SContext>
|
||||
return styled(styles)(
|
||||
<menu-bar ref={ref} as="div" {...props}>
|
||||
{items.map(item => (
|
||||
<MenuBarElement key={item.id} item={item} menuData={menu} nestedMenuSettings={nestedMenuSettings} rtl={rtl} style={style} />
|
||||
))}
|
||||
</menu-bar>,
|
||||
);
|
||||
}),
|
||||
);
|
||||
@@ -75,9 +65,12 @@ interface IMenuBarElementProps {
|
||||
nestedMenuSettings?: INestedMenuSettings;
|
||||
className?: string;
|
||||
rtl?: boolean;
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
const MenuBarElement = observer<IMenuBarElementProps>(function MenuBarElement({ item, menuData, nestedMenuSettings, className, rtl }) {
|
||||
const MenuBarElement = observer<IMenuBarElementProps>(function MenuBarElement({ item, menuData, nestedMenuSettings, className, rtl, style }) {
|
||||
const styles = useStyles(style);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
item.events?.onSelect?.();
|
||||
}, [item]);
|
||||
@@ -87,30 +80,33 @@ const MenuBarElement = observer<IMenuBarElementProps>(function MenuBarElement({
|
||||
}
|
||||
|
||||
if (item instanceof MenuSubMenuItem) {
|
||||
return <SubMenuItem item={item} menuData={menuData} className={className} rtl={rtl} nestedMenuSettings={nestedMenuSettings} />;
|
||||
return styled(styles)(
|
||||
<SubMenuItem item={item} menuData={menuData} style={style} className={className} rtl={rtl} nestedMenuSettings={nestedMenuSettings} />,
|
||||
);
|
||||
}
|
||||
|
||||
if (item instanceof MenuSeparatorItem) {
|
||||
return <MenuSeparator className={className} />;
|
||||
return styled(styles)(<MenuSeparator className={className} />);
|
||||
}
|
||||
|
||||
if (item instanceof MenuActionItem) {
|
||||
return <MenuBarAction item={item} className={className} onClick={onClick} />;
|
||||
return styled(styles)(<MenuBarAction item={item} style={style} className={className} onClick={onClick} />);
|
||||
}
|
||||
|
||||
if (item instanceof MenuBaseItem) {
|
||||
return (
|
||||
return styled(styles)(
|
||||
<MenuBarItem
|
||||
id={item.id}
|
||||
hidden={item.hidden}
|
||||
aria-label={item.label}
|
||||
label={item.label}
|
||||
icon={item.icon}
|
||||
title={item.tooltip}
|
||||
disabled={item.disabled}
|
||||
style={style}
|
||||
className={className}
|
||||
onClick={onClick}
|
||||
/>
|
||||
{...use({ hidden: item.hidden })}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -119,11 +115,14 @@ const MenuBarElement = observer<IMenuBarElementProps>(function MenuBarElement({
|
||||
|
||||
interface IMenuBarActionProps {
|
||||
item: IMenuActionItem;
|
||||
style?: ComponentStyle;
|
||||
className?: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const MenuBarAction = observer<IMenuBarActionProps>(function MenuBarAction({ item, className, onClick }) {
|
||||
const MenuBarAction = observer<IMenuBarActionProps>(function MenuBarAction({ item, style, className, onClick }) {
|
||||
const styles = useStyles(style);
|
||||
|
||||
const actionInfo = item.action.actionInfo;
|
||||
const loading = item.action.isLoading();
|
||||
|
||||
@@ -135,10 +134,9 @@ const MenuBarAction = observer<IMenuBarActionProps>(function MenuBarAction({ ite
|
||||
item.action.activate();
|
||||
}
|
||||
|
||||
return (
|
||||
return styled(styles)(
|
||||
<MenuBarItem
|
||||
id={item.id}
|
||||
hidden={item.hidden}
|
||||
aria-label={actionInfo.label}
|
||||
label={actionInfo.label}
|
||||
displayLabel={displayLabel}
|
||||
@@ -146,9 +144,11 @@ const MenuBarAction = observer<IMenuBarActionProps>(function MenuBarAction({ ite
|
||||
title={actionInfo.tooltip}
|
||||
disabled={item.disabled}
|
||||
loading={loading}
|
||||
style={style}
|
||||
className={className}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
{...use({ hidden: item.hidden })}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -158,9 +158,11 @@ interface ISubMenuItemProps {
|
||||
nestedMenuSettings?: INestedMenuSettings;
|
||||
className?: string;
|
||||
rtl?: boolean;
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
const SubMenuItem = observer<ISubMenuItemProps>(function SubmenuItem({ item, menuData, nestedMenuSettings, className, rtl }) {
|
||||
const SubMenuItem = observer<ISubMenuItemProps>(function SubmenuItem({ item, menuData, nestedMenuSettings, className, rtl, style }) {
|
||||
const styles = useStyles(style);
|
||||
const subMenuData = useMenu({ menu: item.menu, context: menuData.context });
|
||||
|
||||
subMenuData.context.set(DATA_CONTEXT_MENU_NESTED, true);
|
||||
@@ -187,22 +189,31 @@ const SubMenuItem = observer<ISubMenuItemProps>(function SubmenuItem({ item, men
|
||||
const tooltip = info?.tooltip ?? item.tooltip ?? item.menu.tooltip;
|
||||
const panelAvailable = subMenuData.itemCreators.length > 0;
|
||||
|
||||
return (
|
||||
<ContextMenu menu={subMenuData} className={className} rtl={rtl} disclosure {...nestedMenuSettings}>
|
||||
return styled(styles)(
|
||||
<ContextMenu menu={subMenuData} style={style} className={className} rtl={rtl} disclosure {...nestedMenuSettings}>
|
||||
{({ loading, disabled }) => (
|
||||
<MenuBarItem
|
||||
id={item.id}
|
||||
hidden={item.hidden}
|
||||
aria-label={item.menu.label}
|
||||
label={label}
|
||||
displayLabel={displayLabel}
|
||||
icon={IconComponent ? <IconComponent item={item} {...extraProps} /> : icon}
|
||||
icon={
|
||||
IconComponent
|
||||
? styled(styles)(
|
||||
<Loader suspense small fullSize>
|
||||
<IconComponent item={item} style={style} {...extraProps} />
|
||||
</Loader>,
|
||||
)
|
||||
: icon
|
||||
}
|
||||
title={tooltip}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
style={style}
|
||||
displaySubmenuMark={panelAvailable}
|
||||
{...use({ hidden: item.hidden })}
|
||||
/>
|
||||
)}
|
||||
</ContextMenu>
|
||||
</ContextMenu>,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
.menuBarItem {
|
||||
composes: theme-ripple from global;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& .menuBarItemBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
& .menuBarItemIcon {
|
||||
display: flex;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& .iconOrImage {
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
& .loader,
|
||||
& .iconOrImage {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
& .menuBarItemLabel {
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
& .menuBarItemIcon + .menuBarItemLabel {
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
& .menuBarItemMark {
|
||||
display: none;
|
||||
|
||||
& .icon {
|
||||
width: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.floating {
|
||||
& .menuBarItem {
|
||||
padding: 4px;
|
||||
margin: 2px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
border-radius: var(--theme-form-element-radius);
|
||||
}
|
||||
& .menuBarItemIcon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
@@ -7,62 +7,49 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { ButtonHTMLAttributes, forwardRef } from 'react';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import { Icon, IconOrImage, Loader, s, useS, useStateDelay, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import style from './MenuBarItem.m.css';
|
||||
import { Icon, IconOrImage, Loader, useStateDelay, useStyles, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
interface Props extends Omit<React.DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, 'style'> {
|
||||
label?: string;
|
||||
/** @deprecated must be refactored (#1)*/
|
||||
displayLabel?: boolean;
|
||||
loading?: boolean;
|
||||
hidden?: boolean;
|
||||
icon?: string | React.ReactNode;
|
||||
displaySubmenuMark?: boolean;
|
||||
viewBox?: string;
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
export const MenuBarItem = observer<Props, HTMLButtonElement>(
|
||||
forwardRef(function MenuBarItem(
|
||||
{ label, displayLabel = true, loading = false, hidden, icon, displaySubmenuMark, viewBox = '0 0 24 24', className, ...rest },
|
||||
{ label, displayLabel = true, loading = false, icon, displaySubmenuMark, viewBox = '0 0 24 24', style = [], ...rest },
|
||||
ref,
|
||||
) {
|
||||
const styles = useS(style);
|
||||
const translate = useTranslate();
|
||||
loading = useStateDelay(loading, 100);
|
||||
|
||||
const title = translate(rest.title);
|
||||
return (
|
||||
<button ref={ref} className={s(styles, { menuBarItem: true, hidden }, className)} {...rest} title={title} aria-label={title}>
|
||||
<div className={s(styles, { menuBarItemBox: true }, className)}>
|
||||
return styled(useStyles(style))(
|
||||
<menu-bar-item ref={ref} as="button" {...rest} title={title} aria-label={title}>
|
||||
<menu-bar-item-box>
|
||||
{loading ? (
|
||||
<div className={s(styles, { menuBarItemIcon: true }, className)}>
|
||||
<Loader className={s(styles, { loader: true }, className)} small fullSize />
|
||||
</div>
|
||||
<menu-bar-item-icon>
|
||||
<Loader small fullSize />
|
||||
</menu-bar-item-icon>
|
||||
) : (
|
||||
icon && (
|
||||
<div className={s(styles, { menuBarItemIcon: true }, className)}>
|
||||
<Loader className={s(styles, { loader: true }, className)} suspense small fullSize>
|
||||
{typeof icon === 'string' ? (
|
||||
<IconOrImage className={s(styles, { iconOrImage: true }, className)} icon={icon} viewBox={viewBox} />
|
||||
) : (
|
||||
icon
|
||||
)}
|
||||
</Loader>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
{label && displayLabel && (
|
||||
<div className={s(styles, { menuBarItemLabel: true }, className)}>{translate(label)}</div>
|
||||
icon && <menu-bar-item-icon>{typeof icon === 'string' ? <IconOrImage icon={icon} viewBox={viewBox} /> : icon}</menu-bar-item-icon>
|
||||
)}
|
||||
{label && displayLabel ? <menu-bar-item-label>{translate(label)}</menu-bar-item-label> : <padding />}
|
||||
{displaySubmenuMark && (
|
||||
<div className={s(styles, { menuBarItemMark: true }, className)}>
|
||||
<Icon className={s(styles, { icon: true }, className)} name="angle" viewBox="0 0 15 8" />
|
||||
</div>
|
||||
<menu-bar-item-mark>
|
||||
<Icon name="angle" viewBox="0 0 15 8" />
|
||||
</menu-bar-item-mark>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
</menu-bar-item-box>
|
||||
</menu-bar-item>,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -7,8 +7,11 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useCallback } from 'react';
|
||||
import { MenuItem, MenuItemCheckbox, MenuSeparator, MenuStateReturn } from 'reakit/Menu';
|
||||
import styled, { use } from 'reshadow';
|
||||
|
||||
import { Checkbox, MenuItem, MenuItemCheckbox, MenuItemElement, MenuSeparator } from '@cloudbeaver/core-blocks';
|
||||
import { Checkbox, joinStyles, Loader, MenuItemElement, menuPanelStyles, useStyles } from '@cloudbeaver/core-blocks';
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
import {
|
||||
IMenuData,
|
||||
IMenuItem,
|
||||
@@ -23,15 +26,18 @@ import {
|
||||
import { MenuActionElement } from './MenuActionElement';
|
||||
import { SubMenuElement } from './SubMenuElement';
|
||||
|
||||
export interface IMenuItemRendererProps extends React.ButtonHTMLAttributes<any> {
|
||||
export interface IMenuItemRendererProps extends Omit<React.ButtonHTMLAttributes<any>, 'style'> {
|
||||
item: IMenuItem;
|
||||
menuData: IMenuData;
|
||||
menu: MenuStateReturn; // from reakit useMenuState
|
||||
modal?: boolean;
|
||||
rtl?: boolean;
|
||||
onItemClose?: () => void;
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
export const MenuItemRenderer = observer<IMenuItemRendererProps>(function MenuItemRenderer({ item, modal, rtl, menuData, onItemClose }) {
|
||||
export const MenuItemRenderer = observer<IMenuItemRendererProps>(function MenuItemRenderer({ item, modal, rtl, menuData, menu, onItemClose, style }) {
|
||||
const styles = useStyles(menuPanelStyles, style);
|
||||
const onClick = useCallback(
|
||||
(keepMenuOpen = true) => {
|
||||
item.events?.onSelect?.();
|
||||
@@ -46,39 +52,42 @@ export const MenuItemRenderer = observer<IMenuItemRendererProps>(function MenuIt
|
||||
if (item instanceof MenuCustomItem) {
|
||||
const CustomMenuItem = item.getComponent();
|
||||
|
||||
return <CustomMenuItem item={item} menuData={menuData} onClick={onClick} />;
|
||||
return styled(styles)(<CustomMenuItem item={item} menu={menu} menuData={menuData} style={style} onClick={onClick} />);
|
||||
}
|
||||
|
||||
if (item instanceof MenuSubMenuItem) {
|
||||
return (
|
||||
return styled(styles)(
|
||||
<MenuItem
|
||||
{...menu}
|
||||
{...{ as: SubMenuElement }}
|
||||
{...use({ hidden: item.hidden })}
|
||||
id={item.id}
|
||||
aria-label={item.menu.label}
|
||||
hidden={item.hidden}
|
||||
itemRenderer={MenuItemRenderer}
|
||||
menuRtl={rtl}
|
||||
menuData={menuData}
|
||||
menuModal={modal}
|
||||
subMenu={item}
|
||||
style={style}
|
||||
onItemClose={onItemClose}
|
||||
onClick={() => onClick()}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
if (item instanceof MenuSeparatorItem) {
|
||||
return <MenuSeparator />;
|
||||
return styled(styles)(<MenuSeparator {...menu} />);
|
||||
}
|
||||
|
||||
if (item instanceof MenuActionItem) {
|
||||
return <MenuActionElement item={item} menuData={menuData} onClick={onClick} />;
|
||||
return <MenuActionElement item={item} menu={menu} menuData={menuData} style={style} onClick={onClick} />;
|
||||
}
|
||||
|
||||
if (item instanceof MenuCheckboxItem) {
|
||||
return (
|
||||
return styled(styles)(
|
||||
<MenuItemCheckbox
|
||||
hidden={item.hidden}
|
||||
{...menu}
|
||||
{...use({ hidden: item.hidden })}
|
||||
id={item.id}
|
||||
aria-label={item.label}
|
||||
disabled={item.disabled}
|
||||
@@ -89,10 +98,11 @@ export const MenuItemRenderer = observer<IMenuItemRendererProps>(function MenuIt
|
||||
>
|
||||
<MenuItemElement
|
||||
label={item.label}
|
||||
icon={<Checkbox checked={item.checked} mod={['primary', 'small']} ripple={false} />}
|
||||
icon={<Checkbox checked={item.checked} mod={['primary', 'small']} style={style} ripple={false} />}
|
||||
tooltip={item.tooltip}
|
||||
style={style}
|
||||
/>
|
||||
</MenuItemCheckbox>
|
||||
</MenuItemCheckbox>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -100,10 +110,31 @@ export const MenuItemRenderer = observer<IMenuItemRendererProps>(function MenuIt
|
||||
const IconComponent = item.iconComponent?.();
|
||||
const extraProps = item.getExtraProps?.();
|
||||
|
||||
return (
|
||||
<MenuItem id={item.id} aria-label={item.label} hidden={item.hidden} disabled={item.disabled} onClick={onClick} {...extraProps}>
|
||||
<MenuItemElement label={item.label} icon={IconComponent ? <IconComponent item={item} {...extraProps} /> : item.icon} tooltip={item.tooltip} />
|
||||
</MenuItem>
|
||||
return styled(styles)(
|
||||
<MenuItem
|
||||
{...menu}
|
||||
{...use({ hidden: item.hidden })}
|
||||
id={item.id}
|
||||
aria-label={item.label}
|
||||
disabled={item.disabled}
|
||||
onClick={onClick}
|
||||
{...extraProps}
|
||||
>
|
||||
<MenuItemElement
|
||||
label={item.label}
|
||||
icon={
|
||||
IconComponent
|
||||
? styled(styles)(
|
||||
<Loader suspense inline fullSize>
|
||||
<IconComponent item={item} style={joinStyles(menuPanelStyles, style)} {...extraProps} />
|
||||
</Loader>,
|
||||
)
|
||||
: item.icon
|
||||
}
|
||||
tooltip={item.tooltip}
|
||||
style={style}
|
||||
/>
|
||||
</MenuItem>,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,21 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { forwardRef, useRef, useState } from 'react';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import { getComputed, IMenuState, Menu, MenuItemElement, useAutoLoad, useObjectRef } from '@cloudbeaver/core-blocks';
|
||||
import {
|
||||
getComputed,
|
||||
IMenuState,
|
||||
joinStyles,
|
||||
Loader,
|
||||
Menu,
|
||||
MenuItemElement,
|
||||
menuPanelStyles,
|
||||
useAutoLoad,
|
||||
useObjectRef,
|
||||
useStyles,
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
import { DATA_CONTEXT_MENU_NESTED, DATA_CONTEXT_SUBMENU_ITEM, IMenuData, IMenuSubMenuItem, MenuActionItem, useMenu } from '@cloudbeaver/core-view';
|
||||
|
||||
import type { IMenuItemRendererProps } from './MenuItemRenderer';
|
||||
@@ -20,11 +33,13 @@ interface ISubMenuElementProps extends Omit<React.ButtonHTMLAttributes<any>, 'st
|
||||
menuModal?: boolean;
|
||||
menuRtl?: boolean;
|
||||
onItemClose?: () => void;
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
export const SubMenuElement = observer<ISubMenuElementProps, HTMLButtonElement>(
|
||||
forwardRef(function SubMenuElement({ menuData, subMenu, itemRenderer, menuModal: modal, menuRtl: rtl, onItemClose, ...rest }, ref) {
|
||||
forwardRef(function SubMenuElement({ menuData, subMenu, itemRenderer, style, menuModal: modal, menuRtl: rtl, onItemClose, ...rest }, ref) {
|
||||
const menu = useRef<IMenuState>();
|
||||
const styles = useStyles(menuPanelStyles, style);
|
||||
const subMenuData = useMenu({ menu: subMenu.menu, context: menuData.context });
|
||||
const [visible, setVisible] = useState(false);
|
||||
subMenuData.context.set(DATA_CONTEXT_MENU_NESTED, true);
|
||||
@@ -74,7 +89,7 @@ export const SubMenuElement = observer<ISubMenuElementProps, HTMLButtonElement>(
|
||||
const MenuItemRenderer = itemRenderer;
|
||||
const panelAvailable = subMenuData.available || !loaded;
|
||||
|
||||
return (
|
||||
return styled(styles)(
|
||||
<Menu
|
||||
ref={ref}
|
||||
menuRef={menu}
|
||||
@@ -83,12 +98,22 @@ export const SubMenuElement = observer<ISubMenuElementProps, HTMLButtonElement>(
|
||||
subMenuData.items.map(
|
||||
item =>
|
||||
menu.current && (
|
||||
<MenuItemRenderer key={item.id} item={item} menuData={subMenuData} rtl={rtl} modal={modal} onItemClose={handlers.handleItemClose} />
|
||||
<MenuItemRenderer
|
||||
key={item.id}
|
||||
item={item}
|
||||
menuData={subMenuData}
|
||||
menu={menu.current}
|
||||
rtl={rtl}
|
||||
modal={modal}
|
||||
style={style}
|
||||
onItemClose={handlers.handleItemClose}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}
|
||||
rtl={rtl}
|
||||
modal={modal}
|
||||
style={style}
|
||||
placement={rtl ? 'left-start' : 'right-start'}
|
||||
panelAvailable={panelAvailable}
|
||||
disabled={disabled}
|
||||
@@ -100,13 +125,22 @@ export const SubMenuElement = observer<ISubMenuElementProps, HTMLButtonElement>(
|
||||
<MenuItemElement
|
||||
label={label}
|
||||
displayLabel={displayLabel}
|
||||
icon={IconComponent ? <IconComponent item={subMenu} {...extraProps} /> : icon}
|
||||
icon={
|
||||
IconComponent
|
||||
? styled(styles)(
|
||||
<Loader suspense small fullSize>
|
||||
<IconComponent item={subMenu} style={joinStyles(menuPanelStyles, style)} {...extraProps} />
|
||||
</Loader>,
|
||||
)
|
||||
: icon
|
||||
}
|
||||
tooltip={tooltip}
|
||||
panelAvailable={panelAvailable}
|
||||
loading={loading}
|
||||
style={style}
|
||||
menu
|
||||
/>
|
||||
</Menu>
|
||||
</Menu>,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@ const styles = css`
|
||||
CommonDialogFooter {
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 16px;
|
||||
gap: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@ const tabsStyles = css`
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
}
|
||||
Tab {
|
||||
height: 36px !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
TabPanel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -19,7 +19,7 @@ export const BASE_TAB_STYLES = css`
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
|
||||
height: 32px;
|
||||
height: 48px;
|
||||
border-top: solid 2px transparent;
|
||||
|
||||
&:global([aria-selected='true']) {
|
||||
@@ -63,29 +63,25 @@ export const BASE_TAB_STYLES = css`
|
||||
align-items: center;
|
||||
|
||||
& tab-icon {
|
||||
display: flex;
|
||||
height: 24px;
|
||||
padding-left: 12px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
& StaticImage,
|
||||
& IconOrImage,
|
||||
& img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
& placeholder {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
& tab-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0 8px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
& tab-title placeholder {
|
||||
|
||||
@@ -12,7 +12,7 @@ export const UNDERLINE_TAB_STYLES = css`
|
||||
display: flex;
|
||||
}
|
||||
Tab {
|
||||
composes: theme-typography--caption from global;
|
||||
composes: theme-typography--body2 from global;
|
||||
background: none;
|
||||
color: inherit;
|
||||
border: none;
|
||||
@@ -20,9 +20,9 @@ export const UNDERLINE_TAB_STYLES = css`
|
||||
outline: none;
|
||||
opacity: 1;
|
||||
height: 30px !important;
|
||||
padding: 0 14px !important;
|
||||
border-top: none !important;
|
||||
font-weight: normal !important;
|
||||
text-transform: uppercase;
|
||||
|
||||
&:global([aria-selected='false']) {
|
||||
opacity: 0.8;
|
||||
|
||||
@@ -12,7 +12,7 @@ export const verticalTabStyles = css`
|
||||
overflow: auto;
|
||||
border: none !important;
|
||||
flex-shrink: 0;
|
||||
height: 32px !important;
|
||||
height: 36px !important;
|
||||
}
|
||||
vertical-tabs {
|
||||
display: flex;
|
||||
|
||||
@@ -4,8 +4,8 @@ export * from './Clipboard/ClipboardService';
|
||||
export * from './ContextMenu/ContextMenu';
|
||||
export * from './ContextMenu/IContextMenuItemProps';
|
||||
export * from './ContextMenu/MenuBar/MenuBar';
|
||||
export { default as MenuBarStyles } from './ContextMenu/MenuBar/MenuBar.m.css';
|
||||
export { default as MenuBarItemStyles } from './ContextMenu/MenuBar/MenuBarItem.m.css';
|
||||
export * from './ContextMenu/MenuBar/MENU_BAR_DEFAULT_STYLES';
|
||||
export * from './ContextMenu/MenuBar/MENU_BAR_SMALL_STYLES';
|
||||
|
||||
export * from './DragAndDrop/DND_ELEMENT_TYPE';
|
||||
export * from './DragAndDrop/DND_NATIVE_TYPE';
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
export { default as clsx, type ClassValue } from 'clsx';
|
||||
export { default as clsx } from 'clsx';
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import type { IMenuItem } from './IMenuItem';
|
||||
|
||||
interface IMenuBaseItemCommonProperties<TExtraProps = unknown> {
|
||||
@@ -21,6 +23,7 @@ export type MenuBaseItemIconComponent<TExtraProps = unknown> = React.FC<IIconCom
|
||||
|
||||
export interface IIconComponentProps<TExtraProps = unknown> {
|
||||
item: IMenuBaseItem<TExtraProps>;
|
||||
style?: ComponentStyle;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import type { IMenu } from '../IMenu';
|
||||
import type { IMenuItem, IMenuItemEvents } from './IMenuItem';
|
||||
|
||||
@@ -29,6 +31,7 @@ export type MenuSubMenuItemIconComponent<TExtraProps = unknown> = React.FC<ISubM
|
||||
|
||||
export interface ISubMenuIconComponentProps<TExtraProps = unknown> {
|
||||
item: IMenuSubMenuItem<TExtraProps>;
|
||||
style?: ComponentStyle;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
"@typescript-eslint/restrict-template-expressions": "off",
|
||||
"@typescript-eslint/return-await": "off",
|
||||
"@typescript-eslint/strict-boolean-expressions": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-for-in-array": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
@@ -167,12 +166,11 @@
|
||||
"react/display-name": "off",
|
||||
"react/jsx-no-literals": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"react/jsx-curly-newline": "off",
|
||||
"react/jsx-wrap-multilines": "off",
|
||||
"react/jsx-closing-bracket-location": "error",
|
||||
"react/jsx-closing-tag-location": "error",
|
||||
"react/jsx-boolean-value": "error",
|
||||
"react/self-closing-comp": "error",
|
||||
"react/jsx-curly-newline": "off",
|
||||
"react/jsx-curly-brace-presence": ["error", "never"],
|
||||
"react/jsx-fragments": ["off", "syntax"],
|
||||
"react/jsx-equals-spacing": ["error", "never"],
|
||||
@@ -199,6 +197,18 @@
|
||||
"beforeClosing": "never"
|
||||
}
|
||||
],
|
||||
"react/jsx-wrap-multilines": [
|
||||
"error",
|
||||
{
|
||||
"declaration": "parens-new-line",
|
||||
"assignment": "parens-new-line",
|
||||
"return": "parens-new-line",
|
||||
"arrow": "parens-new-line",
|
||||
"condition": "parens-new-line",
|
||||
"logical": "parens-new-line",
|
||||
"prop": "parens-new-line"
|
||||
}
|
||||
],
|
||||
"react/jsx-sort-props": [
|
||||
"error",
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ const administrationStyles = css`
|
||||
}
|
||||
TabList {
|
||||
width: 240px;
|
||||
padding-top: 8px;
|
||||
padding-top: 16px;
|
||||
border-right: 2px solid;
|
||||
}
|
||||
content-container {
|
||||
|
||||
+10
-7
@@ -6,18 +6,17 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import { AppAuthService } from '@cloudbeaver/core-authentication';
|
||||
import { s, useS } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { MenuBar } from '@cloudbeaver/core-ui';
|
||||
import { useMenu } from '@cloudbeaver/core-view';
|
||||
import { TopMenuWrapperStyles } from '@cloudbeaver/plugin-top-app-bar';
|
||||
import { MENU_BAR_DISABLE_EFFECT_STYLES, MENU_BAR_ITEM_STYLES, MENU_BAR_STYLES, topMenuStyles } from '@cloudbeaver/plugin-top-app-bar';
|
||||
|
||||
import { MENU_APP_ADMINISTRATION_ACTIONS } from './MENU_APP_ADMINISTRATION_ACTIONS';
|
||||
|
||||
export const AdministrationMenu = observer(function AdministrationMenu() {
|
||||
const styles = useS(TopMenuWrapperStyles);
|
||||
const menu = useMenu({ menu: MENU_APP_ADMINISTRATION_ACTIONS });
|
||||
const { authenticated } = useService(AppAuthService);
|
||||
|
||||
@@ -25,9 +24,13 @@ export const AdministrationMenu = observer(function AdministrationMenu() {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={s(styles, { menuWrapper: true })}>
|
||||
<MenuBar menu={menu} nestedMenuSettings={{ modal: true }} />
|
||||
</div>
|
||||
return styled(MENU_BAR_STYLES)(
|
||||
<menu-wrapper>
|
||||
<MenuBar
|
||||
menu={menu}
|
||||
style={[topMenuStyles, MENU_BAR_ITEM_STYLES, topMenuStyles, MENU_BAR_DISABLE_EFFECT_STYLES]}
|
||||
nestedMenuSettings={{ modal: true }}
|
||||
/>
|
||||
</menu-wrapper>,
|
||||
);
|
||||
});
|
||||
|
||||
+6
-2
@@ -5,13 +5,17 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { Placeholder, TopAppBar } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { TopNavBar } from '@cloudbeaver/plugin-top-app-bar';
|
||||
|
||||
import { AdministrationTopAppBarService } from './AdministrationTopAppBarService';
|
||||
|
||||
export function AdministrationTopAppBar() {
|
||||
const administrationTopAppBarService = useService(AdministrationTopAppBarService);
|
||||
|
||||
return <TopNavBar container={administrationTopAppBarService.placeholder} />;
|
||||
return (
|
||||
<TopAppBar>
|
||||
<Placeholder container={administrationTopAppBarService.placeholder} />
|
||||
</TopAppBar>
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ const styles = css`
|
||||
composes: theme-background-surface theme-text-on-surface theme-border-color-background from global;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 32px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
padding: 0 16px;
|
||||
align-items: center;
|
||||
|
||||
@@ -26,7 +26,7 @@ const styles = css`
|
||||
composes: theme-background-surface theme-text-on-surface theme-border-color-background from global;
|
||||
position: relative;
|
||||
border: solid 1px;
|
||||
padding: 8px;
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
p {
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { lazy } from 'react';
|
||||
|
||||
import { AdministrationScreenService } from '@cloudbeaver/core-administration';
|
||||
import { EAdminPermission } from '@cloudbeaver/core-authentication';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
@@ -14,12 +12,11 @@ import { PermissionsService } from '@cloudbeaver/core-root';
|
||||
import { ScreenService } from '@cloudbeaver/core-routing';
|
||||
import { DATA_CONTEXT_MENU, MenuBaseItem, MenuService } from '@cloudbeaver/core-view';
|
||||
import { TOP_NAV_BAR_SETTINGS_MENU } from '@cloudbeaver/plugin-settings-menu';
|
||||
import { AppStateMenu } from '@cloudbeaver/plugin-top-app-bar';
|
||||
|
||||
import { AdministrationMenu } from './AdministrationMenu/AdministrationMenu';
|
||||
import { AdministrationTopAppBarService } from './AdministrationScreen/AdministrationTopAppBar/AdministrationTopAppBarService';
|
||||
|
||||
const AdministrationMenu = lazy(() => import('./AdministrationMenu/AdministrationMenu').then(m => ({ default: m.AdministrationMenu })));
|
||||
const AppStateMenu = lazy(() => import('@cloudbeaver/plugin-top-app-bar').then(m => ({ default: m.AppStateMenu })));
|
||||
|
||||
@injectable()
|
||||
export class PluginBootstrap extends Bootstrap {
|
||||
constructor(
|
||||
|
||||
+8
-3
@@ -33,6 +33,11 @@ const tabsStyles = css`
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
}
|
||||
Tab {
|
||||
height: 46px !important;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
`;
|
||||
|
||||
const topBarStyles = css`
|
||||
@@ -40,7 +45,7 @@ const topBarStyles = css`
|
||||
composes: theme-border-color-background theme-background-secondary theme-text-on-secondary from global;
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding-top: 8px;
|
||||
padding-top: 16px;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
@@ -58,14 +63,14 @@ const topBarStyles = css`
|
||||
configuration-top-bar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
padding: 0 24px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
configuration-status-message {
|
||||
composes: theme-typography--caption from global;
|
||||
height: 24px;
|
||||
padding: 0 8px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ import { CreateAuthConfigurationService } from './CreateAuthConfigurationService
|
||||
|
||||
const loaderStyle = css`
|
||||
ExceptionMessage {
|
||||
padding: 16px;
|
||||
padding: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
+2
-2
@@ -23,9 +23,9 @@ const styles = css`
|
||||
}
|
||||
|
||||
title-bar {
|
||||
composes: theme-border-color-background from global;
|
||||
composes: theme-border-color-background theme-typography--headline6 from global;
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
padding: 16px 24px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-weight: 400;
|
||||
|
||||
+2
-2
@@ -25,9 +25,9 @@ const styles = css`
|
||||
}
|
||||
|
||||
title-bar {
|
||||
composes: theme-border-color-background from global;
|
||||
composes: theme-border-color-background theme-typography--headline6 from global;
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
padding: 16px 24px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-weight: 400;
|
||||
|
||||
+8
-3
@@ -22,6 +22,11 @@ const tabsStyles = css`
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
}
|
||||
Tab {
|
||||
height: 46px !important;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
`;
|
||||
|
||||
const topBarStyles = css`
|
||||
@@ -29,7 +34,7 @@ const topBarStyles = css`
|
||||
composes: theme-border-color-background theme-background-secondary theme-text-on-secondary from global;
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding-top: 8px;
|
||||
padding-top: 16px;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
@@ -47,7 +52,7 @@ const topBarStyles = css`
|
||||
top-bar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
padding: 0 24px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
@@ -58,7 +63,7 @@ const topBarStyles = css`
|
||||
status-message {
|
||||
composes: theme-typography--caption from global;
|
||||
height: 24px;
|
||||
padding: 0 8px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import { observer } from 'mobx-react-lite';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
import { Tab, TabTitle } from '@cloudbeaver/core-ui';
|
||||
import { Tab } from '@cloudbeaver/core-ui';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
@@ -22,7 +22,7 @@ export const MetaParameterInfoTab = observer<Props>(function MetaParameterInfoTa
|
||||
|
||||
return styled(styles)(
|
||||
<Tab tabId="info" style={style} className={className}>
|
||||
<TabTitle>{translate('plugin_user_profile_info')}</TabTitle>
|
||||
{translate('plugin_user_profile_info')}
|
||||
</Tab>,
|
||||
);
|
||||
});
|
||||
|
||||
+2
-2
@@ -23,9 +23,9 @@ const styles = css`
|
||||
}
|
||||
|
||||
title-bar {
|
||||
composes: theme-border-color-background from global;
|
||||
composes: theme-border-color-background theme-typography--headline6 from global;
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
padding: 16px 24px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-weight: 400;
|
||||
|
||||
+8
-3
@@ -33,6 +33,11 @@ const tabsStyles = css`
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
}
|
||||
Tab {
|
||||
height: 46px !important;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
`;
|
||||
|
||||
const topBarStyles = css`
|
||||
@@ -40,7 +45,7 @@ const topBarStyles = css`
|
||||
composes: theme-border-color-background theme-background-secondary theme-text-on-secondary from global;
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding-top: 8px;
|
||||
padding-top: 16px;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
@@ -58,14 +63,14 @@ const topBarStyles = css`
|
||||
team-top-bar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
padding: 0 24px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
team-status-message {
|
||||
composes: theme-typography--caption from global;
|
||||
height: 24px;
|
||||
padding: 0 8px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user