mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
fix: CB-1151 optimisations
This commit is contained in:
@@ -49,5 +49,5 @@ export function useLogViewer() {
|
||||
props.logViewerService.clearLog();
|
||||
this.selectItem(null);
|
||||
},
|
||||
}, {});
|
||||
}, null);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { observer } from 'mobx-react-lite';
|
||||
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { filterLayoutFakeProps } from '../../Containers/filterLayoutFakeProps';
|
||||
import type { ILayoutSizeProps } from '../../Containers/ILayoutSizeProps';
|
||||
import { isControlPresented } from '../isControlPresented';
|
||||
import { CheckboxMarkup, CheckboxMod } from './CheckboxMarkup';
|
||||
@@ -65,6 +66,7 @@ export const Checkbox: CheckboxType = observer(function Checkbox({
|
||||
onChange,
|
||||
...rest
|
||||
}: CheckboxBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps<any>)) {
|
||||
rest = filterLayoutFakeProps(rest);
|
||||
const checkboxState = useCheckboxState({
|
||||
value,
|
||||
defaultValue,
|
||||
|
||||
@@ -11,6 +11,7 @@ import styled, { css } from 'reshadow';
|
||||
|
||||
import { useStyles, composes } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { filterLayoutFakeProps } from '../../Containers/filterLayoutFakeProps';
|
||||
import { baseFormControlStylesNew } from '../baseFormControlStylesNew';
|
||||
import { isControlPresented } from '../isControlPresented';
|
||||
import type { ICheckboxControlledProps, ICheckboxObjectProps } from './Checkbox';
|
||||
@@ -117,6 +118,7 @@ export const SwitchNew: SwitchType = observer(function SwitchNew({
|
||||
name,
|
||||
onChange,
|
||||
});
|
||||
rest = filterLayoutFakeProps(rest);
|
||||
const styles = useStyles(
|
||||
baseFormControlStylesNew,
|
||||
switchStyles,
|
||||
@@ -130,12 +132,12 @@ export const SwitchNew: SwitchType = observer(function SwitchNew({
|
||||
}
|
||||
|
||||
return styled(styles)(
|
||||
<field as="div" className={className}>
|
||||
<switch-body as='div'>
|
||||
<switch-control as='div'>
|
||||
<switch-control-track as='div' />
|
||||
<switch-control-underlay as='div'>
|
||||
<switch-control-thumb as='div' />
|
||||
<field className={className}>
|
||||
<switch-body>
|
||||
<switch-control>
|
||||
<switch-control-track />
|
||||
<switch-control-underlay>
|
||||
<switch-control-thumb />
|
||||
<switch-input
|
||||
as='input'
|
||||
{...rest}
|
||||
@@ -151,7 +153,7 @@ export const SwitchNew: SwitchType = observer(function SwitchNew({
|
||||
</switch-control>
|
||||
<field-label as="label" htmlFor={value || name}>{children}</field-label>
|
||||
</switch-body>
|
||||
<field-description as='div'>{description}</field-description>
|
||||
<field-description>{description}</field-description>
|
||||
</field>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ import styled, { css, use } from 'reshadow';
|
||||
|
||||
import { useStyles, composes } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { filterLayoutFakeProps } from '../Containers/filterLayoutFakeProps';
|
||||
import type { ILayoutSizeProps } from '../Containers/ILayoutSizeProps';
|
||||
import { Icon } from '../Icon';
|
||||
import { IconButton } from '../IconButton';
|
||||
@@ -148,6 +149,7 @@ export const ComboboxNew: ComboboxType = observer(function ComboboxNew({
|
||||
onSwitch,
|
||||
...rest
|
||||
}: ControlledProps<any, any> | ObjectProps<any, any, any>) {
|
||||
rest = filterLayoutFakeProps(rest);
|
||||
const context = useContext(FormContext);
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
const menu = useMenuState({
|
||||
|
||||
@@ -105,9 +105,11 @@ export const InputFieldNew: InputFieldType = observer(function InputFieldNew({
|
||||
className,
|
||||
description,
|
||||
mod,
|
||||
fill,
|
||||
small,
|
||||
medium,
|
||||
large,
|
||||
tiny,
|
||||
autoHide,
|
||||
onChange,
|
||||
...rest
|
||||
@@ -156,7 +158,7 @@ export const InputFieldNew: InputFieldType = observer(function InputFieldNew({
|
||||
const showRevealPasswordButton = rest.type === 'password' && !rest.readOnly;
|
||||
|
||||
return styled(styles)(
|
||||
<field className={className} {...use({ small, medium, large })}>
|
||||
<field className={className} {...use({ small, medium, large, tiny })}>
|
||||
<field-label title={rest.title}>{children}{required && ' *'}</field-label>
|
||||
<input-container>
|
||||
<input
|
||||
|
||||
@@ -75,6 +75,8 @@ export const TextareaNew: TextareaType = observer(function TextareaNew({
|
||||
state,
|
||||
children,
|
||||
className,
|
||||
fill,
|
||||
tiny,
|
||||
small,
|
||||
medium,
|
||||
large,
|
||||
@@ -100,7 +102,7 @@ export const TextareaNew: TextareaType = observer(function TextareaNew({
|
||||
const value = state ? state[name] : controlledValue;
|
||||
|
||||
return styled(useStyles(baseFormControlStylesNew, styles, style))(
|
||||
<field className={className} {...use({ small, medium, large, embedded })}>
|
||||
<field className={className} {...use({ tiny, small, medium, large, embedded })}>
|
||||
<field-label as='label'>{children}</field-label>
|
||||
<textarea
|
||||
{...rest}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogBackdrop,
|
||||
@@ -64,15 +64,17 @@ function NestedDialog({
|
||||
const dialogState = useDialogState({ visible });
|
||||
const styles = useStyles(dialogStyles);
|
||||
|
||||
if (!dialogState.visible
|
||||
useEffect(() => {
|
||||
if (!dialogState.visible
|
||||
&& dialogState.visible !== lastVisibility.current
|
||||
&& !dialog.options?.persistent
|
||||
) {
|
||||
rejectDialog(dialog);
|
||||
} else {
|
||||
lastVisibility.current = visible;
|
||||
dialogState.setVisible(visible);
|
||||
}
|
||||
) {
|
||||
rejectDialog(dialog);
|
||||
} else {
|
||||
lastVisibility.current = visible;
|
||||
dialogState.setVisible(visible);
|
||||
}
|
||||
});
|
||||
|
||||
const handleReject = useCallback(() => rejectDialog(dialog), [dialog, rejectDialog]);
|
||||
const handleResolve = useCallback(
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { useObserver } from 'mobx-react-lite';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import styled from 'reshadow';
|
||||
|
||||
import { Checkbox, Icon, IconOrImage, Radio } from '@cloudbeaver/core-blocks';
|
||||
@@ -22,41 +22,32 @@ interface MenuPanelItemProps {
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
export const MenuPanelItem: React.FC<MenuPanelItemProps> = function MenuPanelItem({
|
||||
export const MenuPanelItem: React.FC<MenuPanelItemProps> = observer(function MenuPanelItem({
|
||||
menuItem,
|
||||
style = [],
|
||||
}) {
|
||||
const { title, panel, icon, isChecked, controlType, separator } = useObserver(() => ({ // TODO: provide title and panel via props
|
||||
title: menuItem.title,
|
||||
panel: menuItem.panel,
|
||||
icon: menuItem.icon,
|
||||
isChecked: menuItem.isChecked,
|
||||
controlType: menuItem.type,
|
||||
separator: menuItem.separator,
|
||||
}));
|
||||
|
||||
let control = null;
|
||||
|
||||
if (controlType === 'radio') {
|
||||
control = <Radio checked={isChecked} mod={['primary', 'small']} ripple={false} />;
|
||||
} else if (controlType === 'checkbox') {
|
||||
control = <Checkbox checked={isChecked} mod={['primary', 'small']} style={style} ripple={false} />;
|
||||
if (menuItem.type === 'radio') {
|
||||
control = <Radio checked={menuItem.isChecked} mod={['primary', 'small']} ripple={false} />;
|
||||
} else if (menuItem.type === 'checkbox') {
|
||||
control = <Checkbox checked={menuItem.isChecked} mod={['primary', 'small']} style={style} ripple={false} />;
|
||||
}
|
||||
|
||||
return styled(useStyles(menuPanelStyles, style))(
|
||||
<menu-panel-item {...use({ separator })}>
|
||||
<menu-panel-item {...use({ separator: menuItem.separator })}>
|
||||
<menu-item-content>
|
||||
{icon ? (
|
||||
<IconOrImage icon={icon} />
|
||||
{menuItem.icon ? (
|
||||
<IconOrImage icon={menuItem.icon} />
|
||||
) : control}
|
||||
</menu-item-content>
|
||||
<menu-item-text>
|
||||
<Translate token={title} />
|
||||
<Translate token={menuItem.title} />
|
||||
</menu-item-text>
|
||||
<menu-item-content>
|
||||
{panel && <Icon name="arrow" viewBox="0 0 16 16" />}
|
||||
{menuItem.panel && <Icon name="arrow" viewBox="0 0 16 16" />}
|
||||
</menu-item-content>
|
||||
</menu-panel-item>
|
||||
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -7,10 +7,8 @@
|
||||
*/
|
||||
|
||||
import { computed } from 'mobx';
|
||||
import { observer, useObserver } from 'mobx-react-lite';
|
||||
import {
|
||||
forwardRef, Ref, useCallback, useEffect, useMemo
|
||||
} from 'react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Ref, useCallback, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
MenuButton,
|
||||
Menu, MenuItem, MenuStateReturn, useMenuState, MenuItemCheckbox, MenuItemRadio
|
||||
@@ -204,7 +202,7 @@ type MenuInnerTriggerProps = Omit<React.ButtonHTMLAttributes<any>, 'style'> & {
|
||||
style?: Style[];
|
||||
};
|
||||
|
||||
export const MenuInnerTrigger = forwardRef(function MenuInnerTrigger(
|
||||
export const MenuInnerTrigger = observer(function MenuInnerTrigger(
|
||||
props: MenuInnerTriggerProps,
|
||||
ref: Ref<HTMLButtonElement>
|
||||
) {
|
||||
@@ -212,7 +210,6 @@ export const MenuInnerTrigger = forwardRef(function MenuInnerTrigger(
|
||||
menuItem, style = [], ...rest
|
||||
} = props;
|
||||
const menu = useMenuState();
|
||||
const panel = useObserver(() => menuItem.panel);
|
||||
|
||||
const handleItemClose = useCallback(() => {
|
||||
menu.hide();
|
||||
@@ -226,7 +223,7 @@ export const MenuInnerTrigger = forwardRef(function MenuInnerTrigger(
|
||||
<MenuPanelItem menuItem={menuItem} style={style} />
|
||||
</box>
|
||||
</MenuButton>
|
||||
<MenuPanel panel={panel!} menu={menu} style={style} onItemClose={handleItemClose} />
|
||||
<MenuPanel panel={menuItem.panel!} menu={menu} style={style} onItemClose={handleItemClose} />
|
||||
</>
|
||||
);
|
||||
});
|
||||
}, { forwardRef: true });
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { useObserver } from 'mobx-react-lite';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import { autorun } from 'mobx';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { create } from 'reshadow';
|
||||
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
@@ -36,10 +36,20 @@ export function useStyles(
|
||||
const [patch, forceUpdate] = useState(0);
|
||||
const loadedStyles = useRef<BaseStyles[]>([]);
|
||||
const themeService = useService(ThemeService);
|
||||
const currentThemeId = useObserver(() => themeService.currentThemeId);
|
||||
const [currentThemeId, setCurrentThemeId] = useState(themeService.currentThemeId);
|
||||
const lastThemeRef = useRef<string>(currentThemeId);
|
||||
const filteredStyles = flat(componentStyles).filter(Boolean) as Style[];
|
||||
|
||||
useEffect(() => {
|
||||
const dispose = autorun(() => {
|
||||
if (currentThemeId !== themeService.currentThemeId) {
|
||||
setCurrentThemeId(themeService.currentThemeId);
|
||||
}
|
||||
});
|
||||
|
||||
return dispose;
|
||||
}, [currentThemeId, themeService]);
|
||||
|
||||
let changed = lastThemeRef.current !== currentThemeId || componentStyles.length !== stylesRef.current.length;
|
||||
for (let i = 0; !changed && i < componentStyles.length; i++) {
|
||||
changed = stylesRef.current[i] !== componentStyles[i];
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observable } from 'mobx';
|
||||
import { observable, untracked } from 'mobx';
|
||||
|
||||
export type MetadataValueGetter<TKey, TValue> = (key: TKey, metadata: MetadataMap<TKey, any>) => TValue;
|
||||
export type DefaultValueGetter<TKey, TValue> = (key: TKey, metadata: MetadataMap<TKey, TValue>) => TValue;
|
||||
@@ -59,9 +59,11 @@ export class MetadataMap<TKey, TValue> {
|
||||
throw new Error('MetadataMap: defaultValue should be provided if defaultValueGetter not set');
|
||||
}
|
||||
|
||||
const value = provider(key, this);
|
||||
this.data.set(key, value);
|
||||
this.length++;
|
||||
untracked(() => {
|
||||
const value = provider(key, this);
|
||||
this.data.set(key, value);
|
||||
this.length++;
|
||||
});
|
||||
return this.data.get(key)!;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,12 +82,18 @@ export class DataPresentationService {
|
||||
resultIndex: number,
|
||||
): IDataPresentation[] {
|
||||
return Array.from(this.dataPresentations.values()).filter(presentation => {
|
||||
if (
|
||||
presentation.dataFormat !== undefined
|
||||
&& supportedDataFormats.includes(presentation.dataFormat)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (presentation.type !== type || presentation.hidden?.(dataFormat, model, resultIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return presentation.dataFormat === undefined
|
||||
|| supportedDataFormats.includes(presentation.dataFormat);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+27
-9
@@ -85,18 +85,34 @@ export class ResultSetSelectAction extends DatabaseDataAction<any, IDatabaseResu
|
||||
return this.selectedElements.get(ResultSetDataKeysUtils.serialize(row)) || [];
|
||||
}
|
||||
|
||||
set(key: IResultSetPartialKey, selected: boolean): void {
|
||||
set(key: IResultSetPartialKey, selected: boolean, silent?: boolean): void {
|
||||
if (key.row === undefined) {
|
||||
for (const row of this.data.rowKeys) {
|
||||
this.set({ row, column: key.column }, selected);
|
||||
this.set({ row, column: key.column }, selected, true);
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
this.actions.execute({
|
||||
type: 'select',
|
||||
resultId: this.result.id,
|
||||
key,
|
||||
selected,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.column === undefined) {
|
||||
for (const column of this.data.columnKeys) {
|
||||
this.set({ row: key.row, column }, selected);
|
||||
this.set({ row: key.row, column }, selected, true);
|
||||
}
|
||||
if (!silent) {
|
||||
this.actions.execute({
|
||||
type: 'select',
|
||||
resultId: this.result.id,
|
||||
key,
|
||||
selected,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -123,12 +139,14 @@ export class ResultSetSelectAction extends DatabaseDataAction<any, IDatabaseResu
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.actions.execute({
|
||||
type: 'select',
|
||||
resultId: this.result.id,
|
||||
key,
|
||||
selected,
|
||||
});
|
||||
if (!silent) {
|
||||
this.actions.execute({
|
||||
type: 'select',
|
||||
resultId: this.result.id,
|
||||
key,
|
||||
selected,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import styled, { css, use } from 'reshadow';
|
||||
|
||||
import { IconOrImage, SubmittingForm, ToolsPanel } from '@cloudbeaver/core-blocks';
|
||||
import { composes, useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
import type { IDatabaseDataModel } from '../../DatabaseDataModel/IDatabaseDataModel';
|
||||
import { getDefaultRowsCount } from '../../getDefaultRowsCount';
|
||||
import { TableFooterMenu } from './TableFooterMenu/TableFooterMenu';
|
||||
|
||||
const tableFooterStyles = composes(
|
||||
@@ -77,16 +78,19 @@ export const TableFooter = observer(function TableFooter({
|
||||
model,
|
||||
}: TableFooterProps) {
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
const [limit, setLimit] = useState(model.countGain + '');
|
||||
|
||||
const handleChange = useCallback(
|
||||
() => {
|
||||
if (!ref.current) {
|
||||
return;
|
||||
}
|
||||
const value = parseInt(ref.current.value, 10);
|
||||
|
||||
const value = getDefaultRowsCount(parseInt(ref.current.value, 10));
|
||||
|
||||
setLimit(value + '');
|
||||
if (model.countGain !== value) {
|
||||
model.setCountGain(value)
|
||||
.reload();
|
||||
model.setCountGain(value).reload();
|
||||
}
|
||||
},
|
||||
[model]
|
||||
@@ -104,8 +108,9 @@ export const TableFooter = observer(function TableFooter({
|
||||
<input
|
||||
ref={ref}
|
||||
type="number"
|
||||
value={model.countGain}
|
||||
value={limit}
|
||||
disabled={disabled}
|
||||
onChange={e => setLimit(e.target.value)}
|
||||
onBlur={handleChange}
|
||||
{...use({ mod: 'surface' })}
|
||||
/>
|
||||
|
||||
@@ -13,6 +13,9 @@ const fetchingSettings = {
|
||||
};
|
||||
|
||||
export function getDefaultRowsCount(count?: number): number {
|
||||
if (typeof count === 'number' && Number.isNaN(count)) {
|
||||
count = 0;
|
||||
}
|
||||
return count !== undefined
|
||||
? Math.max(
|
||||
fetchingSettings.fetchMin,
|
||||
|
||||
Reference in New Issue
Block a user