From fab9d1dd43353c93c5e73aa4e1535c57775f036a Mon Sep 17 00:00:00 2001 From: alex <48489896+devnaumov@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:18:35 +0100 Subject: [PATCH 1/4] 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> --- .../@dbeaver/ui-kit/src/Button/Button.tsx | 6 +- .../@dbeaver/ui-kit/src/Button/_base.css | 2 +- .../ui-kit/src/Composite/Composite.tsx | 32 +++ .../@dbeaver/ui-kit/src/Menu/Menu.tsx | 4 + .../@dbeaver/ui-kit/src/Popover/Popover.tsx | 9 +- .../@dbeaver/ui-kit/src/Portal/Portal.tsx | 15 ++ .../common-react/@dbeaver/ui-kit/src/index.ts | 15 +- webapp/package.json | 1 - webapp/packages/core-blocks/package.json | 2 - webapp/packages/core-blocks/src/Button.css | 5 +- .../InputAutocompletionMenu.module.css | 11 - .../FormControls/InputAutocompletionMenu.tsx | 87 +++----- .../InputFileTextContent.module.css | 9 - .../src/FormControls/InputFileTextContent.tsx | 18 +- .../src/FormControls/InputFiles.tsx | 2 +- .../core-blocks/src/FormControls/Textarea.tsx | 2 +- .../src/FormControls/useInputAutocomplete.ts | 17 +- .../packages/core-blocks/src/IconButton.tsx | 16 +- .../core-blocks/src/Menu/Menu.module.css | 34 --- webapp/packages/core-blocks/src/Menu/Menu.tsx | 202 ------------------ .../src/Menu/MenuBarSmallItem.module.css | 35 --- .../core-blocks/src/Menu/MenuBarSmallItem.tsx | 38 ---- .../core-blocks/src/Menu/MenuEmptyItem.tsx | 24 --- .../core-blocks/src/Menu/MenuItem.module.css | 30 --- .../core-blocks/src/Menu/MenuItem.tsx | 55 ----- .../core-blocks/src/Menu/MenuPanel.module.css | 34 --- .../core-blocks/src/Menu/MenuPanel.tsx | 71 ------ .../core-blocks/src/Menu/MenuStateContext.ts | 13 -- .../src/Menu/ReakitProxyComponent.ts | 22 -- webapp/packages/core-blocks/src/index.ts | 9 - .../packages/core-notifications/package.json | 2 +- .../core-notifications/src/Notifications.tsx | 3 +- .../packages/core-notifications/tsconfig.json | 3 + webapp/packages/core-ui/package.json | 1 - .../core-ui/src/ContextMenu/ContextMenu.tsx | 2 +- .../src/ContextMenu/IContextMenuProps.ts | 5 +- .../package.json | 1 - .../src/ConnectionForm/SSH/SSHKeyUploader.tsx | 2 +- .../package.json | 1 + .../GroupingTableItem.tsx | 12 +- .../tsconfig.json | 3 + .../TableHeader/TableWhereFilter.tsx | 53 +++-- .../packages/plugin-log-viewer/package.json | 1 + .../LogViewer/LogViewerInfoPanel.module.css | 4 - .../src/LogViewer/LogViewerInfoPanel.tsx | 24 ++- .../src/LogViewer/LogViewerTable.module.css | 9 +- .../src/LogViewer/LogViewerTable.tsx | 16 +- .../packages/plugin-log-viewer/tsconfig.json | 3 + .../plugin-object-viewer/package.json | 1 - .../src/ObjectPropertiesPage/FolderContext.ts | 11 - .../SQLCodeEditorPanel/SQLCodeEditorPanel.tsx | 20 +- webapp/yarn.lock | 73 +------ 52 files changed, 244 insertions(+), 826 deletions(-) create mode 100644 webapp/common-react/@dbeaver/ui-kit/src/Composite/Composite.tsx create mode 100644 webapp/common-react/@dbeaver/ui-kit/src/Portal/Portal.tsx delete mode 100644 webapp/packages/core-blocks/src/FormControls/InputAutocompletionMenu.module.css delete mode 100644 webapp/packages/core-blocks/src/Menu/Menu.module.css delete mode 100644 webapp/packages/core-blocks/src/Menu/Menu.tsx delete mode 100644 webapp/packages/core-blocks/src/Menu/MenuBarSmallItem.module.css delete mode 100644 webapp/packages/core-blocks/src/Menu/MenuBarSmallItem.tsx delete mode 100644 webapp/packages/core-blocks/src/Menu/MenuEmptyItem.tsx delete mode 100644 webapp/packages/core-blocks/src/Menu/MenuItem.module.css delete mode 100644 webapp/packages/core-blocks/src/Menu/MenuItem.tsx delete mode 100644 webapp/packages/core-blocks/src/Menu/MenuPanel.module.css delete mode 100644 webapp/packages/core-blocks/src/Menu/MenuPanel.tsx delete mode 100644 webapp/packages/core-blocks/src/Menu/MenuStateContext.ts delete mode 100644 webapp/packages/core-blocks/src/Menu/ReakitProxyComponent.ts delete mode 100644 webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/FolderContext.ts diff --git a/webapp/common-react/@dbeaver/ui-kit/src/Button/Button.tsx b/webapp/common-react/@dbeaver/ui-kit/src/Button/Button.tsx index fd94ee9e73..e942dc5c54 100644 --- a/webapp/common-react/@dbeaver/ui-kit/src/Button/Button.tsx +++ b/webapp/common-react/@dbeaver/ui-kit/src/Button/Button.tsx @@ -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 }; \ No newline at end of file diff --git a/webapp/common-react/@dbeaver/ui-kit/src/Button/_base.css b/webapp/common-react/@dbeaver/ui-kit/src/Button/_base.css index 23a0f00b2f..831cb41fc1 100644 --- a/webapp/common-react/@dbeaver/ui-kit/src/Button/_base.css +++ b/webapp/common-react/@dbeaver/ui-kit/src/Button/_base.css @@ -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); diff --git a/webapp/common-react/@dbeaver/ui-kit/src/Composite/Composite.tsx b/webapp/common-react/@dbeaver/ui-kit/src/Composite/Composite.tsx new file mode 100644 index 0000000000..d051a16d5b --- /dev/null +++ b/webapp/common-react/@dbeaver/ui-kit/src/Composite/Composite.tsx @@ -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 {children}; +} + +export function Composite({ children, ...props }: CompositeProps) { + return {children}; +} + +export function CompositeItem({ children, ...props }: CompositeItemProps) { + return {children}; +} + +export { useCompositeStore, type CompositeProps, type CompositeItemProps, type CompositeProviderProps, type CompositeStore }; \ No newline at end of file diff --git a/webapp/common-react/@dbeaver/ui-kit/src/Menu/Menu.tsx b/webapp/common-react/@dbeaver/ui-kit/src/Menu/Menu.tsx index ab1838aacd..aff7ab7292 100644 --- a/webapp/common-react/@dbeaver/ui-kit/src/Menu/Menu.tsx +++ b/webapp/common-react/@dbeaver/ui-kit/src/Menu/Menu.tsx @@ -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; diff --git a/webapp/common-react/@dbeaver/ui-kit/src/Popover/Popover.tsx b/webapp/common-react/@dbeaver/ui-kit/src/Popover/Popover.tsx index c5e70bb742..94248eaa6a 100644 --- a/webapp/common-react/@dbeaver/ui-kit/src/Popover/Popover.tsx +++ b/webapp/common-react/@dbeaver/ui-kit/src/Popover/Popover.tsx @@ -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 {children}; +} + export function Popover({ children, ...props }: Ariakit.PopoverProviderProps) { return {children}; } @@ -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'; \ No newline at end of file diff --git a/webapp/common-react/@dbeaver/ui-kit/src/Portal/Portal.tsx b/webapp/common-react/@dbeaver/ui-kit/src/Portal/Portal.tsx new file mode 100644 index 0000000000..7da9521ae4 --- /dev/null +++ b/webapp/common-react/@dbeaver/ui-kit/src/Portal/Portal.tsx @@ -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 {children}; +} + +export type { PortalProps }; \ No newline at end of file diff --git a/webapp/common-react/@dbeaver/ui-kit/src/index.ts b/webapp/common-react/@dbeaver/ui-kit/src/index.ts index 5596debaf8..8c6d0485d6 100644 --- a/webapp/common-react/@dbeaver/ui-kit/src/index.ts +++ b/webapp/common-react/@dbeaver/ui-kit/src/index.ts @@ -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'; diff --git a/webapp/package.json b/webapp/package.json index 2b49086829..cd3c47d47e 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -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", diff --git a/webapp/packages/core-blocks/package.json b/webapp/packages/core-blocks/package.json index a00aa4d2cb..4d8f460cd6 100644 --- a/webapp/packages/core-blocks/package.json +++ b/webapp/packages/core-blocks/package.json @@ -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" }, diff --git a/webapp/packages/core-blocks/src/Button.css b/webapp/packages/core-blocks/src/Button.css index d717a9d86b..803c66f177 100644 --- a/webapp/packages/core-blocks/src/Button.css +++ b/webapp/packages/core-blocks/src/Button.css @@ -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; diff --git a/webapp/packages/core-blocks/src/FormControls/InputAutocompletionMenu.module.css b/webapp/packages/core-blocks/src/FormControls/InputAutocompletionMenu.module.css deleted file mode 100644 index d06eb71803..0000000000 --- a/webapp/packages/core-blocks/src/FormControls/InputAutocompletionMenu.module.css +++ /dev/null @@ -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; -} diff --git a/webapp/packages/core-blocks/src/FormControls/InputAutocompletionMenu.tsx b/webapp/packages/core-blocks/src/FormControls/InputAutocompletionMenu.tsx index d276efb887..4352e46c65 100644 --- a/webapp/packages/core-blocks/src/FormControls/InputAutocompletionMenu.tsx +++ b/webapp/packages/core-blocks/src/FormControls/InputAutocompletionMenu.tsx @@ -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; className?: string; onSelect?: (proposal: InputAutocompleteProposal) => void; } -export const InputAutocompletionMenu = observer(function InputAutocompletionMenu({ - position, - className, - menuRef, +export const InputAutocompletionMenu = observer(forwardRef(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 ( - 0} - panelAvailable={proposals.length > 0} - className={s(styles, { menu: true }, className)} - menuRef={menuRef} - label="Autocompletion" - items={proposals.map(proposal => ( - handleSelect(proposal)} - > - {proposal.icon && ( -
- -
- )} - {proposal.displayString} -
- ))} - modal - /> + + + {proposals.map(proposal => ( + onSelect?.(proposal)}> + {proposal.icon && ( +
+ +
+ )} + {proposal.displayString} +
+ ))} +
+
); -}); +})); diff --git a/webapp/packages/core-blocks/src/FormControls/InputFileTextContent.module.css b/webapp/packages/core-blocks/src/FormControls/InputFileTextContent.module.css index 2d866a5806..8cd1e7c66c 100644 --- a/webapp/packages/core-blocks/src/FormControls/InputFileTextContent.module.css +++ b/webapp/packages/core-blocks/src/FormControls/InputFileTextContent.module.css @@ -18,12 +18,3 @@ align-items: center; gap: 2px; } -.iconButton { - width: 12px; - height: 12px; - flex-shrink: 0; - cursor: pointer; - &:hover { - opacity: 0.5; - } -} diff --git a/webapp/packages/core-blocks/src/FormControls/InputFileTextContent.tsx b/webapp/packages/core-blocks/src/FormControls/InputFileTextContent.tsx index d7108bcdc4..06cbc4a78a 100644 --- a/webapp/packages/core-blocks/src/FormControls/InputFileTextContent.tsx +++ b/webapp/packages/core-blocks/src/FormControls/InputFileTextContent.tsx @@ -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} - {description} - {(selected || saved) && } + {(selected || saved) && ( + + + + )} ); diff --git a/webapp/packages/core-blocks/src/FormControls/InputFiles.tsx b/webapp/packages/core-blocks/src/FormControls/InputFiles.tsx index 9940effe85..3342cafc53 100644 --- a/webapp/packages/core-blocks/src/FormControls/InputFiles.tsx +++ b/webapp/packages/core-blocks/src/FormControls/InputFiles.tsx @@ -184,7 +184,7 @@ export const InputFiles: InputFilesType = observer( diff --git a/webapp/packages/core-blocks/src/FormControls/useInputAutocomplete.ts b/webapp/packages/core-blocks/src/FormControls/useInputAutocomplete.ts index 1bd33d93e1..7ca873b2ee 100644 --- a/webapp/packages/core-blocks/src/FormControls/useInputAutocomplete.ts +++ b/webapp/packages/core-blocks/src/FormControls/useInputAutocomplete.ts @@ -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; } const DEFAULT_SEPARATOR = ' '; @@ -48,7 +48,7 @@ const CONTEXT_INPUT_OFFSET_Y = 3; export const useInputAutocomplete = ( inputRef: RefObject, - { sourceHints, separator = DEFAULT_SEPARATOR, matchStrategy = 'contains', predicate }: InputAutocompleteOptions, + { sourceHints, separator = DEFAULT_SEPARATOR, matchStrategy = 'contains', onNavigate, predicate }: InputAutocompleteOptions, ): Readonly => { const search = useSearch({ sourceHints, @@ -56,7 +56,7 @@ export const useInputAutocomplete = ( matchStrategy, predicate, }); - const menuRef = useRef(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(); diff --git a/webapp/packages/core-blocks/src/IconButton.tsx b/webapp/packages/core-blocks/src/IconButton.tsx index 0028a8ae22..e5c70ccc68 100644 --- a/webapp/packages/core-blocks/src/IconButton.tsx +++ b/webapp/packages/core-blocks/src/IconButton.tsx @@ -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 = observer(function IconButton({ tag, name, img, viewBox, className, ...rest }) { +export const IconButton: React.FC = observer(function IconButton({ name, img, viewBox, className, ...rest }) { const styles = useS(IconButtonStyles); - const Button = tag ?? ReakitButton; - return ( - + ); }); diff --git a/webapp/packages/core-blocks/src/Menu/Menu.module.css b/webapp/packages/core-blocks/src/Menu/Menu.module.css deleted file mode 100644 index 63a2520c16..0000000000 --- a/webapp/packages/core-blocks/src/Menu/Menu.module.css +++ /dev/null @@ -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; -} diff --git a/webapp/packages/core-blocks/src/Menu/Menu.tsx b/webapp/packages/core-blocks/src/Menu/Menu.tsx deleted file mode 100644 index 3467f5d984..0000000000 --- a/webapp/packages/core-blocks/src/Menu/Menu.tsx +++ /dev/null @@ -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 { - contextMenuPosition?: IContextMenuPosition; - label: string; - items: React.ReactNode | (() => React.ReactNode); - menuRef?: React.RefObject; - 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( - forwardRef(function Menu( - { - contextMenuPosition, - label, - items, - menuRef, - disclosure, - children, - placement, - visible, - hasBindings, - panelAvailable, - getHasBindings, - onVisibleSwitch, - modal, - submenu, - rtl, - className, - ...props - }, - ref, - ) { - const innerMenuButtonRef = useRef(null); - const combinedRef = useCombinedRef(ref, innerMenuButtonRef); - const [relativePosition, setRelativePosition] = useState<{ x: number; y: number } | null>(null); - const menuButtonLinkRef = useRef(null); - const menuPanelRef = useRef(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 ( - - - - {(disclosureProps: ExtractHTMLAttributes) => React.cloneElement(children, { ...disclosureProps, ...(children.props as any) })} - - - {items} - - {relativePosition && ( - - )} - - - ); - } - - return ( - - - -
{children}
-
- - {items} - - {relativePosition && ( - - )} -
-
- ); - }), -); diff --git a/webapp/packages/core-blocks/src/Menu/MenuBarSmallItem.module.css b/webapp/packages/core-blocks/src/Menu/MenuBarSmallItem.module.css deleted file mode 100644 index 983b66aec3..0000000000 --- a/webapp/packages/core-blocks/src/Menu/MenuBarSmallItem.module.css +++ /dev/null @@ -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; - } -} diff --git a/webapp/packages/core-blocks/src/Menu/MenuBarSmallItem.tsx b/webapp/packages/core-blocks/src/Menu/MenuBarSmallItem.tsx deleted file mode 100644 index 44234d1fbe..0000000000 --- a/webapp/packages/core-blocks/src/Menu/MenuBarSmallItem.tsx +++ /dev/null @@ -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 { - icon?: string; - viewBox?: string; - onClick?: React.MouseEventHandler; -} - -export const MenuBarSmallItem: React.FC> = 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 ( -
- {icon && } - {children &&
{children}
} -
- ); -}; diff --git a/webapp/packages/core-blocks/src/Menu/MenuEmptyItem.tsx b/webapp/packages/core-blocks/src/Menu/MenuEmptyItem.tsx deleted file mode 100644 index 3d46af0a2a..0000000000 --- a/webapp/packages/core-blocks/src/Menu/MenuEmptyItem.tsx +++ /dev/null @@ -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 ( - - - - ); -}); diff --git a/webapp/packages/core-blocks/src/Menu/MenuItem.module.css b/webapp/packages/core-blocks/src/Menu/MenuItem.module.css deleted file mode 100644 index d3f2ca38fd..0000000000 --- a/webapp/packages/core-blocks/src/Menu/MenuItem.module.css +++ /dev/null @@ -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; - } -} diff --git a/webapp/packages/core-blocks/src/Menu/MenuItem.tsx b/webapp/packages/core-blocks/src/Menu/MenuItem.tsx deleted file mode 100644 index 7c6f8d6859..0000000000 --- a/webapp/packages/core-blocks/src/Menu/MenuItem.tsx +++ /dev/null @@ -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>( - function MenuItem({ children, hidden, selected, close, onClick, className, ...rest }) { - const menu = useContext(MenuStateContext); - const styles = useS(style); - - const handleClick = useCombinedHandler<[React.MouseEvent]>(onClick, function handleClick() { - if (close) { - menu?.hide(); - } - }); - - const MenuItem = ReakitMenuItem; - - return ( - - ); - }, -) as ReakitProxyComponent<'button', MenuItemOptions>; diff --git a/webapp/packages/core-blocks/src/Menu/MenuPanel.module.css b/webapp/packages/core-blocks/src/Menu/MenuPanel.module.css deleted file mode 100644 index 1880d7f0ab..0000000000 --- a/webapp/packages/core-blocks/src/Menu/MenuPanel.module.css +++ /dev/null @@ -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; -} diff --git a/webapp/packages/core-blocks/src/Menu/MenuPanel.tsx b/webapp/packages/core-blocks/src/Menu/MenuPanel.tsx deleted file mode 100644 index 404469be68..0000000000 --- a/webapp/packages/core-blocks/src/Menu/MenuPanel.tsx +++ /dev/null @@ -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( - 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 ( - - -
- {Children.count(renderedChildren) === 0 && } - {renderedChildren} -
-
-
- ); - }), -); diff --git a/webapp/packages/core-blocks/src/Menu/MenuStateContext.ts b/webapp/packages/core-blocks/src/Menu/MenuStateContext.ts deleted file mode 100644 index 8b3280b6b3..0000000000 --- a/webapp/packages/core-blocks/src/Menu/MenuStateContext.ts +++ /dev/null @@ -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(null); diff --git a/webapp/packages/core-blocks/src/Menu/ReakitProxyComponent.ts b/webapp/packages/core-blocks/src/Menu/ReakitProxyComponent.ts deleted file mode 100644 index 9e58300060..0000000000 --- a/webapp/packages/core-blocks/src/Menu/ReakitProxyComponent.ts +++ /dev/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 = PropsWithAs; - -export type ReakitProxyComponentOptions = ReakitProxyPropsWithAs; - -export type ReakitProxyComponent = { - ( - props: ReakitProxyPropsWithAs & { - as: TT; - }, - context?: any, - ): React.ReactElement | null; - (props: ReakitProxyPropsWithAs, context?: any): React.ReactElement | null; -}; diff --git a/webapp/packages/core-blocks/src/index.ts b/webapp/packages/core-blocks/src/index.ts index a94c34875f..7fcad9c718 100644 --- a/webapp/packages/core-blocks/src/index.ts +++ b/webapp/packages/core-blocks/src/index.ts @@ -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'; diff --git a/webapp/packages/core-notifications/package.json b/webapp/packages/core-notifications/package.json index 37c9e02212..0f20a7aa88 100644 --- a/webapp/packages/core-notifications/package.json +++ b/webapp/packages/core-notifications/package.json @@ -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": { diff --git a/webapp/packages/core-notifications/src/Notifications.tsx b/webapp/packages/core-notifications/src/Notifications.tsx index 0730b37924..8a36bc2f98 100644 --- a/webapp/packages/core-notifications/src/Notifications.tsx +++ b/webapp/packages/core-notifications/src/Notifications.tsx @@ -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'; diff --git a/webapp/packages/core-notifications/tsconfig.json b/webapp/packages/core-notifications/tsconfig.json index 40adf4cbe0..ca5c074904 100644 --- a/webapp/packages/core-notifications/tsconfig.json +++ b/webapp/packages/core-notifications/tsconfig.json @@ -7,6 +7,9 @@ "composite": true }, "references": [ + { + "path": "../../common-react/@dbeaver/ui-kit" + }, { "path": "../core-blocks" }, diff --git a/webapp/packages/core-ui/package.json b/webapp/packages/core-ui/package.json index a43ad85f91..99d005d47f 100644 --- a/webapp/packages/core-ui/package.json +++ b/webapp/packages/core-ui/package.json @@ -42,7 +42,6 @@ "react-dnd": "^16", "react-dnd-html5-backend": "^16", "react-dom": "^19", - "reakit": "^1", "tslib": "^2" }, "devDependencies": { diff --git a/webapp/packages/core-ui/src/ContextMenu/ContextMenu.tsx b/webapp/packages/core-ui/src/ContextMenu/ContextMenu.tsx index cb970a2fa2..36c547c471 100644 --- a/webapp/packages/core-ui/src/ContextMenu/ContextMenu.tsx +++ b/webapp/packages/core-ui/src/ContextMenu/ContextMenu.tsx @@ -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 { +export interface IContextMenuNewProps extends IContextMenuProps { render?: React.ReactElement; placement?: HovercardStoreState['placement']; showOnHover?: boolean; diff --git a/webapp/packages/core-ui/src/ContextMenu/IContextMenuProps.ts b/webapp/packages/core-ui/src/ContextMenu/IContextMenuProps.ts index 6fe7f03387..da94454062 100644 --- a/webapp/packages/core-ui/src/ContextMenu/IContextMenuProps.ts +++ b/webapp/packages/core-ui/src/ContextMenu/IContextMenuProps.ts @@ -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, 'children'> { contextMenuPosition?: IContextMenuPosition; menu: IMenuData; - placement?: MenuInitialState['placement']; visible?: boolean; rtl?: boolean; children?: React.ReactNode; diff --git a/webapp/packages/plugin-authentication-administration/package.json b/webapp/packages/plugin-authentication-administration/package.json index 115266a3e5..0a848cc86b 100644 --- a/webapp/packages/plugin-authentication-administration/package.json +++ b/webapp/packages/plugin-authentication-administration/package.json @@ -48,7 +48,6 @@ "mobx-react-lite": "^4", "react": "^19", "react-dom": "^19", - "reakit": "^1", "tslib": "^2" }, "devDependencies": { diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/SSH/SSHKeyUploader.tsx b/webapp/packages/plugin-connections/src/ConnectionForm/SSH/SSHKeyUploader.tsx index f5669f319e..c84af42db1 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/SSH/SSHKeyUploader.tsx +++ b/webapp/packages/plugin-connections/src/ConnectionForm/SSH/SSHKeyUploader.tsx @@ -52,7 +52,7 @@ export const SSHKeyUploader = observer(function SSHKeyUploader({ state, s - diff --git a/webapp/packages/plugin-data-viewer-result-set-grouping/package.json b/webapp/packages/plugin-data-viewer-result-set-grouping/package.json index 4b153065e4..5ebc71e008 100644 --- a/webapp/packages/plugin-data-viewer-result-set-grouping/package.json +++ b/webapp/packages/plugin-data-viewer-result-set-grouping/package.json @@ -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", diff --git a/webapp/packages/plugin-data-viewer-result-set-grouping/src/DVGroupingColumnEditorDialog/GroupingTableItem.tsx b/webapp/packages/plugin-data-viewer-result-set-grouping/src/DVGroupingColumnEditorDialog/GroupingTableItem.tsx index b01f1b26d0..32ead62f19 100644 --- a/webapp/packages/plugin-data-viewer-result-set-grouping/src/DVGroupingColumnEditorDialog/GroupingTableItem.tsx +++ b/webapp/packages/plugin-data-viewer-result-set-grouping/src/DVGroupingColumnEditorDialog/GroupingTableItem.tsx @@ -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(function GroupingTableItem({ id, name, className, onDelete }) { + const translate = useTranslate(); + return ( {name} - onDelete(id)} /> + onDelete(id)}> + + ); diff --git a/webapp/packages/plugin-data-viewer-result-set-grouping/tsconfig.json b/webapp/packages/plugin-data-viewer-result-set-grouping/tsconfig.json index 35c15896ac..e189710694 100644 --- a/webapp/packages/plugin-data-viewer-result-set-grouping/tsconfig.json +++ b/webapp/packages/plugin-data-viewer-result-set-grouping/tsconfig.json @@ -7,6 +7,9 @@ "composite": true }, "references": [ + { + "path": "../../common-react/@dbeaver/ui-kit" + }, { "path": "../core-blocks" }, diff --git a/webapp/packages/plugin-data-viewer/src/TableViewer/TableHeader/TableWhereFilter.tsx b/webapp/packages/plugin-data-viewer/src/TableViewer/TableHeader/TableWhereFilter.tsx index 8af46221c3..da628777ee 100644 --- a/webapp/packages/plugin-data-viewer/src/TableViewer/TableHeader/TableWhereFilter.tsx +++ b/webapp/packages/plugin-data-viewer/src/TableViewer/TableHeader/TableWhereFilter.tsx @@ -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(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 - - + + 0}> + + + + + + ); }); diff --git a/webapp/packages/plugin-log-viewer/package.json b/webapp/packages/plugin-log-viewer/package.json index 69daa1426f..121a73688b 100644 --- a/webapp/packages/plugin-log-viewer/package.json +++ b/webapp/packages/plugin-log-viewer/package.json @@ -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", diff --git a/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerInfoPanel.module.css b/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerInfoPanel.module.css index 697218a078..eda3d55b93 100644 --- a/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerInfoPanel.module.css +++ b/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerInfoPanel.module.css @@ -26,10 +26,6 @@ padding: 4px; } -.button { - flex: unset; -} - .type { composes: theme-typography--body2 from global; margin: 0; diff --git a/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerInfoPanel.tsx b/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerInfoPanel.tsx index 80adde202b..bc7bb0a2ab 100644 --- a/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerInfoPanel.tsx +++ b/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerInfoPanel.tsx @@ -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(function LogViewerInfoPanel({ - + + +
diff --git a/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerTable.module.css b/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerTable.module.css index 3d000a41c9..f18e965c6f 100644 --- a/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerTable.module.css +++ b/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerTable.module.css @@ -42,11 +42,4 @@ .timestamp { width: 116px; min-width: 116px; -} - -.clearButton { - position: absolute !important; - top: 4px; - right: 16px; - z-index: 100; -} +} \ No newline at end of file diff --git a/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerTable.tsx b/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerTable.tsx index 587b7be20d..4fb754cdcb 100644 --- a/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerTable.tsx +++ b/webapp/packages/plugin-log-viewer/src/LogViewer/LogViewerTable.tsx @@ -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(function LogViewerTable({ items, s return ( - + + + {translate('ui_clear')} - + diff --git a/webapp/packages/plugin-log-viewer/tsconfig.json b/webapp/packages/plugin-log-viewer/tsconfig.json index 35f4c3fb84..f6812f382d 100644 --- a/webapp/packages/plugin-log-viewer/tsconfig.json +++ b/webapp/packages/plugin-log-viewer/tsconfig.json @@ -10,6 +10,9 @@ { "path": "../../common-react/@dbeaver/react-tests" }, + { + "path": "../../common-react/@dbeaver/ui-kit" + }, { "path": "../../common-typescript/@dbeaver/cli" }, diff --git a/webapp/packages/plugin-object-viewer/package.json b/webapp/packages/plugin-object-viewer/package.json index 14b8772d6c..30cddb3ba2 100644 --- a/webapp/packages/plugin-object-viewer/package.json +++ b/webapp/packages/plugin-object-viewer/package.json @@ -46,7 +46,6 @@ "mobx-react-lite": "^4", "react": "^19", "react-dom": "^19", - "reakit": "^1", "tslib": "^2" }, "devDependencies": { diff --git a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/FolderContext.ts b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/FolderContext.ts deleted file mode 100644 index cb4f3f272a..0000000000 --- a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/FolderContext.ts +++ /dev/null @@ -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(undefined); diff --git a/webapp/packages/plugin-sql-editor-new/src/SQLEditor/SQLCodeEditorPanel/SQLCodeEditorPanel.tsx b/webapp/packages/plugin-sql-editor-new/src/SQLEditor/SQLCodeEditorPanel/SQLCodeEditorPanel.tsx index 0e70e575ab..7d64688899 100644 --- a/webapp/packages/plugin-sql-editor-new/src/SQLEditor/SQLCodeEditorPanel/SQLCodeEditorPanel.tsx +++ b/webapp/packages/plugin-sql-editor-new/src/SQLEditor/SQLCodeEditorPanel/SQLCodeEditorPanel.tsx @@ -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 {data.isIncomingChanges && ( <> - + - + )} diff --git a/webapp/yarn.lock b/webapp/yarn.lock index af485d524b..b2d82bef4e 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -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" From df676913a0c3044717d333073d216a0783bb2f55 Mon Sep 17 00:00:00 2001 From: Sychev Andrey <44414066+SychevAndrey@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:22:14 +0100 Subject: [PATCH 2/4] dbeaver/pro#8808 feat: add shortcut for search in data viewer (#4239) * dbeaver/pro#8808 feat: add shortcut for search in data viewer * dbeaver/pro#8808 feat: update shortcut ID for search functionality --- .../plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts | 12 ++++++++---- webapp/packages/plugin-help/src/locales/en.ts | 5 +++-- webapp/packages/plugin-help/src/locales/fr.ts | 4 ++-- webapp/packages/plugin-help/src/locales/it.ts | 2 +- webapp/packages/plugin-help/src/locales/ru.ts | 2 +- webapp/packages/plugin-help/src/locales/vi.ts | 2 +- webapp/packages/plugin-help/src/locales/zh.ts | 2 +- 7 files changed, 17 insertions(+), 12 deletions(-) diff --git a/webapp/packages/plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts b/webapp/packages/plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts index 043ec7b998..20fb2c74c7 100644 --- a/webapp/packages/plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts +++ b/webapp/packages/plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts @@ -33,8 +33,8 @@ const KEY_BINDING_SQL_EDITOR_COMMENT = createKeyBinding({ keys: ['mod+/'], }); -const KEY_BINDING_SQL_EDITOR_FIND = createKeyBinding({ - id: 'sql-editor-find', +const KEY_BINDING_FIND = createKeyBinding({ + id: 'find', keys: ['mod+f'], }); @@ -57,6 +57,10 @@ export const DATA_VIEWER_SHORTCUTS: IShortcut[] = [ label: 'data_viewer_shortcut_delete_row', code: transformKeys(KEY_BINDING_DELETE_ROW), }, + { + label: 'plugin_search_panel_find', + code: transformKeys(KEY_BINDING_FIND), + }, { label: 'ui_processing_save', code: transformKeys(KEY_BINDING_SAVE), @@ -109,8 +113,8 @@ export const SQL_EDITOR_SHORTCUTS: IShortcut[] = [ code: transformKeys(KEY_BINDING_REDO), }, { - label: 'sql_editor_shortcut_find', - code: transformKeys(KEY_BINDING_SQL_EDITOR_FIND), + label: 'plugin_search_panel_find', + code: transformKeys(KEY_BINDING_FIND), }, { label: 'sql_editor_shortcut_comment_uncomment_selection', diff --git a/webapp/packages/plugin-help/src/locales/en.ts b/webapp/packages/plugin-help/src/locales/en.ts index c6aa2b5af2..bbfd865412 100644 --- a/webapp/packages/plugin-help/src/locales/en.ts +++ b/webapp/packages/plugin-help/src/locales/en.ts @@ -18,12 +18,13 @@ export default [ ['sql_editor_shortcut_show_execution_plan', 'Show Execution plan'], ['sql_editor_shortcut_format', 'Format script'], ['sql_editor_shortcut_open_editor_in_new_tab', 'Open SQL Editor in the separate browser Tab'], - ['sql_editor_shortcut_find', 'Find'], ['sql_editor_shortcut_comment_uncomment_selection', 'Comment/Uncomment selection'], ['sql_editor_shortcut_start_completion', 'Show autocomplete suggestions'], ['sql_editor_shortcut_accept_completion', 'Accept autocompletion'], ['sql_editor_shortcut_escape', 'Escape editor'], + ['plugin_search_panel_find', 'Find'], + ['navigation_tree_shortcut_enable_filter', 'Enable filtering'], ['plugin_help_multi_tab_support_title', 'Multi-tab is not supported'], @@ -34,5 +35,5 @@ export default [ Your local application settings will be lost after the browser tab is closed. You can load tabs and settings for this tab`, ], - ['plugin_help_multi_tab_support_load_settings', 'Load tabs ans settings'], + ['plugin_help_multi_tab_support_load_settings', 'Load tabs and settings'], ]; diff --git a/webapp/packages/plugin-help/src/locales/fr.ts b/webapp/packages/plugin-help/src/locales/fr.ts index ef26c8ae1e..23147abf82 100644 --- a/webapp/packages/plugin-help/src/locales/fr.ts +++ b/webapp/packages/plugin-help/src/locales/fr.ts @@ -17,16 +17,16 @@ export default [ ['sql_editor_shortcut_show_execution_plan', "Afficher le plan d'exécution"], ['sql_editor_shortcut_format', 'Formater le script'], ['sql_editor_shortcut_open_editor_in_new_tab', "Ouvrir l'éditeur SQL dans un nouvel onglet"], - ['sql_editor_shortcut_find', 'Rechercher'], ['sql_editor_shortcut_comment_uncomment_selection', 'Commenter/Décommenter la sélection'], ['sql_editor_shortcut_start_completion', 'Afficher les suggestions de saisie semi-automatique'], ['sql_editor_shortcut_accept_completion', 'Accepter la saisie semi-automatique'], ['sql_editor_shortcut_escape', "Échapper l'éditeur"], + ['plugin_search_panel_find', 'Rechercher'], ['navigation_tree_shortcut_enable_filter', 'Activer le filtrage'], ['plugin_help_multi_tab_support_title', "Le multi-onglet n'est pas supporté"], [ 'plugin_help_multi_tab_support_description', "Les données ne sont pas connectées à d'autres onglets du navigateur. Tout onglet d'application ouvert ne sera pas sauvegardé et sera probablement perdu après la fermeture de l'onglet. Vos paramètres et données locaux seront perdus après la fermeture de l'onglet. Vous pouvez charger les onglets et les paramètres pour cet onglet.", ], - ['plugin_help_multi_tab_support_load_settings', 'Charger les onglets comme paramètres'], + ['plugin_help_multi_tab_support_load_settings', 'Charger les onglets et les paramètres'], ]; diff --git a/webapp/packages/plugin-help/src/locales/it.ts b/webapp/packages/plugin-help/src/locales/it.ts index bb84316ebd..35daa88e60 100644 --- a/webapp/packages/plugin-help/src/locales/it.ts +++ b/webapp/packages/plugin-help/src/locales/it.ts @@ -18,11 +18,11 @@ export default [ ['sql_editor_shortcut_show_execution_plan', 'Show Execution plan'], ['sql_editor_shortcut_format', 'Format script'], ['sql_editor_shortcut_open_editor_in_new_tab', 'Open SQL Editor in the separate browser Tab'], - ['sql_editor_shortcut_find', 'Find'], ['sql_editor_shortcut_comment_uncomment_selection', 'Comment/Uncomment selection'], ['sql_editor_shortcut_start_completion', 'Show autocomplete suggestions'], ['sql_editor_shortcut_accept_completion', 'Accept autocompletion'], ['sql_editor_shortcut_escape', 'Escape editor'], + ['plugin_search_panel_find', 'Find'], ['navigation_tree_shortcut_enable_filter', 'Enable filtering'], ]; diff --git a/webapp/packages/plugin-help/src/locales/ru.ts b/webapp/packages/plugin-help/src/locales/ru.ts index f11ecee81f..93f64b7281 100644 --- a/webapp/packages/plugin-help/src/locales/ru.ts +++ b/webapp/packages/plugin-help/src/locales/ru.ts @@ -18,11 +18,11 @@ export default [ ['sql_editor_shortcut_show_execution_plan', 'Показать план выполнения'], ['sql_editor_shortcut_format', 'Форматировать скрипт'], ['sql_editor_shortcut_open_editor_in_new_tab', 'Открыть SQL редактор в новой бразуерной вкладке'], - ['sql_editor_shortcut_find', 'Найти'], ['sql_editor_shortcut_comment_uncomment_selection', 'Закомментировать/раскомментировать выделение'], ['sql_editor_shortcut_start_completion', 'Показать подсказки автодополнения'], ['sql_editor_shortcut_accept_completion', 'Принять автодополнение'], ['sql_editor_shortcut_escape', 'Выйти из редактора'], + ['plugin_search_panel_find', 'Найти'], ['navigation_tree_shortcut_enable_filter', 'Включить фильтрацию'], ]; diff --git a/webapp/packages/plugin-help/src/locales/vi.ts b/webapp/packages/plugin-help/src/locales/vi.ts index 2e049fb8f6..ba36545b9e 100644 --- a/webapp/packages/plugin-help/src/locales/vi.ts +++ b/webapp/packages/plugin-help/src/locales/vi.ts @@ -18,11 +18,11 @@ export default [ ['sql_editor_shortcut_show_execution_plan', 'Hiển thị kế hoạch thực thi'], ['sql_editor_shortcut_format', 'Định dạng kịch bản'], ['sql_editor_shortcut_open_editor_in_new_tab', 'Mở Trình chỉnh sửa SQL trong tab riêng biệt'], - ['sql_editor_shortcut_find', 'Tìm kiếm'], ['sql_editor_shortcut_comment_uncomment_selection', 'Bình luận/Bỏ bình luận lựa chọn'], ['sql_editor_shortcut_start_completion', 'Hiển thị gợi ý tự động hoàn thành'], ['sql_editor_shortcut_accept_completion', 'Chấp nhận tự động hoàn thành'], ['sql_editor_shortcut_escape', 'Thoát khỏi trình chỉnh sửa'], + ['plugin_search_panel_find', 'Tìm kiếm'], ['navigation_tree_shortcut_enable_filter', 'Bật lọc'], diff --git a/webapp/packages/plugin-help/src/locales/zh.ts b/webapp/packages/plugin-help/src/locales/zh.ts index f8489b3088..642c60bba2 100644 --- a/webapp/packages/plugin-help/src/locales/zh.ts +++ b/webapp/packages/plugin-help/src/locales/zh.ts @@ -18,11 +18,11 @@ export default [ ['sql_editor_shortcut_show_execution_plan', '显示执行计划'], ['sql_editor_shortcut_format', '格式化脚本'], ['sql_editor_shortcut_open_editor_in_new_tab', '在单独的浏览器标签下打开SQL编辑器'], - ['sql_editor_shortcut_find', '查找'], ['sql_editor_shortcut_comment_uncomment_selection', '注释/取消注释所选内容'], ['sql_editor_shortcut_start_completion', '显示自动完成建议'], ['sql_editor_shortcut_accept_completion', '接受自动完成'], ['sql_editor_shortcut_escape', '退出编辑器'], + ['plugin_search_panel_find', '查找'], ['navigation_tree_shortcut_enable_filter', '启用过滤'], ]; From b779b9d8550f633500760eeb408a124333f162b0 Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Fri, 27 Mar 2026 14:24:43 +0100 Subject: [PATCH 3/4] [CB] Fix high CVE-2026-33671 in picomatch (#4244) * dbeaver/pro#8836 fixes picomatch CVE * fixes happy-dom CVE --- webapp/common-react/package.json | 3 +++ webapp/package.json | 4 +++- webapp/yarn.lock | 23 ++++++++--------------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/webapp/common-react/package.json b/webapp/common-react/package.json index 3bbab6148d..56740aaaf1 100644 --- a/webapp/common-react/package.json +++ b/webapp/common-react/package.json @@ -11,6 +11,9 @@ "clear": "rimraf -g '@dbeaver/*/{dist,lib}'", "test": "dbeaver-test" }, + "resolutions": { + "happy-dom": ">=20.8.8" + }, "devDependencies": { "@dbeaver/cli": "workspace:^", "@dbeaver/prettier-config": "workspace:^", diff --git a/webapp/package.json b/webapp/package.json index cd3c47d47e..59f95ce006 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -24,7 +24,9 @@ "koa": ">=2.16.4", "immutable": ">=4.3.8", "serialize-javascript": ">=7.0.3", - "flatted": ">=3.4.2" + "flatted": ">=3.4.2", + "picomatch": ">=2.3.2", + "happy-dom": ">=20.8.8" }, "scripts": { "test": "dbeaver-test", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index b2d82bef4e..eba0f28e25 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -13122,9 +13122,9 @@ __metadata: languageName: node linkType: hard -"happy-dom@npm:^20": - version: 20.8.3 - resolution: "happy-dom@npm:20.8.3" +"happy-dom@npm:>=20.8.8": + version: 20.8.9 + resolution: "happy-dom@npm:20.8.9" dependencies: "@types/node": "npm:>=20.0.0" "@types/whatwg-mimetype": "npm:^3.0.2" @@ -13132,7 +13132,7 @@ __metadata: entities: "npm:^7.0.1" whatwg-mimetype: "npm:^3.0.0" ws: "npm:^8.18.3" - checksum: 10c0/79a2b6d32e2f778b015511c9d2de7f7859954cbd27a30720736e60552a88e0345ffc8b2c5e77140c5f2f7fef95c563aea7ab0b2edba5d8c7edd5676b43ecef55 + checksum: 10c0/12fe9b85891b1289935b2a4f726e2839e7c92e3f55401bd20cb5500763dd994364645acaab20a754fd90d5e1b2ffdf734b39a32ea02704c3e3ee0c4d30cf958e languageName: node linkType: hard @@ -16559,17 +16559,10 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.2.2, picomatch@npm:^2.3.1": - version: 2.3.1 - resolution: "picomatch@npm:2.3.1" - checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be - languageName: node - linkType: hard - -"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3": - version: 4.0.3 - resolution: "picomatch@npm:4.0.3" - checksum: 10c0/9582c951e95eebee5434f59e426cddd228a7b97a0161a375aed4be244bd3fe8e3a31b846808ea14ef2c8a2527a6eeab7b3946a67d5979e81694654f939473ae2 +"picomatch@npm:>=2.3.2": + version: 4.0.4 + resolution: "picomatch@npm:4.0.4" + checksum: 10c0/e2c6023372cc7b5764719a5ffb9da0f8e781212fa7ca4bd0562db929df8e117460f00dff3cb7509dacfc06b86de924b247f504d0ce1806a37fac4633081466b0 languageName: node linkType: hard From 1d5b475482e5d6e95b0f2b62be27e37b2a78ddca Mon Sep 17 00:00:00 2001 From: Sychev Andrey <44414066+SychevAndrey@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:07:31 +0100 Subject: [PATCH 4/4] dbeaver/pro#8553 sql execution plan diagram (#4218) * dbeaver/pro#8553 feat: add execution plan diagram component * dbeaver/pro#8553 feat: enhance ExecutionPlanDiagram component with selectedNodeId prop * dbeaver/pro#8553 feat: add flex-shrink to metric value for better layout * dbeaver/pro#8553 refactor: remove queryString from IPlanData interface * dbeaver/pro#8553 feat: integrate execution plan diagram component * dbeaver/pro#8553 refactor: execution plan diagram separate state, add l10n, make toolbar composable * dbeaver/pro#8553 feat: implement keyboard navigation for execution plan nodes * dbeaver/pro#8553 feat: add translation support for execution plan diagram labels * dbeaver/pro#8553 fix: execution plan node dimensions * dbeaver/pro#8553 refactor: move formatNumber to helpers to use it * dbeaver/pro#8553 feat: add translation support for execution plan diagram API * dbeaver/pro#8553 docs: add README and API documentation for execution plan diagram component * dbeaver/pro#8553 feat: adjust cost bar height and collapse indicator position * dbeaver/pro#8553 feat: update PRIMARY_AXIS_WEIGHT for improved directional navigation * dbeaver/pro#8553 feat: refactor diagram actions and add collapse state management * dbeaver/pro#8553 refactor: move execution plan diagram to separate EE plugin * dbeaver/pro#8553 docs: remove redundant comment * dbeaver/pro#8553 docs: update comments for source and target anchors in ILayoutEdge interface * dbeaver/pro#8553 docs: update comment in ILayoutNode interface * dbeaver/pro#8553 refactor: simplify heavy route computation * dbeaver/pro#8553 refactor: optimize node handling in computePlanLayout function * dbeaver/pro#8553 refactor: improve tree structure handling in usePlanTree and buildTree functions * dbeaver/pro#8553 refactor: simplify usePlanTree * dbeaver/pro#8553 refactor: extract actions from useExecutionPlanDiagramState * dbeaver/pro#8553 refactor: improve configuration in execution plan diagram * dbeaver/pro#8553 refactor: simplify metric rendering in PlanNode component * dbeaver/pro#8553 feat: collapse by hotkey, not an extra element * dbeaver/pro#8553 feat: add tooltip hints for plan node interactions * dbeaver/pro#8553 refactor: remove high cost calculation and add node kind attribute * dbeaver/pro#8553 style: fix formatting in index.ts for ISqlExecutionPlanViewProps export * dbeaver/pro#8553 ci: remove unused dependency on @dbeaver/ui-kit * dbeaver/pro#8553 refactor: remove SQL execution plan diagram translations from locale files * dbeaver/pro#8553 refactor: update translation keys for SQL execution plan components * dbeaver/pro#8553 refactor: rename components * dbeaver/pro#8553 style: const * dbeaver/pro#8553 refactor: reset execution plan diagram state properties * dbeaver/pro#8553 refactor: update component imports and extract a component * dbeaver/pro#8553 refactor: rename PlanNodeKind to EPlanNodeKind and update references * dbeaver/pro#8553 feat: add zoom icons and update action references * dbeaver/pro#8553 refactor: enhance translation functionality by adding args interpolation * dbeaver/pro#8553 fix: Polynomial regular expression used on uncontrolled data * dbeaver/pro#8553 refactor: update ActionsCapture to use ref for toolbar actions * dbeaver/pro#8553 refactor: use jsx * dbeaver/pro#8553 refactor: simplify * dbeaver/pro#8553 refactor: replace useContext with use in DiagramContext * dbeaver/pro#8553 refactor: convert ExecutionPlanEdge to memoized component * dbeaver/pro#8553 refactor: convert ExecutionPlanNode to memoized component * dbeaver/pro#8553 refactor: unify ref prop in ExecutionPlanNode and ExecutionPlanViewport * dbeaver/pro#8553 refactor: extract ExecutionPlanViewportContent component and clean up CSS * dbeaver/pro#8553 refactor: convert ExecutionPlanNodeMetric to memoized component * dbeaver/pro#8553 refactor: improve usePlanLayout hook by utilizing useEffectEvent for stable callback * dbeaver/pro#8553 refactor: simplify diagram actions handling in createExecutionPlanDiagram * dbeaver/pro#8553 refactor: move out execution plan diagram component from --------- Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com> Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com> Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com> --- .../react-translate/src/TranslateContext.ts | 30 +++++++++++--- .../@dbeaver/js-helpers/src/formatNumber.ts | 24 +++++++++++ .../@dbeaver/js-helpers/src/index.ts | 1 + webapp/packages/core-utils/package.json | 1 + .../packages/core-utils/src/formatNumber.ts | 19 +-------- webapp/packages/core-utils/tsconfig.json | 3 ++ .../core-view/public/icons/zoom-in.svg | 3 -- .../core-view/public/icons/zoom-out.svg | 3 -- .../packages/core-view/public/icons/zoom.svg | 11 +++++ .../core-view/public/icons/zoom_in.svg | 13 ++++++ .../core-view/public/icons/zoom_in_m.svg | 13 ++++++ .../core-view/public/icons/zoom_in_sm.svg | 18 ++++++++ .../core-view/public/icons/zoom_m.svg | 11 +++++ .../core-view/public/icons/zoom_out.svg | 12 ++++++ .../core-view/public/icons/zoom_out_m.svg | 12 ++++++ .../core-view/public/icons/zoom_out_sm.svg | 17 ++++++++ .../core-view/public/icons/zoom_sm.svg | 16 ++++++++ .../src/Action/Actions/ACTION_ZOOM_IN.ts | 4 +- .../src/Action/Actions/ACTION_ZOOM_OUT.ts | 4 +- .../ExecutionPlan/ExecutionPlanTreeBlock.tsx | 4 +- .../ExecutionPlan/ExecutionPlanTreeView.tsx | 20 +++++++++ .../ISqlExecutionPlanViewProps.ts | 16 ++++++++ .../PropertiesPanel/PropertiesPanel.tsx | 16 +++++--- .../SqlExecutionPlanPanel.module.css | 41 ++++++++++++++++++- .../ExecutionPlan/SqlExecutionPlanPanel.tsx | 20 +++++++-- .../SqlExecutionPlanViewBootstrap.ts | 30 ++++++++++++++ .../SqlExecutionPlanViewService.ts | 21 ++++++++++ .../packages/plugin-sql-editor/src/index.ts | 3 ++ .../plugin-sql-editor/src/locales/en.ts | 13 +++--- .../plugin-sql-editor/src/locales/fr.ts | 13 +++--- .../plugin-sql-editor/src/locales/it.ts | 13 +++--- .../plugin-sql-editor/src/locales/ru.ts | 13 +++--- .../plugin-sql-editor/src/locales/vi.ts | 13 +++--- .../plugin-sql-editor/src/locales/zh.ts | 13 +++--- .../packages/plugin-sql-editor/src/module.ts | 6 ++- webapp/yarn.lock | 1 + 36 files changed, 389 insertions(+), 82 deletions(-) create mode 100644 webapp/common-typescript/@dbeaver/js-helpers/src/formatNumber.ts delete mode 100644 webapp/packages/core-view/public/icons/zoom-in.svg delete mode 100644 webapp/packages/core-view/public/icons/zoom-out.svg create mode 100644 webapp/packages/core-view/public/icons/zoom.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_in.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_in_m.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_in_sm.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_m.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_out.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_out_m.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_out_sm.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_sm.svg create mode 100644 webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeView.tsx create mode 100644 webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ISqlExecutionPlanViewProps.ts create mode 100644 webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewBootstrap.ts create mode 100644 webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.ts diff --git a/webapp/common-react/@dbeaver/react-translate/src/TranslateContext.ts b/webapp/common-react/@dbeaver/react-translate/src/TranslateContext.ts index f2605c4a63..eef5a781f9 100644 --- a/webapp/common-react/@dbeaver/react-translate/src/TranslateContext.ts +++ b/webapp/common-react/@dbeaver/react-translate/src/TranslateContext.ts @@ -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. @@ -9,11 +9,31 @@ import { createContext } from 'react'; import type { LocalizationToken } from './LocalizationToken.js'; -export function defaultTranslateFn(token: T, fallback?: T): T { - return fallback || token; +export function interpolateTranslation(template: string, args?: Record): string { + if (!args) { + return template; + } + + let result = template; + + for (const [key, value] of Object.entries(args)) { + if (value != null) { + result = result.split(`{arg:${key}}`).join(String(value)); + } + } + + return result; } -export type TranslateFn = (token: T, fallback?: T, args?: Record) => T; +export function defaultTranslateFn(token: T, fallback?: T, args?: Record): T { + const result = fallback || token; + if (result && args) { + return interpolateTranslation(result as string, args) as T; + } + return result; +} + +export type TranslateFn = (token: T, fallback?: T, args?: Record) => T; export const TranslateContext = createContext<{ translate: TranslateFn }>({ - translate: (key, fallback) => fallback || key, + translate: defaultTranslateFn, }); diff --git a/webapp/common-typescript/@dbeaver/js-helpers/src/formatNumber.ts b/webapp/common-typescript/@dbeaver/js-helpers/src/formatNumber.ts new file mode 100644 index 0000000000..16699a7a95 --- /dev/null +++ b/webapp/common-typescript/@dbeaver/js-helpers/src/formatNumber.ts @@ -0,0 +1,24 @@ +/* + * 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. + */ + +export function formatNumber(n: number, d: number): string { + if (n < 1000) { + return n.toLocaleString(undefined, { maximumFractionDigits: 2 }); + } + + const numStr = n.toString(); + const exponent = numStr.length - (numStr.length % 3); + + const power = Math.pow(10, d); + const rounded = Math.round((n * power) / Math.pow(10, exponent)) / power; + + const units = ' kMBTPE'; + const unit = units[exponent / 3]!; + + return rounded + unit; +} \ No newline at end of file diff --git a/webapp/common-typescript/@dbeaver/js-helpers/src/index.ts b/webapp/common-typescript/@dbeaver/js-helpers/src/index.ts index e8565e0642..8a8f1be87e 100644 --- a/webapp/common-typescript/@dbeaver/js-helpers/src/index.ts +++ b/webapp/common-typescript/@dbeaver/js-helpers/src/index.ts @@ -16,3 +16,4 @@ export * from './memoizeLast.js'; export * from './mutex.js'; export * from './reorderArray.js'; export * from './getLocalizedDisplayName.js'; +export * from './formatNumber.js'; diff --git a/webapp/packages/core-utils/package.json b/webapp/packages/core-utils/package.json index f62ccba5bd..c9972bacf6 100644 --- a/webapp/packages/core-utils/package.json +++ b/webapp/packages/core-utils/package.json @@ -23,6 +23,7 @@ "validate-dependencies": "core-cli-validate-dependencies" }, "dependencies": { + "@dbeaver/js-helpers": "workspace:*", "@timohausmann/quadtree-ts": "^2", "fast-deep-equal": "^3", "md5": "^2", diff --git a/webapp/packages/core-utils/src/formatNumber.ts b/webapp/packages/core-utils/src/formatNumber.ts index 1a3d8f9e12..30d442a075 100644 --- a/webapp/packages/core-utils/src/formatNumber.ts +++ b/webapp/packages/core-utils/src/formatNumber.ts @@ -1,24 +1,9 @@ /* * 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. */ -export function formatNumber(n: number, d: number) { - if (n < 1000) { - return n.toLocaleString(undefined, { maximumFractionDigits: 2 }); - } - - const numStr = n.toString(); - const exponent = numStr.length - (numStr.length % 3); - - const power = Math.pow(10, d); - const rounded = Math.round((n * power) / Math.pow(10, exponent)) / power; - - const units = ' kMBTPE'; - const unit = units[exponent / 3]!; - - return rounded + unit; -} +export { formatNumber } from '@dbeaver/js-helpers'; diff --git a/webapp/packages/core-utils/tsconfig.json b/webapp/packages/core-utils/tsconfig.json index 964a4a5f8b..30236ffc9b 100644 --- a/webapp/packages/core-utils/tsconfig.json +++ b/webapp/packages/core-utils/tsconfig.json @@ -13,6 +13,9 @@ { "path": "../../common-typescript/@dbeaver/cli" }, + { + "path": "../../common-typescript/@dbeaver/js-helpers" + }, { "path": "../core-cli" }, diff --git a/webapp/packages/core-view/public/icons/zoom-in.svg b/webapp/packages/core-view/public/icons/zoom-in.svg deleted file mode 100644 index 7ca279793b..0000000000 --- a/webapp/packages/core-view/public/icons/zoom-in.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/webapp/packages/core-view/public/icons/zoom-out.svg b/webapp/packages/core-view/public/icons/zoom-out.svg deleted file mode 100644 index efa287627c..0000000000 --- a/webapp/packages/core-view/public/icons/zoom-out.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/webapp/packages/core-view/public/icons/zoom.svg b/webapp/packages/core-view/public/icons/zoom.svg new file mode 100644 index 0000000000..8f0f823ec6 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_in.svg b/webapp/packages/core-view/public/icons/zoom_in.svg new file mode 100644 index 0000000000..0a07b3fa4b --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_in.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_in_m.svg b/webapp/packages/core-view/public/icons/zoom_in_m.svg new file mode 100644 index 0000000000..5b863afa24 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_in_m.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_in_sm.svg b/webapp/packages/core-view/public/icons/zoom_in_sm.svg new file mode 100644 index 0000000000..6ffe2e4d23 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_in_sm.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_m.svg b/webapp/packages/core-view/public/icons/zoom_m.svg new file mode 100644 index 0000000000..5f66f43286 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_m.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_out.svg b/webapp/packages/core-view/public/icons/zoom_out.svg new file mode 100644 index 0000000000..01af24e5f0 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_out.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_out_m.svg b/webapp/packages/core-view/public/icons/zoom_out_m.svg new file mode 100644 index 0000000000..72c80d928b --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_out_m.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_out_sm.svg b/webapp/packages/core-view/public/icons/zoom_out_sm.svg new file mode 100644 index 0000000000..fe110d6b54 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_out_sm.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_sm.svg b/webapp/packages/core-view/public/icons/zoom_sm.svg new file mode 100644 index 0000000000..7228cdb884 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_sm.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_IN.ts b/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_IN.ts index 536eac3898..fe01f537f4 100644 --- a/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_IN.ts +++ b/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_IN.ts @@ -1,6 +1,6 @@ /* * 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. @@ -10,5 +10,5 @@ import { createAction } from '../createAction.js'; export const ACTION_ZOOM_IN = createAction('zoom-in', { label: 'core_view_zoom_in', tooltip: 'core_view_zoom_in', - icon: '/icons/zoom-in.svg', + icon: '/icons/zoom_in.svg', }); diff --git a/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_OUT.ts b/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_OUT.ts index 5f63cb7822..8ed984b25e 100644 --- a/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_OUT.ts +++ b/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_OUT.ts @@ -1,6 +1,6 @@ /* * 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. @@ -10,5 +10,5 @@ import { createAction } from '../createAction.js'; export const ACTION_ZOOM_OUT = createAction('zoom-out', { label: 'core_view_zoom_out', tooltip: 'core_view_zoom_out', - icon: '/icons/zoom-out.svg', + icon: '/icons/zoom_out.svg', }); diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeBlock.tsx b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeBlock.tsx index abd6111b21..395355f184 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeBlock.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeBlock.tsx @@ -1,6 +1,6 @@ /* * 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. @@ -64,7 +64,7 @@ export const ExecutionPlanTreeBlock = observer(function ExecutionPlanTree
) : ( - {translate('sql_execution_plan_placeholder')} + {translate('plugin_sql_execution_plan_placeholder')} )} diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeView.tsx b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeView.tsx new file mode 100644 index 0000000000..f2ae91aa78 --- /dev/null +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeView.tsx @@ -0,0 +1,20 @@ +/* + * 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 { observer } from 'mobx-react-lite'; + +import type { ISqlExecutionPlanViewProps } from './ISqlExecutionPlanViewProps.js'; +import { ExecutionPlanTreeBlock } from './ExecutionPlanTreeBlock.js'; + +export const ExecutionPlanTreeView = observer(function ExecutionPlanTreeView({ + nodes, + query, + onNodeSelect, +}) { + return ; +}); diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ISqlExecutionPlanViewProps.ts b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ISqlExecutionPlanViewProps.ts new file mode 100644 index 0000000000..4d295ea10d --- /dev/null +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ISqlExecutionPlanViewProps.ts @@ -0,0 +1,16 @@ +/* + * 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 type { SqlExecutionPlanNode } from '@cloudbeaver/core-sdk'; + +export interface ISqlExecutionPlanViewProps { + nodes: SqlExecutionPlanNode[]; + query: string; + selectedNode: string | null; + onNodeSelect: (nodeId: string | null) => void; +} diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanel.tsx b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanel.tsx index 9e64f308c6..8abb0f1cd6 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanel.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanel.tsx @@ -1,6 +1,6 @@ /* * 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. @@ -50,11 +50,11 @@ export const PropertiesPanel = observer(function PropertiesPanel({ select ).get(); if (!general.length && !details.length) { - return {translate('sql_execution_plan_properties_panel_placeholder')}; + return {translate('plugin_sql_execution_plan_properties_panel_placeholder')}; } - const nameColumnTitle = translate('sql_execution_plan_properties_panel_name'); - const valueColumnTitle = translate('sql_execution_plan_properties_panel_value'); + const nameColumnTitle = translate('plugin_sql_execution_plan_properties_panel_name'); + const valueColumnTitle = translate('plugin_sql_execution_plan_properties_panel_value'); return ( @@ -63,8 +63,12 @@ export const PropertiesPanel = observer(function PropertiesPanel({ select {valueColumnTitle} - {!!general.length && } - {!!details.length && } + {!!general.length && ( + + )} + {!!details.length && ( + + )}
); diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.module.css b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.module.css index c03d414621..7b3d848fba 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.module.css +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.module.css @@ -1,6 +1,6 @@ /* * 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. @@ -8,6 +8,45 @@ .pane { &:first-child { + display: flex; + flex-direction: column; overflow: hidden; + + /* Map diagram component variables to CloudBeaver theme */ + + /* Viewport */ + --dbv-plan-viewport-bg: var(--theme-surface); + + /* Node */ + --dbv-plan-node-bg: var(--theme-sub-secondary); + --dbv-plan-node-border-color: var(--theme-input-border); + --dbv-plan-node-selected-border: var(--theme-primary); + --dbv-plan-node-selected-glow: color-mix(in srgb, var(--theme-primary) 30%, transparent); + --dbv-plan-node-heavy-color: var(--theme-negative); + + /* Node caption */ + --dbv-plan-node-caption-bg: var(--theme-background); + --dbv-plan-node-caption-color: var(--theme-on-surface); + + /* Node name */ + --dbv-plan-node-name-color: var(--theme-on-surface); + + /* Node metrics */ + --dbv-plan-node-metric-color: var(--theme-text-secondary-on-light); + --dbv-plan-node-metric-value-color: var(--theme-on-surface); + + /* Node cost bar */ + --dbv-plan-node-cost-bar-bg: var(--theme-input-border); + --dbv-plan-node-cost-bar-color: var(--theme-status); + + /* Edges */ + --dbv-plan-edge-color: var(--theme-text-hint-on-light); + + /* Toolbar */ + --dbv-plan-toolbar-border: var(--theme-input-border); + --dbv-plan-toolbar-bg: var(--theme-surface); + --dbv-plan-toolbar-color: var(--theme-on-surface); + --dbv-plan-toolbar-hover-bg: var(--theme-sub-secondary); + --dbv-plan-toolbar-active-bg: var(--theme-secondary); } } diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.tsx b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.tsx index 64d23e5c43..b177772a0d 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.tsx @@ -1,6 +1,6 @@ /* * 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. @@ -10,12 +10,13 @@ import { useState } from 'react'; import { Loader, Pane, ResizerControls, s, Split, useS, useSplitUserState } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; +import { TabList, TabPanelList, TabsState } from '@cloudbeaver/core-ui'; import type { IExecutionPlanTab } from '../../ISqlEditorTabState.js'; -import { ExecutionPlanTreeBlock } from './ExecutionPlanTreeBlock.js'; import { PropertiesPanel } from './PropertiesPanel/PropertiesPanel.js'; -import style from './SqlExecutionPlanPanel.module.css'; import { SqlExecutionPlanService } from './SqlExecutionPlanService.js'; +import { SqlExecutionPlanViewService } from './SqlExecutionPlanViewService.js'; +import style from './SqlExecutionPlanPanel.module.css'; interface Props { executionPlanTab: IExecutionPlanTab; @@ -24,6 +25,7 @@ interface Props { export const SqlExecutionPlanPanel = observer(function SqlExecutionPlanPanel({ executionPlanTab }) { const styles = useS(style); const sqlExecutionPlanService = useService(SqlExecutionPlanService); + const sqlExecutionPlanViewService = useService(SqlExecutionPlanViewService); const data = sqlExecutionPlanService.data.get(executionPlanTab.tabId); const [selectedNode, setSelectedNode] = useState(null); const splitState = useSplitUserState('execution-plan'); @@ -35,7 +37,17 @@ export const SqlExecutionPlanPanel = observer(function SqlExecutionPlanPa return ( - + + + + diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewBootstrap.ts b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewBootstrap.ts new file mode 100644 index 0000000000..2e4165bd9d --- /dev/null +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewBootstrap.ts @@ -0,0 +1,30 @@ +/* + * 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 { importLazyComponent } from '@cloudbeaver/core-blocks'; +import { Bootstrap, injectable } from '@cloudbeaver/core-di'; + +import { SqlExecutionPlanViewService } from './SqlExecutionPlanViewService.js'; + +const ExecutionPlanTreeView = importLazyComponent(() => import('./ExecutionPlanTreeView.js').then(m => m.ExecutionPlanTreeView)); + +@injectable(() => [SqlExecutionPlanViewService]) +export class SqlExecutionPlanViewBootstrap extends Bootstrap { + constructor(private readonly sqlExecutionPlanViewService: SqlExecutionPlanViewService) { + super(); + } + + override register(): void { + this.sqlExecutionPlanViewService.tabs.add({ + key: 'table', + name: 'plugin_sql_execution_plan_view_table', + order: 0, + panel: () => ExecutionPlanTreeView, + }); + } +} diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.ts b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.ts new file mode 100644 index 0000000000..7ec9d84f81 --- /dev/null +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.ts @@ -0,0 +1,21 @@ +/* + * 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 { injectable } from '@cloudbeaver/core-di'; +import { TabsContainer } from '@cloudbeaver/core-ui'; + +import type { ISqlExecutionPlanViewProps } from './ISqlExecutionPlanViewProps.js'; + +@injectable() +export class SqlExecutionPlanViewService { + readonly tabs: TabsContainer; + + constructor() { + this.tabs = new TabsContainer('Execution Plan Views'); + } +} diff --git a/webapp/packages/plugin-sql-editor/src/index.ts b/webapp/packages/plugin-sql-editor/src/index.ts index f6a6a39475..fc2ea4dd5e 100644 --- a/webapp/packages/plugin-sql-editor/src/index.ts +++ b/webapp/packages/plugin-sql-editor/src/index.ts @@ -54,3 +54,6 @@ export * from './SqlEditorService.js'; export * from './SqlEditorSettingsService.js'; export * from './SqlEditorView.js'; export * from './SqlResultTabs/SqlQueryService.js'; +export * from './SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.js'; +export type { ISqlExecutionPlanViewProps } from './SqlResultTabs/ExecutionPlan/ISqlExecutionPlanViewProps.js'; + diff --git a/webapp/packages/plugin-sql-editor/src/locales/en.ts b/webapp/packages/plugin-sql-editor/src/locales/en.ts index 311762bf22..faed9e7eeb 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/en.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/en.ts @@ -27,12 +27,13 @@ export default [ ['sql_editor_sql_statistics_unavailable', 'Statistics is not available'], ['sql_editor_sql_execution_executed_queries', 'Executed queries:'], ['sql_editor_sql_execution_executing', 'Executing...'], - ['sql_execution_plan_placeholder', 'Execution plan is empty'], - ['sql_execution_plan_properties_panel_placeholder', 'There are no items to show'], - ['sql_execution_plan_properties_panel_name', 'Name'], - ['sql_execution_plan_properties_panel_value', 'Value'], - ['sql_execution_plan_properties_panel_general', 'General'], - ['sql_execution_plan_properties_panel_details', 'Details'], + ['plugin_sql_execution_plan_placeholder', 'Execution plan is empty'], + ['plugin_sql_execution_plan_properties_panel_placeholder', 'There are no items to show'], + ['plugin_sql_execution_plan_properties_panel_name', 'Name'], + ['plugin_sql_execution_plan_properties_panel_value', 'Value'], + ['plugin_sql_execution_plan_properties_panel_general', 'General'], + ['plugin_sql_execution_plan_properties_panel_details', 'Details'], + ['plugin_sql_execution_plan_view_table', 'Table'], ['sql_editor_upload_script_tooltip', 'Upload sql script'], ['sql_editor_download_script_tooltip', 'Download sql script'], [ diff --git a/webapp/packages/plugin-sql-editor/src/locales/fr.ts b/webapp/packages/plugin-sql-editor/src/locales/fr.ts index 9f566bf708..2d633dc962 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/fr.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/fr.ts @@ -31,12 +31,13 @@ export default [ ['sql_editor_lock_button', "Verrouiller l'éditeur"], ['sql_editor_sql_statistics_unavailable', 'Les statistiques SQL ne sont pas disponibles'], ['sql_editor_queries_tab_title', 'Requêtes exécutées'], - ['sql_execution_plan_placeholder', "Le plan d'exécution est vide"], - ['sql_execution_plan_properties_panel_placeholder', 'Aucun élément à afficher'], - ['sql_execution_plan_properties_panel_name', 'Nom'], - ['sql_execution_plan_properties_panel_value', 'Valeur'], - ['sql_execution_plan_properties_panel_general', 'Général'], - ['sql_execution_plan_properties_panel_details', 'Détails'], + ['plugin_sql_execution_plan_placeholder', "Le plan d'exécution est vide"], + ['plugin_sql_execution_plan_properties_panel_placeholder', 'Aucun élément à afficher'], + ['plugin_sql_execution_plan_properties_panel_name', 'Nom'], + ['plugin_sql_execution_plan_properties_panel_value', 'Valeur'], + ['plugin_sql_execution_plan_properties_panel_general', 'Général'], + ['plugin_sql_execution_plan_properties_panel_details', 'Détails'], + ['plugin_sql_execution_plan_view_table', 'Tableau'], ['sql_editor_upload_script_tooltip', 'Télécharger le script SQL'], ['sql_editor_upload_script', 'Télécharger le script'], ['sql_editor_upload_script_unsaved_changes_dialog_message', "Certains fichiers n'ont pas été enregistrés. Voulez-vous d'abord les enregistrer ?"], diff --git a/webapp/packages/plugin-sql-editor/src/locales/it.ts b/webapp/packages/plugin-sql-editor/src/locales/it.ts index cf2c12f840..0833b07197 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/it.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/it.ts @@ -27,12 +27,13 @@ export default [ ['sql_editor_sql_statistics_unavailable', 'Statistiche non disponibili'], ['sql_editor_sql_execution_executed_queries', 'Executed queries:'], ['sql_editor_sql_execution_executing', 'Esecuzione in corso...'], - ['sql_execution_plan_placeholder', 'Piano di esecuzione vuoto'], - ['sql_execution_plan_properties_panel_placeholder', 'Non ci sono elementi da mostrare'], - ['sql_execution_plan_properties_panel_name', 'Nome'], - ['sql_execution_plan_properties_panel_value', 'Valore'], - ['sql_execution_plan_properties_panel_general', 'Generale'], - ['sql_execution_plan_properties_panel_details', 'Dettagli'], + ['plugin_sql_execution_plan_placeholder', 'Piano di esecuzione vuoto'], + ['plugin_sql_execution_plan_properties_panel_placeholder', 'Non ci sono elementi da mostrare'], + ['plugin_sql_execution_plan_properties_panel_name', 'Nome'], + ['plugin_sql_execution_plan_properties_panel_value', 'Valore'], + ['plugin_sql_execution_plan_properties_panel_general', 'Generale'], + ['plugin_sql_execution_plan_properties_panel_details', 'Dettagli'], + ['plugin_sql_execution_plan_view_table', 'Tabella'], ['sql_editor_close_result_tabs_dialog_title', 'Confirm closing tabs'], ['plugin_sql_editor_action_overlay_title', 'Action required'], ['plugin_sql_editor_action_overlay_description', 'Please reopen editor to continue working.'], diff --git a/webapp/packages/plugin-sql-editor/src/locales/ru.ts b/webapp/packages/plugin-sql-editor/src/locales/ru.ts index 0e16614799..3fc919c7ed 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/ru.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/ru.ts @@ -27,12 +27,13 @@ export default [ ['sql_editor_sql_statistics_unavailable', 'Статистика не доступна'], ['sql_editor_sql_execution_executed_queries', 'Выполненные запросы:'], ['sql_editor_sql_execution_executing', 'Выполняется...'], - ['sql_execution_plan_placeholder', 'План выполнения пуст'], - ['sql_execution_plan_properties_panel_placeholder', 'Информация не доступна'], - ['sql_execution_plan_properties_panel_name', 'Название'], - ['sql_execution_plan_properties_panel_value', 'Значение'], - ['sql_execution_plan_properties_panel_general', 'Общие'], - ['sql_execution_plan_properties_panel_details', 'Дополнительные'], + ['plugin_sql_execution_plan_placeholder', 'План выполнения пуст'], + ['plugin_sql_execution_plan_properties_panel_placeholder', 'Информация не доступна'], + ['plugin_sql_execution_plan_properties_panel_name', 'Название'], + ['plugin_sql_execution_plan_properties_panel_value', 'Значение'], + ['plugin_sql_execution_plan_properties_panel_general', 'Общие'], + ['plugin_sql_execution_plan_properties_panel_details', 'Дополнительные'], + ['plugin_sql_execution_plan_view_table', 'Таблица'], ['sql_editor_upload_script_tooltip', 'Загрузить sql скрипт'], ['sql_editor_download_script_tooltip', 'Скачать sql скрипт'], ['sql_editor_upload_script_unsaved_changes_dialog_message', 'Текущий скрипт будет перезаписан загруженным. Сохранить текущий?'], diff --git a/webapp/packages/plugin-sql-editor/src/locales/vi.ts b/webapp/packages/plugin-sql-editor/src/locales/vi.ts index 513f0746d9..111b4b62b0 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/vi.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/vi.ts @@ -27,12 +27,13 @@ export default [ ['sql_editor_sql_statistics_unavailable', 'Thống kê không khả dụng'], ['sql_editor_sql_execution_executed_queries', 'Các truy vấn đã thực thi:'], ['sql_editor_sql_execution_executing', 'Đang thực thi...'], - ['sql_execution_plan_placeholder', 'Kế hoạch thực thi trống'], - ['sql_execution_plan_properties_panel_placeholder', 'Không có mục nào để hiển thị'], - ['sql_execution_plan_properties_panel_name', 'Tên'], - ['sql_execution_plan_properties_panel_value', 'Giá trị'], - ['sql_execution_plan_properties_panel_general', 'Chung'], - ['sql_execution_plan_properties_panel_details', 'Chi tiết'], + ['plugin_sql_execution_plan_placeholder', 'Kế hoạch thực thi trống'], + ['plugin_sql_execution_plan_properties_panel_placeholder', 'Không có mục nào để hiển thị'], + ['plugin_sql_execution_plan_properties_panel_name', 'Tên'], + ['plugin_sql_execution_plan_properties_panel_value', 'Giá trị'], + ['plugin_sql_execution_plan_properties_panel_general', 'Chung'], + ['plugin_sql_execution_plan_properties_panel_details', 'Chi tiết'], + ['plugin_sql_execution_plan_view_table', 'Bảng'], ['sql_editor_upload_script_tooltip', 'Tải lên kịch bản SQL'], ['sql_editor_download_script_tooltip', 'Tải xuống kịch bản SQL'], [ diff --git a/webapp/packages/plugin-sql-editor/src/locales/zh.ts b/webapp/packages/plugin-sql-editor/src/locales/zh.ts index 7cec11ca77..4a04931be1 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/zh.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/zh.ts @@ -27,12 +27,13 @@ export default [ ['sql_editor_sql_statistics_unavailable', '统计不可用'], ['sql_editor_sql_execution_queries', '查询:'], ['sql_editor_sql_execution_executing', '执行...'], - ['sql_execution_plan_placeholder', '执行计划为空'], - ['sql_execution_plan_properties_panel_placeholder', '没有要显示的项目'], - ['sql_execution_plan_properties_panel_name', '名称'], - ['sql_execution_plan_properties_panel_value', '值'], - ['sql_execution_plan_properties_panel_general', '一般'], - ['sql_execution_plan_properties_panel_details', '详情'], + ['plugin_sql_execution_plan_placeholder', '执行计划为空'], + ['plugin_sql_execution_plan_properties_panel_placeholder', '没有要显示的项目'], + ['plugin_sql_execution_plan_properties_panel_name', '名称'], + ['plugin_sql_execution_plan_properties_panel_value', '值'], + ['plugin_sql_execution_plan_properties_panel_general', '一般'], + ['plugin_sql_execution_plan_properties_panel_details', '详情'], + ['plugin_sql_execution_plan_view_table', '表格'], ['sql_editor_upload_script_tooltip', '上传SQL脚本'], ['sql_editor_download_script_tooltip', '下载SQL脚本'], ['sql_editor_upload_script_unsaved_changes_dialog_message', '您当前的脚本将被上传的脚本覆盖。要先保存吗?'], diff --git a/webapp/packages/plugin-sql-editor/src/module.ts b/webapp/packages/plugin-sql-editor/src/module.ts index 7b4d16819d..3cf6b6541e 100644 --- a/webapp/packages/plugin-sql-editor/src/module.ts +++ b/webapp/packages/plugin-sql-editor/src/module.ts @@ -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. @@ -15,6 +15,8 @@ import { OutputLogsService } from './SqlResultTabs/OutputLogs/OutputLogsService. import { OutputLogsResource } from './SqlResultTabs/OutputLogs/OutputLogsResource.js'; import { OutputLogsEventHandler } from './SqlResultTabs/OutputLogs/OutputLogsEventHandler.js'; import { SqlExecutionPlanService } from './SqlResultTabs/ExecutionPlan/SqlExecutionPlanService.js'; +import { SqlExecutionPlanViewService } from './SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.js'; +import { SqlExecutionPlanViewBootstrap } from './SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewBootstrap.js'; import { SqlEditorView } from './SqlEditorView.js'; import { SqlEditorSettingsService } from './SqlEditorSettingsService.js'; import { SqlEditorService } from './SqlEditorService.js'; @@ -49,6 +51,8 @@ export default ModuleRegistry.add({ .addSingleton(OutputLogsResource) .addSingleton(OutputLogsEventHandler) .addSingleton(SqlExecutionPlanService) + .addSingleton(SqlExecutionPlanViewService) + .addSingleton(Bootstrap, SqlExecutionPlanViewBootstrap) .addSingleton(SqlEditorView) .addSingleton(SqlEditorSettingsService) .addSingleton(SqlEditorService) diff --git a/webapp/yarn.lock b/webapp/yarn.lock index eba0f28e25..035ded3ebc 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2260,6 +2260,7 @@ __metadata: "@cloudbeaver/tests-runner": "workspace:*" "@cloudbeaver/tsconfig": "workspace:*" "@dbeaver/cli": "workspace:*" + "@dbeaver/js-helpers": "workspace:*" "@dbeaver/react-tests": "workspace:*" "@timohausmann/quadtree-ts": "npm:^2" "@types/md5": "npm:^2"