diff --git a/webapp/common-react/@dbeaver/ui-kit/src/Combobox/Combobox.tsx b/webapp/common-react/@dbeaver/ui-kit/src/Combobox/Combobox.tsx index 0296a9434f..057adc41b2 100644 --- a/webapp/common-react/@dbeaver/ui-kit/src/Combobox/Combobox.tsx +++ b/webapp/common-react/@dbeaver/ui-kit/src/Combobox/Combobox.tsx @@ -30,8 +30,8 @@ export interface ComboboxProps extends Omit { size?: 'small' | 'medium' | 'large' | 'xlarge'; } -export function ComboboxInput({ size, ...props }: ComboboxProps): React.JSX.Element { - return ; +export function ComboboxInput({ size, autoSelect = true, ...props }: ComboboxProps): React.JSX.Element { + return ; } export interface ComboboxPopoverProps extends AriaComboboxPopoverProps { diff --git a/webapp/packages/core-blocks/src/FormControls/Combobox.tsx b/webapp/packages/core-blocks/src/FormControls/Combobox.tsx index 00ef225be2..c0d6928efb 100644 --- a/webapp/packages/core-blocks/src/FormControls/Combobox.tsx +++ b/webapp/packages/core-blocks/src/FormControls/Combobox.tsx @@ -7,7 +7,17 @@ */ import { observer } from 'mobx-react-lite'; import { useCallback, useContext, useId, useState } from 'react'; -import { ComboboxInput, ComboboxItem, clsx, Spinner, ComboboxPopover, ComboboxDisclosure, ComboboxProvider, ComboboxCancel } from '@dbeaver/ui-kit'; +import { + ComboboxInput, + ComboboxItem, + clsx, + Spinner, + ComboboxPopover, + ComboboxDisclosure, + ComboboxProvider, + ComboboxCancel, + useComboboxStore, +} from '@dbeaver/ui-kit'; import { filterLayoutFakeProps, getLayoutProps } from '../Containers/filterLayoutFakeProps.js'; import type { ILayoutSizeProps } from '../Containers/ILayoutSizeProps.js'; @@ -165,12 +175,28 @@ export const Combobox: ComboboxType = observer(function Combobox({ onChange?.(value); } + const comboboxStore = useComboboxStore({ + value: displayValue, + setValue: setInputValue, + selectedValue: selectedValue, + defaultValue: comboboxDefaultValue, + defaultSelectedValue: comboboxDefaultSelectedValue as string, + setSelectedValue: handleSelect, + }); + function handleBlur() { if (!allowCustomValue) { setInputValue(null); } } + function handleKeyDown(event: React.KeyboardEvent) { + if (allowCustomValue && event.key === 'Enter') { + event.preventDefault(); + comboboxStore.setOpen(false); + } + } + function handleSetValueOnClick() { if (allowCustomValue) { return true; @@ -194,14 +220,7 @@ export const Combobox: ComboboxType = observer(function Combobox({ {children} )} - +
{loading ? ( diff --git a/webapp/packages/core-blocks/src/Loader/useAutoLoad.ts b/webapp/packages/core-blocks/src/Loader/useAutoLoad.ts index 713a6866ee..ae51fa21a7 100644 --- a/webapp/packages/core-blocks/src/Loader/useAutoLoad.ts +++ b/webapp/packages/core-blocks/src/Loader/useAutoLoad.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. @@ -31,7 +31,7 @@ export function useAutoLoad( for (const loader of state as ReadonlyArray) { getComputed( // activate mobx subscriptions - () => (!loader.isLoaded() || loader.isOutdated?.() === true) && !loader.isError(), + () => (!loader.isLoaded() || loader.isOutdated?.() === true) && !loader.isError() && loader.isLoadable?.() !== false, ); } @@ -42,7 +42,7 @@ export function useAutoLoad( } for (const loader of state as ReadonlyArray) { - if (loader.isError() || (loader.lazy === true && !lazy)) { + if (loader.isError() || (loader.lazy === true && !lazy) || loader.isLoadable?.() === false) { continue; } diff --git a/webapp/packages/core-ui/src/Form/FormPart.ts b/webapp/packages/core-ui/src/Form/FormPart.ts index 4ae66294c7..f5a90ba741 100644 --- a/webapp/packages/core-ui/src/Form/FormPart.ts +++ b/webapp/packages/core-ui/src/Form/FormPart.ts @@ -75,6 +75,10 @@ export abstract class FormPart impl return false; } + isLoadable(): boolean { + return !this.formState.savingPromise; + } + isLoaded(): boolean { return this.loaded; } @@ -125,7 +129,7 @@ export abstract class FormPart impl } async load(): Promise { - if (this.formState.savingPromise) { + if (!this.isLoadable()) { return; } diff --git a/webapp/packages/core-utils/src/ILoadableState.ts b/webapp/packages/core-utils/src/ILoadableState.ts index 003610d1a7..46616a8cc7 100644 --- a/webapp/packages/core-utils/src/ILoadableState.ts +++ b/webapp/packages/core-utils/src/ILoadableState.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. @@ -18,6 +18,7 @@ export interface ILoadableState { isOutdated?: () => boolean; isCancelled?: () => boolean; + isLoadable?: () => boolean; cancel?: () => void; }