dbeaver/pro#7806 remove MenuBarSmallItem (#4224)

* dbeaver/pro#7806 remove MenuBarSmallItem

* dbeaver/pro#7806 remove reakit

* dbeaver/pro#7806 remove extra class

* dbeaver/pro#7806 increase base padding and background color

* dbeaver/pro#7806 use more safe style

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
This commit is contained in:
alex
2026-03-27 14:18:35 +01:00
committed by GitHub
co-authored by Daria Marutkina mr-anton-t
parent 36dfdc8b33
commit fab9d1dd43
52 changed files with 244 additions and 826 deletions
@@ -1,11 +1,12 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 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 clsx from 'clsx';
import { Button as AriaButton, type ButtonProps as UnstyledButtonProps } from '@ariakit/react';
import type { ControlSize } from '../types/controls.js';
import { componentProviderWrapper } from '../componentProviderWrapper.js';
import { ButtonRoot, type ButtonRootProps } from './ButtonRoot.js';
@@ -37,3 +38,6 @@ export function ButtonBaseIcon({ className, children, placement }: ButtonIconPro
export const Button = componentProviderWrapper('Button', ButtonBase);
export const ButtonIcon = componentProviderWrapper('ButtonIcon', ButtonBaseIcon);
export const UnstyledButton = AriaButton;
export { type UnstyledButtonProps };
@@ -1,7 +1,7 @@
@layer base {
:root {
--dbv-kit-btn-height: var(--dbv-kit-control-height-medium);
--dbv-kit-btn-padding-inline: calc(var(--tw-spacing) * 2.5);
--dbv-kit-btn-padding-inline: calc(var(--tw-spacing) * 3);
--dbv-kit-btn-gap: calc(var(--dbv-kit-btn-padding-inline) / 2);
--dbv-kit-btn-outline-offset: var(--dbv-kit-control-outline-offset);
@@ -0,0 +1,32 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 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 {
Composite as AriakitComposite,
CompositeItem as AriakitCompositeItem,
CompositeProvider as AriakitCompositeProvider,
useCompositeStore,
type CompositeProps,
type CompositeItemProps,
type CompositeProviderProps,
type CompositeStore,
} from '@ariakit/react';
export function CompositeProvider({ children, ...props }: CompositeProviderProps) {
return <AriakitCompositeProvider {...props}>{children}</AriakitCompositeProvider>;
}
export function Composite({ children, ...props }: CompositeProps) {
return <AriakitComposite {...props}>{children}</AriakitComposite>;
}
export function CompositeItem({ children, ...props }: CompositeItemProps) {
return <AriakitCompositeItem {...props}>{children}</AriakitCompositeItem>;
}
export { useCompositeStore, type CompositeProps, type CompositeItemProps, type CompositeProviderProps, type CompositeStore };
@@ -45,6 +45,8 @@ import {
type MenuListProps,
type MenuBarProps,
type MenuItemCheckProps,
type MenuStore,
type MenuStoreState
} from '@ariakit/react';
import clsx from 'clsx';
@@ -175,6 +177,8 @@ export {
type MenuStoreProps,
type MenuArrowProps,
type HovercardStoreState,
type MenuStore,
type MenuStoreState
};
Menu.Provider = MenuProvider;
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -11,6 +11,10 @@ import clsx from 'clsx';
import './Popover.css';
export function PopoverAnchor({ children, ...props }: Ariakit.PopoverAnchorProps) {
return <Ariakit.PopoverAnchor {...props}>{children}</Ariakit.PopoverAnchor>;
}
export function Popover({ children, ...props }: Ariakit.PopoverProviderProps) {
return <Ariakit.PopoverProvider {...props}>{children}</Ariakit.PopoverProvider>;
}
@@ -40,3 +44,6 @@ Popover.PopoverArrow = PopoverArrow;
Popover.PopoverHeading = PopoverHeading;
Popover.PopoverDescription = PopoverDescription;
Popover.PopoverContent = PopoverContent;
Popover.Anchor = PopoverAnchor;
export { usePopoverStore, type PopoverStore } from '@ariakit/react';
@@ -0,0 +1,15 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 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 { Portal as AriakitPortal, type PortalProps } from '@ariakit/react';
export function Portal({ children, ...props }: PortalProps) {
return <AriakitPortal {...props}>{children}</AriakitPortal>;
}
export type { PortalProps };
@@ -21,7 +21,7 @@ export {
type FocusTrapRegionProps,
} from '@ariakit/react';
export { Button, ButtonBase, type ButtonProps, ButtonIcon, type ButtonIconProps } from './Button/Button.js';
export { Button, ButtonBase, type ButtonProps, ButtonIcon, type ButtonIconProps, UnstyledButton, type UnstyledButtonProps } from './Button/Button.js';
export { IconButton, IconButtonBase, type IconButtonProps } from './IconButton/IconButton.js';
export { Checkbox, CheckboxBase, type CheckboxProps } from './Checkbox/Checkbox.js';
export { ColorPicker } from './ColorPicker/ColorPicker.js';
@@ -46,7 +46,7 @@ export {
} from './Select/Select.js';
export * from './Combobox/Combobox.js';
export * from './Command/Command.js';
export { Popover } from './Popover/Popover.js';
export { Popover, usePopoverStore, type PopoverStore } from './Popover/Popover.js';
export { SelectField, type SelectFieldProps, type SelectItem } from './Select/SelectField.js';
export { Spinner, type SpinnerProps } from './Spinner/Spinner.js';
export { Radio, RadioGroup, useRadioContext, useRadioStore, type RadioProviderProps, type RadioProps, type RadioGroupProps } from './Radio/index.js';
@@ -57,3 +57,14 @@ export * from './Menu/Menu.js';
export * from './Disclosure/Disclosure.js';
export * from './Dialog/Dialog.js';
export * from './Tab/Tab.js';
export { Portal, type PortalProps } from './Portal/Portal.js';
export {
Composite,
CompositeItem,
CompositeProvider,
useCompositeStore,
type CompositeProps,
type CompositeItemProps,
type CompositeProviderProps,
type CompositeStore,
} from './Composite/Composite.js';
-1
View File
@@ -59,7 +59,6 @@
"prettier": "^3.6.2",
"react": "^19",
"react-dom": "^19",
"reakit": "^1",
"rimraf": "^6",
"typescript": "^5.8.3",
"typescript-plugin-css-modules": "^5",
-2
View File
@@ -44,8 +44,6 @@
"react-dom": "^19",
"react-hotkeys-hook": "5.1.0",
"react-minisearch": "^7",
"reakit": "^1",
"reakit-utils": "^0",
"subscript": "^9",
"tslib": "^2"
},
+2 -3
View File
@@ -7,7 +7,6 @@
*/
@layer components {
.dbv-kit-button {
--dbv-kit-btn-padding-inline: 15px;
--dbv-kit-btn-outline-color: var(--theme-primary);
--dbv-kit-control-disabled-foreground: #3535355e;
--dbv-kit-control-disabled-background: #3535351f;
@@ -22,7 +21,7 @@
--dbv-kit-btn-primary-border-color: var(--theme-primary);
--dbv-kit-btn-secondary-background: transparent;
--dbv-kit-btn-secondary-background-hover: color-mix(in srgb, transparent, var(--theme-primary) 5%);
--dbv-kit-btn-secondary-background-hover: color-mix(in srgb, transparent, var(--theme-primary) 10%);
--dbv-kit-btn-secondary-background-active: color-mix(in srgb, transparent, var(--theme-primary) 20%);
--dbv-kit-btn-secondary-foreground: var(--theme-link-color);
--dbv-kit-btn-secondary-foreground-hover: var(--theme-link-color);
@@ -37,7 +36,7 @@
--dbv-kit-btn-ghost-foreground-hover: var(--theme-link-color);
--dbv-kit-btn-ghost-foreground-active: var(--theme-link-color);
--dbv-kit-btn-ghost-background: transparent;
--dbv-kit-btn-ghost-background-hover: color-mix(in srgb, transparent, var(--theme-primary) 5%);
--dbv-kit-btn-ghost-background-hover: color-mix(in srgb, transparent, var(--theme-primary) 10%);
--dbv-kit-btn-ghost-background-active: color-mix(in srgb, transparent, var(--theme-primary) 20%);
--dbv-kit-btn-ghost-border-color: transparent;
@@ -1,11 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
.menu {
display: none !important;
}
@@ -1,82 +1,45 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 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 type { RefObject } from 'react';
import BaseDropdownStyles from '../FormControls/BaseDropdown.module.css';
import { IconOrImage } from '../IconOrImage.js';
import { Menu } from '../Menu/Menu.js';
import { MenuItem } from '../Menu/MenuItem.js';
import type { IMenuState } from '../Menu/MenuStateContext.js';
import { s } from '../s.js';
import { Text } from '../Text.js';
import { useS } from '../useS.js';
import style from './InputAutocompletionMenu.module.css';
import { observer } from 'mobx-react-lite';
import { Composite, CompositeItem, Popover } from '@dbeaver/ui-kit';
import { type InputAutocompleteProposal } from './useInputAutocomplete.js';
import { useS } from '../useS.js';
import { BaseDropdownStyles, IconOrImage, s, Text } from '../index.js';
import { forwardRef } from 'react';
interface AutocompletionProps {
position: { x: number; y: number };
proposals: InputAutocompleteProposal[];
menuRef: RefObject<IMenuState>;
className?: string;
onSelect?: (proposal: InputAutocompleteProposal) => void;
}
export const InputAutocompletionMenu = observer(function InputAutocompletionMenu({
position,
className,
menuRef,
export const InputAutocompletionMenu = observer(forwardRef<HTMLDivElement, AutocompletionProps>(function InputAutocompletionMenu({
proposals,
onSelect,
}: AutocompletionProps) {
const styles = useS(style, BaseDropdownStyles);
const contextMenuPosition = {
position,
open: () => {},
close: () => {},
};
}, ref) {
const styles = useS(BaseDropdownStyles);
function handleSelect(proposal: InputAutocompleteProposal) {
menuRef.current?.hide();
onSelect?.(proposal);
}
if (!proposals.length) {
return;
}
// TODO: migrate to new menu or popover
return (
<Menu
contextMenuPosition={contextMenuPosition}
visible={proposals.length > 0}
panelAvailable={proposals.length > 0}
className={s(styles, { menu: true }, className)}
menuRef={menuRef}
label="Autocompletion"
items={proposals.map(proposal => (
<MenuItem
key={proposal.displayString}
id={proposal.displayString}
type="button"
title={proposal.title}
className={s(styles, { menuItem: true })}
onClick={event => handleSelect(proposal)}
>
{proposal.icon && (
<div className={s(styles, { itemIcon: true })}>
<IconOrImage icon={proposal.icon} className={s(styles, { iconOrImage: true })} />
</div>
)}
<Text truncate>{proposal.displayString}</Text>
</MenuItem>
))}
modal
/>
<Popover.PopoverContent ref={ref} className={s(styles, { menu: true })} gutter={4} modal={false} autoFocusOnShow={false} unmountOnHide>
<Composite>
{proposals.map(proposal => (
<CompositeItem key={proposal.title} className={s(styles, { menuItem: true }, 'tw:w-full')} onClick={() => onSelect?.(proposal)}>
{proposal.icon && (
<div className={s(styles, { itemIcon: true })}>
<IconOrImage icon={proposal.icon} className={s(styles, { iconOrImage: true })} />
</div>
)}
<Text truncate>{proposal.displayString}</Text>
</CompositeItem>
))}
</Composite>
</Popover.PopoverContent>
);
});
}));
@@ -18,12 +18,3 @@
align-items: center;
gap: 2px;
}
.iconButton {
width: 12px;
height: 12px;
flex-shrink: 0;
cursor: pointer;
&:hover {
opacity: 0.5;
}
}
@@ -9,10 +9,10 @@ import { observer } from 'mobx-react-lite';
import { type ReactNode, useContext, useState } from 'react';
import { blobToBase64, bytesToSize } from '@cloudbeaver/core-utils';
import { IconButton } from '@dbeaver/ui-kit';
import { Button } from '../Button.js';
import type { ILayoutSizeProps } from '../Containers/ILayoutSizeProps.js';
import { IconButton } from '../IconButton.js';
import { useTranslate } from '../localization/useTranslate.js';
import { s } from '../s.js';
import { UploadArea } from '../UploadArea.js';
@@ -22,6 +22,7 @@ import { FieldDescription } from './FieldDescription.js';
import { FieldLabel } from './FieldLabel.js';
import { FormContext } from './FormContext.js';
import inputFileTextContentStyles from './InputFileTextContent.module.css';
import { Icon } from '../Icon.js';
const DEFAULT_MAX_FILE_SIZE = 2048;
@@ -139,13 +140,24 @@ export const InputFileTextContent: InputFileTextContentType = observer(function
{children}
</FieldLabel>
<UploadArea title={tooltip} disabled={disabled} accept={accept} reset onChange={handleChange}>
<Button className="tw:inline-flex" icon="/icons/import.svg" tag="div" variant="secondary" disabled={disabled}>
<Button className="tw:w-max" icon="/icons/import.svg" tag="div" variant="secondary" disabled={disabled}>
{translate('ui_upload_file')}
</Button>
</UploadArea>
<FieldDescription className={s(styles, { fieldDescription: true })}>
{description}
{(selected || saved) && <IconButton className={s(styles, { iconButton: true })} disabled={disabled} name="cross" onClick={removeFile} />}
{(selected || saved) && (
<IconButton
className="tw:shrink-0"
variant="secondary"
disabled={disabled}
size="small"
aria-label={translate('ui_remove')}
onClick={removeFile}
>
<Icon name="cross" width={12} height={12} />
</IconButton>
)}
</FieldDescription>
</Field>
);
@@ -184,7 +184,7 @@ export const InputFiles: InputFilesType = observer(
<UploadArea ref={ref} {...rest} name={name} value={value} required={required} reset onChange={handleChange}>
<Button
iconPlacement="start"
className="tw:inline-flex tw:justify-center"
className="tw:w-max tw:justify-center"
icon="/icons/import.svg"
tag="div"
loading={loading}
@@ -143,7 +143,7 @@ export const Textarea: TextareaType = observer(function Textarea({
}
}}
>
<Button className="tw:inline-flex" tag="div" disabled={rest.disabled || rest.readOnly} variant="secondary">
<Button className="tw:w-max" tag="div" disabled={rest.disabled || rest.readOnly} variant="secondary">
{translate('ui_file')}
</Button>
</UploadArea>
@@ -1,17 +1,17 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 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 { action, computed, observable } from 'mobx';
import { type RefObject, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
import { type RefObject, useEffect, useLayoutEffect, useMemo } from 'react';
import { debounce } from '@cloudbeaver/core-utils';
import { isNotNullDefined } from '@dbeaver/js-helpers';
import type { IMenuState } from '../Menu/MenuStateContext.js';
import type { IContextMenuPositionCoords } from '../Menu/useContextMenuPosition.js';
import { useObservableRef } from '../useObservableRef.js';
import { type SearchStrategy, useSearch } from '../useSearch.js';
@@ -23,6 +23,7 @@ interface InputAutocompleteOptions {
separator?: string | RegExp;
matchStrategy?: SearchStrategy;
predicate?: (suggestion: InputAutocompleteProposal, lastWord?: string) => boolean;
onNavigate?: () => void;
}
export interface InputAutocompleteProposal {
@@ -38,7 +39,6 @@ interface State {
proposals: InputAutocompleteProposal[];
position: IContextMenuPositionCoords;
inputValue: string;
menuRef: RefObject<IMenuState>;
}
const DEFAULT_SEPARATOR = ' ';
@@ -48,7 +48,7 @@ const CONTEXT_INPUT_OFFSET_Y = 3;
export const useInputAutocomplete = (
inputRef: RefObject<HTMLInputElement | HTMLTextAreaElement | null>,
{ sourceHints, separator = DEFAULT_SEPARATOR, matchStrategy = 'contains', predicate }: InputAutocompleteOptions,
{ sourceHints, separator = DEFAULT_SEPARATOR, matchStrategy = 'contains', onNavigate, predicate }: InputAutocompleteOptions,
): Readonly<State> => {
const search = useSearch({
sourceHints,
@@ -56,7 +56,7 @@ export const useInputAutocomplete = (
matchStrategy,
predicate,
});
const menuRef = useRef<IMenuState>(null);
const state = useObservableRef(
() => ({
position: { x: 0, y: 0 } as IContextMenuPositionCoords,
@@ -126,7 +126,7 @@ export const useInputAutocomplete = (
position: observable.ref,
inputValue: observable.ref,
},
{ inputRef, search, menuRef },
{ inputRef, search, onNavigate },
);
const handleInput = useMemo(
@@ -145,12 +145,11 @@ export const useInputAutocomplete = (
function handleKeyDown(event: any) {
switch (event.key) {
case 'Escape':
state.menuRef.current?.hide();
state.resetState();
break;
case 'ArrowDown':
case 'ArrowUp':
state.menuRef.current?.first();
state.onNavigate?.();
break;
case 'Tab':
state.resetState();
@@ -1,13 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 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 type React from 'react';
import { type ButtonProps, Button as ReakitButton } from 'reakit';
import { UnstyledButton, type UnstyledButtonProps } from '@dbeaver/ui-kit';
import { Icon } from './Icon.js';
import IconButtonStyles from './IconButton.module.css';
@@ -16,25 +17,22 @@ import { StaticImage } from './StaticImage.js';
import { useS } from './useS.js';
interface Props {
tag?: 'button' | 'a' | 'div';
name: string;
img?: boolean;
viewBox?: string;
}
export type IconButtonProps = Props & ButtonProps;
export type IconButtonProps = Props & UnstyledButtonProps;
export const IconButton: React.FC<IconButtonProps> = observer(function IconButton({ tag, name, img, viewBox, className, ...rest }) {
export const IconButton: React.FC<IconButtonProps> = observer(function IconButton({ name, img, viewBox, className, ...rest }) {
const styles = useS(IconButtonStyles);
const Button = tag ?? ReakitButton;
return (
<Button tabIndex={0} {...rest} className={s(styles, { iconButton: true }, className)}>
<UnstyledButton tabIndex={0} {...rest} className={s(styles, { iconButton: true }, className)}>
<div className={s(styles, { iconBox: true })}>
{img && <StaticImage className={s(styles, { staticImage: true })} icon={name} />}
{!img && <Icon className={s(styles, { icon: true })} name={name} viewBox={viewBox} />}
</div>
</Button>
</UnstyledButton>
);
});
@@ -1,34 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
.menuButton {
composes: theme-ripple from global;
}
.menuButton {
background: none;
border: none;
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;
}
@@ -1,202 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 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 React, { forwardRef, useLayoutEffect, useRef, useState } from 'react';
import { MenuButton, type MenuInitialState, useMenuState } from 'reakit';
import type { ExtractHTMLAttributes } from 'reakit-utils';
import { ErrorBoundary } from '../ErrorBoundary.js';
import { s } from '../s.js';
import { useCombinedRef } from '../useCombinedRef.js';
import { useObjectRef } from '../useObjectRef.js';
import { useS } from '../useS.js';
import style from './Menu.module.css';
import { MenuPanel } from './MenuPanel.js';
import { type IMenuState, MenuStateContext } from './MenuStateContext.js';
import type { IContextMenuPosition } from './useContextMenuPosition.js';
interface IMenuProps extends React.ButtonHTMLAttributes<any> {
contextMenuPosition?: IContextMenuPosition;
label: string;
items: React.ReactNode | (() => React.ReactNode);
menuRef?: React.RefObject<IMenuState | null>;
disclosure?: boolean;
placement?: MenuInitialState['placement'];
submenu?: boolean;
modal?: boolean;
visible?: boolean;
rtl?: boolean;
hasBindings?: boolean;
panelAvailable?: boolean;
getHasBindings?: () => boolean;
onVisibleSwitch?: (visible: boolean) => void;
}
/**
* @deprecated use the @dbeaver/ui-kit package instead
*/
export const Menu = observer<IMenuProps, HTMLButtonElement>(
forwardRef(function Menu(
{
contextMenuPosition,
label,
items,
menuRef,
disclosure,
children,
placement,
visible,
hasBindings,
panelAvailable,
getHasBindings,
onVisibleSwitch,
modal,
submenu,
rtl,
className,
...props
},
ref,
) {
const innerMenuButtonRef = useRef<HTMLButtonElement>(null);
const combinedRef = useCombinedRef(ref, innerMenuButtonRef);
const [relativePosition, setRelativePosition] = useState<{ x: number; y: number } | null>(null);
const menuButtonLinkRef = useRef<HTMLButtonElement>(null);
const menuPanelRef = useRef<HTMLDivElement>(null);
const propsRef = useObjectRef({ onVisibleSwitch, visible });
const menu = useMenuState({
modal,
placement,
visible,
rtl,
unstable_fixed: true,
});
const styles = useS(style);
if (menuRef) {
menuRef.current = menu;
}
let menuVisible = menu.visible;
if (panelAvailable === false) {
menuVisible = false;
}
useLayoutEffect(() => {
if (!menuVisible) {
setRelativePosition(null);
}
propsRef.onVisibleSwitch?.(menuVisible);
}, [menuVisible]);
useLayoutEffect(() => {
if (!contextMenuPosition?.position) {
return;
}
if (menuVisible) {
menu.hide();
return;
}
if (innerMenuButtonRef.current) {
menu.show();
const boxSize = innerMenuButtonRef.current.getBoundingClientRect();
setRelativePosition({
x: contextMenuPosition.position.x - boxSize.x,
y: contextMenuPosition.position.y - boxSize.y,
});
contextMenuPosition.position = null;
}
}, [contextMenuPosition?.position, menuVisible]);
useLayoutEffect(() => {
if (relativePosition) {
if (menuButtonLinkRef.current) {
menuButtonLinkRef.current.style.left = `${relativePosition.x}px`;
menuButtonLinkRef.current.style.top = `${relativePosition.y}px`;
}
}
});
const MenuButtonLink = MenuButton;
if (React.isValidElement(children) && disclosure) {
return (
<ErrorBoundary>
<MenuStateContext.Provider value={menu}>
<MenuButton
key={relativePosition ? 'link' : 'main'}
ref={combinedRef}
tabIndex={0}
className={s(styles, { menuButton: true }, className)}
{...menu}
visible={menuVisible}
{...props}
{...(children.props as any)}
>
{(disclosureProps: ExtractHTMLAttributes<any>) => React.cloneElement(children, { ...disclosureProps, ...(children.props as any) })}
</MenuButton>
<MenuPanel
ref={menuPanelRef}
label={label}
menu={menu}
rtl={rtl}
submenu={submenu}
panelAvailable={panelAvailable}
hasBindings={hasBindings}
getHasBindings={getHasBindings}
>
{items}
</MenuPanel>
{relativePosition && (
<MenuButtonLink ref={menuButtonLinkRef} className={s(styles, { menuButtonLink: true })} {...menu} visible={menuVisible} />
)}
</MenuStateContext.Provider>
</ErrorBoundary>
);
}
return (
<ErrorBoundary>
<MenuStateContext.Provider value={menu}>
<MenuButton
key={relativePosition ? 'link' : 'main'}
ref={combinedRef}
tabIndex={0}
className={s(styles, { menuButton: true }, className)}
{...menu}
visible={menuVisible}
{...props}
>
<div className={s(styles, { box: true }, className)}>{children}</div>
</MenuButton>
<MenuPanel
ref={menuPanelRef}
label={label}
menu={menu}
rtl={rtl}
submenu={submenu}
panelAvailable={panelAvailable}
hasBindings={hasBindings}
getHasBindings={getHasBindings}
>
{items}
</MenuPanel>
{relativePosition && (
<MenuButtonLink ref={menuButtonLinkRef} className={s(styles, { menuButtonLink: true })} {...menu} visible={menuVisible} />
)}
</MenuStateContext.Provider>
</ErrorBoundary>
);
}),
);
@@ -1,35 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
.iconButton {
overflow: hidden;
padding: 0;
margin: 0;
flex-shrink: 0;
}
.iconBox {
composes: theme-form-element-radius theme-text-primary theme-ripple from global;
box-sizing: border-box;
overflow: hidden;
padding: 4px !important;
margin: 2px !important;
flex-shrink: 0;
height: 24px !important;
display: flex;
cursor: pointer;
align-items: center;
gap: 2px;
user-select: none;
outline: none;
& .iconLabel {
text-transform: uppercase;
font-weight: 500;
}
}
@@ -1,38 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 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 { ButtonProps } from 'reakit';
import { IconButton } from '../IconButton.js';
import { s } from '../s.js';
import { useS } from '../useS.js';
import moduleStyles from './MenuBarSmallItem.module.css';
interface Props extends Omit<ButtonProps, 'style' | 'icon'> {
icon?: string;
viewBox?: string;
onClick?: React.MouseEventHandler<HTMLDivElement>;
}
export const MenuBarSmallItem: React.FC<React.PropsWithChildren<Props>> = function MenuBarSmallItem({
icon,
viewBox,
children,
className,
onClick,
...rest
}) {
const style = useS(moduleStyles);
// TODO: replace IconButton with StaticImage / Icon
// TODO: use button for icon-box (maybe)
return (
<div className={s(style, { iconBox: true }, className)} tabIndex={0} onClick={onClick}>
{icon && <IconButton className={s(style, { iconButton: true })} name={icon} viewBox={viewBox} {...rest} />}
{children && <div className={s(style, { iconLabel: true })}>{children}</div>}
</div>
);
};
@@ -1,24 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 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 { useTranslate } from '../localization/useTranslate.js';
import { MenuItem } from './MenuItem.js';
import { MenuItemElement } from './MenuItemElement.js';
export const MenuEmptyItem = observer(function MenuEmptyItem() {
const translate = useTranslate();
const label = translate('core_ui_empty');
return (
<MenuItem id="empty" aria-label={label} disabled>
<MenuItemElement label={label} />
</MenuItem>
);
});
@@ -1,30 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
.menuItem {
composes: theme-ripple from global;
composes: menuItem from './MenuItemElement.module.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;
}
}
@@ -1,55 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 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 React, { useContext } from 'react';
import { MenuItem as ReakitMenuItem, type CompositeItemOptions } from 'reakit';
import { s } from '../s.js';
import { useCombinedHandler } from '../useCombinedHandler.js';
import { useS } from '../useS.js';
import style from './MenuItem.module.css';
import { MenuStateContext } from './MenuStateContext.js';
import type { ReakitProxyComponent, ReakitProxyComponentOptions } from './ReakitProxyComponent.js';
export type MenuItemOptions = CompositeItemOptions & {
selected?: boolean;
close?: boolean;
};
/**
* @deprecated use the @dbeaver/ui-kit package instead
*/
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);
const handleClick = useCombinedHandler<[React.MouseEvent<HTMLButtonElement>]>(onClick, function handleClick() {
if (close) {
menu?.hide();
}
});
const MenuItem = ReakitMenuItem;
return (
<MenuItem
{...menu}
aria-selected={selected}
{...rest}
className={s(styles, { menuItem: true, hidden }, className)}
disabled={selected || rest.disabled}
style={{ pointerEvents: 'auto' }}
focusable
onClick={handleClick}
>
{children}
</MenuItem>
);
},
) as ReakitProxyComponent<'button', MenuItemOptions>;
@@ -1,34 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
.menu {
outline: none;
z-index: 999;
}
.menuBox {
composes: theme-background-surface theme-text-on-surface theme-group-element-radius theme-typography--body2 theme-elevation-z5 from global;
composes: menuBox from './MenuItemElement.module.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;
}
@@ -1,71 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 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 { Children, forwardRef } from 'react';
import { Menu, type MenuStateReturn } from 'reakit';
import { ErrorBoundary } from '../ErrorBoundary.js';
import { getComputed } from '../getComputed.js';
import { useTranslate } from '../localization/useTranslate.js';
import { s } from '../s.js';
import { useS } from '../useS.js';
import { MenuEmptyItem } from './MenuEmptyItem.js';
import style from './MenuPanel.module.css';
import styleMenuItemElement from './MenuItemElement.module.css';
export interface IMenuPanelProps {
label: string;
menu: MenuStateReturn; // from reakit useMenuState
panelAvailable?: boolean;
hasBindings?: boolean;
getHasBindings?: () => boolean;
children: React.ReactNode | (() => React.ReactNode);
rtl?: boolean;
submenu?: boolean;
className?: string;
}
/**
* @deprecated use the @dbeaver/ui-kit package instead
*/
export const MenuPanel = observer<IMenuPanelProps, HTMLDivElement>(
forwardRef(function MenuPanel({ label, menu, submenu, panelAvailable = true, rtl, getHasBindings, hasBindings, children, className }, ref) {
const translate = useTranslate();
const styles = useS(style, styleMenuItemElement);
const visible = menu.visible;
if (!visible) {
return null;
}
hasBindings = panelAvailable && (hasBindings || getComputed(() => getHasBindings?.()));
let renderedChildren: React.ReactNode = <></>;
if (panelAvailable) {
renderedChildren = typeof children === 'function' ? children() : children;
}
return (
<ErrorBoundary>
<Menu
ref={ref}
className={s(styles, { menu: true, modal: menu.modal, submenu }, className)}
{...menu}
aria-label={translate(label)}
visible={panelAvailable}
>
<div dir={rtl ? 'rtl' : undefined} data-s-has-bindings={hasBindings} className={s(styles, { menuBox: true })}>
{Children.count(renderedChildren) === 0 && <MenuEmptyItem />}
{renderedChildren}
</div>
</Menu>
</ErrorBoundary>
);
}),
);
@@ -1,13 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createContext } from 'react';
import type { MenuStateReturn } from 'reakit';
export type IMenuState = MenuStateReturn;
export const MenuStateContext = createContext<IMenuState | null>(null);
@@ -1,22 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 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 { As, PropsWithAs } from 'reakit-utils/types';
export type ReakitProxyPropsWithAs<O, T extends As> = PropsWithAs<O, T>;
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,
): React.ReactElement<any, any> | null;
(props: ReakitProxyPropsWithAs<O, T>, context?: any): React.ReactElement<any, any> | null;
};
-9
View File
@@ -58,17 +58,8 @@ export * from './ConnectionImageWithMask/ConnectionImageWithMask.js';
export { default as ConnectionImageWithMaskSvgStyles } from './ConnectionImageWithMask/ConnectionImageWithMaskSvg.module.css';
export * from './Menu/AriaKit/MenuAriaKit.js';
export * from './Menu/Menu.js';
export { default as MenuStyles } from './Menu/Menu.module.css';
export * from './Menu/MenuBarSmallItem.js';
export * from './Menu/MenuEmptyItem.js';
export * from './Menu/MenuItem.js';
export { default as MenuItemStyles } from './Menu/MenuItem.module.css';
export * from './Menu/MenuItemElement.js';
export { default as MenuItemElementStyles } from './Menu/MenuItemElement.module.css';
export * from './Menu/MenuPanel.js';
export { default as MenuPanelStyles } from './Menu/MenuPanel.module.css';
export * from './Menu/MenuStateContext.js';
export * from './Menu/useContextMenuPosition.js';
export * from './ObjectPropertyInfo/ObjectPropertyInfoForm/ObjectPropertyInfoFormLoader.js';
@@ -25,11 +25,11 @@
"@cloudbeaver/core-blocks": "workspace:*",
"@cloudbeaver/core-di": "workspace:*",
"@cloudbeaver/core-events": "workspace:*",
"@dbeaver/ui-kit": "workspace:^",
"mobx": "^6",
"mobx-react-lite": "^4",
"react": "^19",
"react-dom": "^19",
"reakit": "^1",
"tslib": "^2"
},
"devDependencies": {
@@ -5,12 +5,13 @@
* 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 { Portal } from 'reakit';
import { s, useS } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
import { Portal } from '@dbeaver/ui-kit';
import styles from './Notifications.module.css';
import { NotificationsItem } from './NotificationsItem/NotificationsItem.js';
@@ -7,6 +7,9 @@
"composite": true
},
"references": [
{
"path": "../../common-react/@dbeaver/ui-kit"
},
{
"path": "../core-blocks"
},
-1
View File
@@ -42,7 +42,6 @@
"react-dnd": "^16",
"react-dnd-html5-backend": "^16",
"react-dom": "^19",
"reakit": "^1",
"tslib": "^2"
},
"devDependencies": {
@@ -14,7 +14,7 @@ import { MenuButton, MenuProvider, Menu, type HovercardStoreState, useMenuStore,
import { RenderMenuItems } from './RenderMenuItems.js';
import { type IMenuContext, MenuContext } from './MenuContext.js';
export interface IContextMenuNewProps extends Omit<IContextMenuProps, 'placement'> {
export interface IContextMenuNewProps extends IContextMenuProps {
render?: React.ReactElement;
placement?: HovercardStoreState['placement'];
showOnHover?: boolean;
@@ -1,12 +1,12 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
* Copyright (C) 2020-2026 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 { ButtonHTMLAttributes } from 'react';
import type { MenuInitialState } from 'reakit';
import type { IContextMenuPosition } from '@cloudbeaver/core-blocks';
import type { IMenuData } from '@cloudbeaver/core-view';
@@ -21,7 +21,6 @@ export type ContextMenuRenderingChildren = (props: IContextMenuBaseProps) => Rea
export interface IContextMenuProps extends Omit<ButtonHTMLAttributes<any>, 'children'> {
contextMenuPosition?: IContextMenuPosition;
menu: IMenuData;
placement?: MenuInitialState['placement'];
visible?: boolean;
rtl?: boolean;
children?: React.ReactNode;
@@ -48,7 +48,6 @@
"mobx-react-lite": "^4",
"react": "^19",
"react-dom": "^19",
"reakit": "^1",
"tslib": "^2"
},
"devDependencies": {
@@ -52,7 +52,7 @@ export const SSHKeyUploader = observer<Props>(function SSHKeyUploader({ state, s
</Textarea>
<GroupItem>
<UploadArea disabled={disabled || readonly} reset onChange={handleKeyUpload}>
<Button className="tw:inline-flex" tag="div" disabled={disabled || readonly} variant="secondary">
<Button className="tw:w-max" tag="div" disabled={disabled || readonly} variant="secondary">
{translate('ui_file')}
</Button>
</UploadArea>
@@ -35,6 +35,7 @@
"@cloudbeaver/core-view": "workspace:*",
"@cloudbeaver/plugin-data-viewer": "workspace:*",
"@cloudbeaver/plugin-sql-editor": "workspace:*",
"@dbeaver/ui-kit": "workspace:*",
"mobx": "^6",
"mobx-react-lite": "^4",
"react": "^19",
@@ -1,13 +1,15 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2026 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 { IconButton, TableColumnValue, TableItem } from '@cloudbeaver/core-blocks';
import { Icon, TableColumnValue, TableItem, useTranslate } from '@cloudbeaver/core-blocks';
import { IconButton } from '@dbeaver/ui-kit';
interface Props {
id: string;
@@ -17,13 +19,17 @@ interface Props {
}
export const GroupingTableItem = observer<Props>(function GroupingTableItem({ id, name, className, onDelete }) {
const translate = useTranslate();
return (
<TableItem className={className} item={id} title={name}>
<TableColumnValue width="100%" flex>
{name}
</TableColumnValue>
<TableColumnValue>
<IconButton name="cross-bold" onClick={() => onDelete(id)} />
<IconButton size='small' aria-label={translate('ui_remove')} onClick={() => onDelete(id)}>
<Icon name='cross-bold' width={16} height={16} />
</IconButton>
</TableColumnValue>
</TableItem>
);
@@ -7,6 +7,9 @@
"composite": true
},
"references": [
{
"path": "../../common-react/@dbeaver/ui-kit"
},
{
"path": "../core-blocks"
},
@@ -1,10 +1,11 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2026 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 { useRef } from 'react';
@@ -17,6 +18,7 @@ import {
useTranslate,
} from '@cloudbeaver/core-blocks';
import { InlineEditor } from '@cloudbeaver/core-ui';
import { CompositeProvider, Popover, useCompositeStore } from '@dbeaver/ui-kit';
import type { ITableHeaderPlaceholderProps } from './TableHeaderService.js';
import styles from './TableWhereFilter.module.css';
@@ -30,10 +32,15 @@ export const TableWhereFilter: PlaceholderComponent<ITableHeaderPlaceholderProps
const state = useWhereFilter(model, resultIndex);
const data = useTableViewerHeaderData({ model, resultIndex });
const inputRef = useRef<HTMLInputElement>(null);
const store = useCompositeStore();
const autocompleteState = useInputAutocomplete(inputRef, {
separator: AUTOCOMPLETE_WORD_SEPARATOR,
sourceHints: data.hintProposals ?? [],
matchStrategy: 'fuzzy',
onNavigate: () => {
store.move(store.first());
},
});
if (!state.supported) {
@@ -55,26 +62,30 @@ export const TableWhereFilter: PlaceholderComponent<ITableHeaderPlaceholderProps
return (
<Container className={styles['imbeddedEditor']}>
<InlineEditor
ref={inputRef}
className={styles['inlineEditor']}
name="data_where"
value={state.filter}
placeholder={translate(state.constraints?.supported ? 'table_header_sql_expression' : 'table_header_sql_expression_not_supported')}
controlsPosition="inside"
edited={!!state.filter}
disableSave={!state.applicableFilter}
disabled={state.disabled}
simple
onSave={onSave}
onChange={state.set}
/>
<InputAutocompletionMenu
position={autocompleteState.position}
proposals={autocompleteState.proposals}
menuRef={autocompleteState.menuRef}
onSelect={handleSelect}
/>
<CompositeProvider store={store}>
<Popover placement='bottom-start' open={autocompleteState.proposals.length > 0}>
<Popover.Anchor>
<InlineEditor
ref={inputRef}
className={styles['inlineEditor']}
name="data_where"
value={state.filter}
placeholder={translate(state.constraints?.supported ? 'table_header_sql_expression' : 'table_header_sql_expression_not_supported')}
controlsPosition="inside"
edited={!!state.filter}
disableSave={!state.applicableFilter}
disabled={state.disabled}
simple
onSave={onSave}
onChange={state.set}
/>
</Popover.Anchor>
<InputAutocompletionMenu
proposals={autocompleteState.proposals}
onSelect={handleSelect}
/>
</Popover>
</CompositeProvider>
</Container>
);
});
@@ -34,6 +34,7 @@
"@cloudbeaver/core-utils": "workspace:*",
"@cloudbeaver/core-view": "workspace:*",
"@cloudbeaver/plugin-tools-panel": "workspace:*",
"@dbeaver/ui-kit": "workspace:^",
"mobx": "^6",
"mobx-react-lite": "^4",
"react": "^19",
@@ -26,10 +26,6 @@
padding: 4px;
}
.button {
flex: unset;
}
.type {
composes: theme-typography--body2 from global;
margin: 0;
@@ -8,7 +8,8 @@
import { observer } from 'mobx-react-lite';
import { useCallback } from 'react';
import { Container, MenuBarSmallItem, s, Textarea, useClipboard, useS, useTranslate } from '@cloudbeaver/core-blocks';
import { Container, s, Textarea, useClipboard, useS, useTranslate } from '@cloudbeaver/core-blocks';
import { Button } from '@dbeaver/ui-kit';
import type { ILogEntry } from './ILogEntry.js';
import classes from './LogViewerInfoPanel.module.css';
@@ -34,12 +35,25 @@ export const LogViewerInfoPanel = observer<Props>(function LogViewerInfoPanel({
<Container className={s(styles, { panelWrapper: true }, className)} vertical>
<Container className={s(styles, { buttons: true })} noGrow noWrap>
<Container keepSize noWrap>
<MenuBarSmallItem className={s(styles, { button: true })} title={translate('ui_copy_to_clipboard')} onClick={copyMessage}>
<Button
className='tw:flex-none'
variant='ghost'
size='small'
title={translate('ui_copy_to_clipboard')}
onClick={copyMessage}
>
{translate('ui_copy_to_clipboard')}
</MenuBarSmallItem>
<MenuBarSmallItem className={s(styles, { button: true })} title={translate('ui_close')} onClick={onClose}>
</Button>
<Button
className='tw:flex-none'
variant='ghost'
size='small'
title={translate('ui_close')}
onClick={onClose}
>
{translate('ui_close')}
</MenuBarSmallItem>
</Button>
</Container>
</Container>
<div className={s(styles, { contentWrapper: true })}>
@@ -42,11 +42,4 @@
.timestamp {
width: 116px;
min-width: 116px;
}
.clearButton {
position: absolute !important;
top: 4px;
right: 16px;
z-index: 100;
}
}
@@ -7,7 +7,8 @@
*/
import { observer } from 'mobx-react-lite';
import { Container, MenuBarSmallItem, s, Table, TableBody, TableColumnHeader, TableHeader, useS, useTranslate } from '@cloudbeaver/core-blocks';
import { Container, Icon, s, Table, TableBody, TableColumnHeader, TableHeader, useS, useTranslate } from '@cloudbeaver/core-blocks';
import { Button, ButtonIcon } from '@dbeaver/ui-kit';
import type { ILogEntry } from './ILogEntry.js';
import { LogEntry } from './LogEntry.js';
@@ -26,15 +27,18 @@ export const LogViewerTable = observer<Props>(function LogViewerTable({ items, s
return (
<Container className={s(style, { wrapper: true }, className)}>
<MenuBarSmallItem
className={s(style, { clearButton: true })}
icon="trash"
viewBox="0 0 24 24"
<Button
className='tw:max-w-max tw:!absolute tw:top-1 tw:right-4 tw:z-[100]'
variant='ghost'
size='small'
title={translate('plugin_log_viewer_clear_log')}
onClick={onClear}
>
<ButtonIcon placement="start">
<Icon className='tw:w-4' name="trash" viewBox="0 0 24 24" />
</ButtonIcon>
{translate('ui_clear')}
</MenuBarSmallItem>
</Button>
<Container className={s(style, { tableWrapper: true })} overflow>
<Table className={s(style, { table: true })}>
<TableHeader fixed>
@@ -10,6 +10,9 @@
{
"path": "../../common-react/@dbeaver/react-tests"
},
{
"path": "../../common-react/@dbeaver/ui-kit"
},
{
"path": "../../common-typescript/@dbeaver/cli"
},
@@ -46,7 +46,6 @@
"mobx-react-lite": "^4",
"react": "^19",
"react-dom": "^19",
"reakit": "^1",
"tslib": "^2"
},
"devDependencies": {
@@ -1,11 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createContext } from 'react';
import type { TabStateReturn } from 'reakit';
export const FolderContext = createContext<TabStateReturn | undefined>(undefined);
@@ -8,7 +8,7 @@
import { observer } from 'mobx-react-lite';
import { useState } from 'react';
import { MenuBarSmallItem, useExecutor, useS, useTranslate } from '@cloudbeaver/core-blocks';
import { Button, useExecutor, useS, useTranslate } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
import { DATA_CONTEXT_NAV_NODE, getNodesFromContext, NavNodeManagerService } from '@cloudbeaver/core-navigation-tree';
@@ -129,14 +129,24 @@ export const SQLCodeEditorPanel: TabContainerPanelComponent<ISqlEditorModeProps>
{data.isIncomingChanges && (
<>
<ReactCodemirrorPanel className={styles['reactCodemirrorPanel']} top>
<MenuBarSmallItem title={translate('plugin_sql_editor_new_merge_conflict_keep_current_tooltip')} onClick={keepCurrent}>
<Button
variant='ghost'
size='small'
title={translate('plugin_sql_editor_new_merge_conflict_keep_current_tooltip')}
onClick={keepCurrent}
>
{translate('plugin_sql_editor_new_merge_conflict_keep_current_label')}
</MenuBarSmallItem>
</Button>
</ReactCodemirrorPanel>
<ReactCodemirrorPanel className={styles['reactCodemirrorPanel']} top incomingView>
<MenuBarSmallItem title={translate('plugin_sql_editor_new_merge_conflict_accept_incoming_tooltip')} onClick={applyIncoming}>
<Button
variant='ghost'
size='small'
title={translate('plugin_sql_editor_new_merge_conflict_accept_incoming_tooltip')}
onClick={applyIncoming}
>
{translate('plugin_sql_editor_new_merge_conflict_accept_incoming_label')}
</MenuBarSmallItem>
</Button>
</ReactCodemirrorPanel>
</>
)}
+3 -70
View File
@@ -1482,8 +1482,6 @@ __metadata:
react-dom: "npm:^19"
react-hotkeys-hook: "npm:5.1.0"
react-minisearch: "npm:^7"
reakit: "npm:^1"
reakit-utils: "npm:^0"
rimraf: "npm:^6"
subscript: "npm:^9"
tslib: "npm:^2"
@@ -1826,12 +1824,12 @@ __metadata:
"@cloudbeaver/core-di": "workspace:*"
"@cloudbeaver/core-events": "workspace:*"
"@cloudbeaver/tsconfig": "workspace:*"
"@dbeaver/ui-kit": "workspace:^"
"@types/react": "npm:^19"
mobx: "npm:^6"
mobx-react-lite: "npm:^4"
react: "npm:^19"
react-dom: "npm:^19"
reakit: "npm:^1"
rimraf: "npm:^6"
tslib: "npm:^2"
typescript: "npm:^5"
@@ -2246,7 +2244,6 @@ __metadata:
react-dnd: "npm:^16"
react-dnd-html5-backend: "npm:^16"
react-dom: "npm:^19"
reakit: "npm:^1"
rimraf: "npm:^6"
tslib: "npm:^2"
typescript: "npm:^5"
@@ -2519,7 +2516,6 @@ __metadata:
mobx-react-lite: "npm:^4"
react: "npm:^19"
react-dom: "npm:^19"
reakit: "npm:^1"
rimraf: "npm:^6"
tslib: "npm:^2"
typescript: "npm:^5"
@@ -3076,6 +3072,7 @@ __metadata:
"@cloudbeaver/plugin-data-viewer": "workspace:*"
"@cloudbeaver/plugin-sql-editor": "workspace:*"
"@cloudbeaver/tsconfig": "workspace:*"
"@dbeaver/ui-kit": "workspace:*"
"@types/react": "npm:^19"
mobx: "npm:^6"
mobx-react-lite: "npm:^4"
@@ -3433,6 +3430,7 @@ __metadata:
"@cloudbeaver/tsconfig": "workspace:*"
"@dbeaver/cli": "workspace:*"
"@dbeaver/react-tests": "workspace:*"
"@dbeaver/ui-kit": "workspace:^"
"@types/react": "npm:^19"
mobx: "npm:^6"
mobx-react-lite: "npm:^4"
@@ -3636,7 +3634,6 @@ __metadata:
mobx-react-lite: "npm:^4"
react: "npm:^19"
react-dom: "npm:^19"
reakit: "npm:^1"
rimraf: "npm:^6"
tslib: "npm:^2"
typescript: "npm:^5"
@@ -7460,13 +7457,6 @@ __metadata:
languageName: node
linkType: hard
"@popperjs/core@npm:^2.5.4":
version: 2.11.8
resolution: "@popperjs/core@npm:2.11.8"
checksum: 10c0/4681e682abc006d25eb380d0cf3efc7557043f53b6aea7a5057d0d1e7df849a00e281cd8ea79c902a35a414d7919621fc2ba293ecec05f413598e0b23d5a1e63
languageName: node
linkType: hard
"@react-dnd/asap@npm:^5.0.1":
version: 5.0.2
resolution: "@react-dnd/asap@npm:5.0.2"
@@ -10058,13 +10048,6 @@ __metadata:
languageName: node
linkType: hard
"body-scroll-lock@npm:^3.1.5":
version: 3.1.5
resolution: "body-scroll-lock@npm:3.1.5"
checksum: 10c0/657c948d64ffe17d2c89958f57c2c636203cf5216e3ba33ad76604bf1b5144fd8f4b5d832d09105f8782d5b8bc96f38780331ec55f3af5f27694dff38a0cc9d7
languageName: node
linkType: hard
"boolbase@npm:^1.0.0":
version: 1.0.0
resolution: "boolbase@npm:1.0.0"
@@ -10570,7 +10553,6 @@ __metadata:
prettier: "npm:^3.6.2"
react: "npm:^19"
react-dom: "npm:^19"
reakit: "npm:^1"
rimraf: "npm:^6"
typescript: "npm:^5.8.3"
typescript-plugin-css-modules: "npm:^5"
@@ -17066,55 +17048,6 @@ __metadata:
languageName: node
linkType: hard
"reakit-system@npm:^0.15.2":
version: 0.15.2
resolution: "reakit-system@npm:0.15.2"
dependencies:
reakit-utils: "npm:^0.15.2"
peerDependencies:
react: ^16.8.0 || ^17.0.0
react-dom: ^16.8.0 || ^17.0.0
checksum: 10c0/bd97e297b972e205656dd99a6955ed263d5102ab24212e55b90a86dd9a4eac056b63ba882af65705036922e9cb599d5903318d1202086a3e4ee45b5cc794d44e
languageName: node
linkType: hard
"reakit-utils@npm:^0, reakit-utils@npm:^0.15.2":
version: 0.15.2
resolution: "reakit-utils@npm:0.15.2"
peerDependencies:
react: ^16.8.0 || ^17.0.0
react-dom: ^16.8.0 || ^17.0.0
checksum: 10c0/258654f9510fb7622fc03f81a63021721ace6c424bdff47a8f79e3b974501ef6b1b44fdb78ab01ed1a75d3fd6867d100e97ef74138c0d848c2aa5738477bf853
languageName: node
linkType: hard
"reakit-warning@npm:^0.6.2":
version: 0.6.2
resolution: "reakit-warning@npm:0.6.2"
dependencies:
reakit-utils: "npm:^0.15.2"
peerDependencies:
react: ^16.8.0 || ^17.0.0
checksum: 10c0/e93856e9dd4b56e7cd36cd654cb5688eb05fc8f75e3d89069352bc61ab6f6cf501892405dd1612442e45880562048af5c17b3e771fd6af79348bc90a20f8d930
languageName: node
linkType: hard
"reakit@npm:^1":
version: 1.3.11
resolution: "reakit@npm:1.3.11"
dependencies:
"@popperjs/core": "npm:^2.5.4"
body-scroll-lock: "npm:^3.1.5"
reakit-system: "npm:^0.15.2"
reakit-utils: "npm:^0.15.2"
reakit-warning: "npm:^0.6.2"
peerDependencies:
react: ^16.8.0 || ^17.0.0
react-dom: ^16.8.0 || ^17.0.0
checksum: 10c0/ce5972b1242bd0704840c77faa6f687cc3bd0e66ac5f6fe84328408d2e624a4532c4bb1d82b6b6dcbc876dc0700c472b420acebf80603d59115a810ae8916e12
languageName: node
linkType: hard
"recast@npm:^0.23.5":
version: 0.23.11
resolution: "recast@npm:0.23.11"