+
{children}
);
diff --git a/webapp/packages/core-blocks/src/Containers/Grid.tsx b/webapp/packages/core-blocks/src/Containers/Grid.tsx
deleted file mode 100644
index a56392bac2..0000000000
--- a/webapp/packages/core-blocks/src/Containers/Grid.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * CloudBeaver - Cloud Database Manager
- * Copyright (C) 2020-2021 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 { IGridOptions, ILayoutSizeProps } from './LayoutProps';
-
-interface Props extends ILayoutSizeProps, IGridOptions {
- className?: string;
- limitWidth?: boolean;
-}
-
-export const Grid: React.FC
= function Grid({ children, className }) {
- return (
-
- {children}
-
- );
-};
diff --git a/webapp/packages/core-blocks/src/Containers/Group.tsx b/webapp/packages/core-blocks/src/Containers/Group.tsx
index 66e8dc2db7..9f56c2222c 100644
--- a/webapp/packages/core-blocks/src/Containers/Group.tsx
+++ b/webapp/packages/core-blocks/src/Containers/Group.tsx
@@ -6,12 +6,12 @@
* you may not use this file except in compliance with the License.
*/
-import type { IGridOptions, ILayoutSizeProps } from './LayoutProps';
+import type { IContainerProps } from './IContainerProps';
-interface Props extends ILayoutSizeProps, IGridOptions {
+interface Props extends IContainerProps {
className?: string;
form?: boolean;
- keepSize?: boolean;
+ center?: boolean;
}
export const Group: React.FC = function Group({ children, className }) {
diff --git a/webapp/packages/core-blocks/src/Containers/GroupItem.tsx b/webapp/packages/core-blocks/src/Containers/GroupItem.tsx
index 1bb7664614..7fda0f808b 100644
--- a/webapp/packages/core-blocks/src/Containers/GroupItem.tsx
+++ b/webapp/packages/core-blocks/src/Containers/GroupItem.tsx
@@ -6,7 +6,9 @@
* you may not use this file except in compliance with the License.
*/
-interface Props {
+import type { ILayoutSizeProps } from './ILayoutSizeProps';
+
+interface Props extends ILayoutSizeProps {
className?: string;
}
diff --git a/webapp/packages/core-blocks/src/Containers/GroupTitle.tsx b/webapp/packages/core-blocks/src/Containers/GroupTitle.tsx
index b5cc1cd3dc..15d5df45f4 100644
--- a/webapp/packages/core-blocks/src/Containers/GroupTitle.tsx
+++ b/webapp/packages/core-blocks/src/Containers/GroupTitle.tsx
@@ -6,9 +6,9 @@
* you may not use this file except in compliance with the License.
*/
-import type { IGridItemsLayoutProps } from './LayoutProps';
+import type { ILayoutSizeProps } from './ILayoutSizeProps';
-interface Props extends IGridItemsLayoutProps {
+interface Props extends ILayoutSizeProps {
className?: string;
}
diff --git a/webapp/packages/core-blocks/src/Containers/IContainerProps.ts b/webapp/packages/core-blocks/src/Containers/IContainerProps.ts
index d6f35a50a9..7b3aaf17aa 100644
--- a/webapp/packages/core-blocks/src/Containers/IContainerProps.ts
+++ b/webapp/packages/core-blocks/src/Containers/IContainerProps.ts
@@ -6,11 +6,11 @@
* you may not use this file except in compliance with the License.
*/
-import type { IGridItemsLayoutProps, ILayoutSizeProps } from './LayoutProps';
+import type { ILayoutSizeProps } from './ILayoutSizeProps';
-export interface IContainerProps extends ILayoutSizeProps, IGridItemsLayoutProps {
- className?: string;
- horizontal?: boolean;
+export interface IContainerProps extends ILayoutSizeProps, React.HTMLAttributes {
+ baseline?: boolean;
+ vertical?: boolean;
wrap?: boolean;
overflow?: boolean;
parent?: boolean;
diff --git a/webapp/packages/core-blocks/src/Containers/ILayoutSizeProps.ts b/webapp/packages/core-blocks/src/Containers/ILayoutSizeProps.ts
new file mode 100644
index 0000000000..f81363ab72
--- /dev/null
+++ b/webapp/packages/core-blocks/src/Containers/ILayoutSizeProps.ts
@@ -0,0 +1,16 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2021 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 interface ILayoutSizeProps {
+ keepSize?: boolean;
+ tiny?: boolean;
+ small?: boolean;
+ medium?: boolean;
+ large?: boolean;
+ fill?: boolean;
+}
diff --git a/webapp/packages/core-blocks/src/Containers/LayoutProps.ts b/webapp/packages/core-blocks/src/Containers/LayoutProps.ts
deleted file mode 100644
index 54707182ef..0000000000
--- a/webapp/packages/core-blocks/src/Containers/LayoutProps.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * CloudBeaver - Cloud Database Manager
- * Copyright (C) 2020-2021 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 interface ILayoutSizeProps {
- keepSize?: boolean;
- limitWidth?: boolean;
- small?: boolean;
- medium?: boolean;
- large?: boolean;
- grow?: boolean;
-}
-
-export interface IGridItemsLayoutProps {
- gridItemMax?: boolean;
-}
-
-export interface IFlexItemsLayoutProps {
- flexItemKeepSize?: boolean;
- flexItemTiny?: boolean;
- flexItemSmall?: boolean;
- flexItemMedium?: boolean;
- flexItemLarge?: boolean;
-}
-export interface IGridOptions {
- noGap?: boolean;
- horizontal?: boolean;
- center?: boolean;
-}
diff --git a/webapp/packages/core-blocks/src/FormControls/Checkboxes/Checkbox.tsx b/webapp/packages/core-blocks/src/FormControls/Checkboxes/Checkbox.tsx
index 9b32abc2f9..f7b575057a 100644
--- a/webapp/packages/core-blocks/src/FormControls/Checkboxes/Checkbox.tsx
+++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/Checkbox.tsx
@@ -7,119 +7,77 @@
*/
import { observer } from 'mobx-react-lite';
-import { useCallback, useContext, useState } from 'react';
-import type { IGridItemsLayoutProps } from '../../Containers/LayoutProps';
-import { FormContext } from '../FormContext';
+import type { ComponentStyle } from '@cloudbeaver/core-theming';
+
+import type { ILayoutSizeProps } from '../../Containers/ILayoutSizeProps';
import { isControlPresented } from '../isControlPresented';
-import { CheckboxMarkup } from './CheckboxMarkup';
+import { CheckboxMarkup, CheckboxMod } from './CheckboxMarkup';
+import { CheckboxOnChangeEvent, useCheckboxState } from './useCheckboxState';
-export type CheckboxMod = 'primary' | 'small';
-
-export type CheckboxBaseProps = Omit, 'onChange' | 'type' | 'value' | 'checked'> & IGridItemsLayoutProps & {
- value?: string;
- checkboxLabel?: string;
- long?: boolean;
+export interface CheckboxBaseProps {
+ label?: string;
mod?: CheckboxMod[];
ripple?: boolean;
-};
-
-type CheckboxOnChangeEvent =
- ((value: boolean, name: T) => void) |
- ((value: boolean, name: T) => boolean);
-
-export type CheckboxControlledProps = CheckboxBaseProps & {
- checked?: boolean;
indeterminate?: boolean;
- onChange?: CheckboxOnChangeEvent;
+ style?: ComponentStyle;
+}
+
+export type CheckboxInputProps = Omit, 'onChange' | 'type' | 'value' | 'checked' | 'id' | 'style'> & ILayoutSizeProps;
+
+export interface ICheckboxControlledProps extends CheckboxInputProps {
+ value?: string;
state?: never;
+ checked?: boolean;
+ onChange?: CheckboxOnChangeEvent;
autoHide?: never;
-};
+}
-export type CheckboxObjectProps = CheckboxBaseProps & {
- name: TKey;
- state: TState;
- onChange?: CheckboxOnChangeEvent;
+export interface ICheckboxObjectProps extends CheckboxInputProps {
+ value?: string;
+ state: Partial>;
checked?: never;
- indeterminate?: boolean;
+ onChange?: CheckboxOnChangeEvent;
autoHide?: boolean;
-};
+ name: TKey;
+}
export interface CheckboxType {
- (props: CheckboxControlledProps): React.ReactElement | null;
- (props: CheckboxObjectProps): React.ReactElement | null;
+ (props: CheckboxBaseProps & ICheckboxControlledProps): React.ReactElement | null;
+ (props: CheckboxBaseProps & ICheckboxObjectProps): React.ReactElement | null;
}
export const Checkbox: CheckboxType = observer(function Checkbox({
name,
value,
state,
- checkboxLabel,
- checked: checkedControlled,
+ label,
+ checked,
children,
mod,
ripple,
className,
- long,
autoHide,
onChange,
...rest
-}: CheckboxControlledProps | CheckboxObjectProps) {
- const [count, refresh] = useState(0);
- const context = useContext(FormContext);
- const handleChange = useCallback((event: React.ChangeEvent) => {
- if (state) {
- if (Array.isArray(state[name])) {
- state[name] = (state[name] as string[]).filter(item => item !== value);
- if (event.target.checked) {
- state[name].push(value);
- }
- } else {
- state[name] = event.target.checked;
- }
- }
- if (onChange) {
- const checked = onChange(event.target.checked, name);
-
- if (typeof checked === 'boolean') {
- event.target.checked = checked;
- }
- }
- if (context) {
- context.onChange(event.target.checked, name);
- }
- refresh(count + 1);
- }, [state, name, value, onChange, context, count]);
+}: CheckboxBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps)) {
+ const checkboxState = useCheckboxState({ value, checked, state, name, onChange });
if (autoHide && !isControlPresented(name, state)) {
return null;
}
- let checked = checkedControlled;
-
- if (state) {
- if (typeof state[name] === 'string') {
- checked = state[name] === 'true';
- } else {
- checked = state[name];
- }
- }
-
- if (Array.isArray(checked)) {
- checked = checked.includes(value);
- }
-
return (
);
});
diff --git a/webapp/packages/core-blocks/src/FormControls/Checkboxes/CheckboxMarkup.tsx b/webapp/packages/core-blocks/src/FormControls/Checkboxes/CheckboxMarkup.tsx
index 5c7de15f09..d719047d18 100644
--- a/webapp/packages/core-blocks/src/FormControls/Checkboxes/CheckboxMarkup.tsx
+++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/CheckboxMarkup.tsx
@@ -8,9 +8,9 @@
import styled, { css } from 'reshadow';
-import { Composes, composes, useStyles } from '@cloudbeaver/core-theming';
+import { ComponentStyle, Composes, composes, useStyles } from '@cloudbeaver/core-theming';
-import type { CheckboxMod } from './Checkbox';
+export type CheckboxMod = 'primary' | 'surface' | 'small';
const checkboxStyles = composes(
css`
@@ -56,6 +56,13 @@ const checkboxMod: Record = {
}
`
),
+ surface: composes(
+ css`
+ checkbox {
+ composes: theme-checkbox_surface from global;
+ }
+ `
+ ),
small: composes(
css`
checkbox {
@@ -94,18 +101,26 @@ const checkboxState = {
),
};
-interface ICheckboxMarkupProps extends React.InputHTMLAttributes {
+interface ICheckboxMarkupProps extends Omit, 'style'> {
label?: string;
indeterminate?: boolean;
ripple?: boolean;
mod?: CheckboxMod[];
+ style?: ComponentStyle;
}
export const CheckboxMarkup: React.FC = function CheckboxMarkup({
- label, className, title, mod = ['primary'], ripple = true, ...rest
+ label, className, title, mod = ['primary'], ripple = true, style, ...rest
}) {
- return styled(useStyles(checkboxStyles, ...(mod || []).map(mod => checkboxMod[mod]), rest.disabled
- && checkboxState.disabled, rest.checked && checkboxState.checked))(
+ return styled(
+ useStyles(
+ checkboxStyles,
+ ...(mod || []).map(mod => checkboxMod[mod]),
+ rest.disabled && checkboxState.disabled,
+ rest.checked && checkboxState.checked,
+ style
+ )
+ )(
diff --git a/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckbox.tsx b/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckbox.tsx
index 7d99afd0f4..9e2cf2b8bf 100644
--- a/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckbox.tsx
+++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckbox.tsx
@@ -6,13 +6,13 @@
* you may not use this file except in compliance with the License.
*/
-import styled, { use, css } from 'reshadow';
+import styled, { css } from 'reshadow';
import { useStyles } from '@cloudbeaver/core-theming';
import { baseFormControlStyles } from '../baseFormControlStyles';
import { isControlPresented } from '../isControlPresented';
-import { Checkbox, CheckboxType, CheckboxControlledProps, CheckboxObjectProps } from './Checkbox';
+import { Checkbox, CheckboxBaseProps, CheckboxType, ICheckboxControlledProps, ICheckboxObjectProps } from './Checkbox';
const fieldCheckboxStyles = css`
Checkbox {
@@ -21,26 +21,20 @@ const fieldCheckboxStyles = css`
`;
export const FieldCheckbox: CheckboxType = function FieldCheckbox({
- checked: checkedControlled,
children,
className,
- long,
- autoHide,
...rest
-}: CheckboxControlledProps | CheckboxObjectProps) {
+}: CheckboxBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps)) {
const styles = useStyles(baseFormControlStyles, fieldCheckboxStyles);
- if (autoHide && !isControlPresented(rest.name, rest.state)) {
+ if (rest.autoHide && !isControlPresented(rest.name, rest.state)) {
return null;
}
return styled(styles)(
-
+
{children}
-
+
);
};
diff --git a/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckboxNew.tsx b/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckboxNew.tsx
index a48f10bf75..82c135094e 100644
--- a/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckboxNew.tsx
+++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckboxNew.tsx
@@ -12,7 +12,7 @@ import { useStyles } from '@cloudbeaver/core-theming';
import { baseFormControlStylesNew } from '../baseFormControlStylesNew';
import { isControlPresented } from '../isControlPresented';
-import { Checkbox, CheckboxType, CheckboxControlledProps, CheckboxObjectProps } from './Checkbox';
+import { Checkbox, CheckboxBaseProps, CheckboxType, ICheckboxControlledProps, ICheckboxObjectProps } from './Checkbox';
const fieldCheckboxStyles = css`
Checkbox {
@@ -24,31 +24,28 @@ const fieldCheckboxStyles = css`
white-space: pre-wrap;
}
field-label {
- padding-left: 10px;
composes: theme-typography--body2 from global;
+ cursor: pointer;
+ user-select: none;
+ padding-left: 10px;
line-height: 16px;
}
`;
export const FieldCheckboxNew: CheckboxType = function FieldCheckboxNew({
- checked: checkedControlled,
children,
className,
- autoHide,
...rest
-}: CheckboxControlledProps | CheckboxObjectProps) {
+}: CheckboxBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps)) {
const styles = useStyles(baseFormControlStylesNew, fieldCheckboxStyles);
- if (autoHide && !isControlPresented(rest.name, rest.state)) {
+ if (rest.autoHide && !isControlPresented(rest.name, rest.state)) {
return null;
}
return styled(styles)(
-
+
{children}
);
diff --git a/webapp/packages/core-blocks/src/FormControls/Switch.tsx b/webapp/packages/core-blocks/src/FormControls/Checkboxes/Switch.tsx
similarity index 59%
rename from webapp/packages/core-blocks/src/FormControls/Switch.tsx
rename to webapp/packages/core-blocks/src/FormControls/Checkboxes/Switch.tsx
index 2e9de5093b..75e8698515 100644
--- a/webapp/packages/core-blocks/src/FormControls/Switch.tsx
+++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/Switch.tsx
@@ -7,14 +7,14 @@
*/
import { observer } from 'mobx-react-lite';
-import { useCallback, useContext } from 'react';
-import styled, { css, use } from 'reshadow';
+import styled, { css } from 'reshadow';
import { useStyles, composes } from '@cloudbeaver/core-theming';
-import { baseFormControlStyles } from './baseFormControlStyles';
-import { FormContext } from './FormContext';
-import { isControlPresented } from './isControlPresented';
+import { baseFormControlStyles } from '../baseFormControlStyles';
+import { isControlPresented } from '../isControlPresented';
+import type { ICheckboxControlledProps, ICheckboxObjectProps } from './Checkbox';
+import { useCheckboxState } from './useCheckboxState';
const switchStyles = composes(
css`
@@ -77,79 +77,47 @@ const switchState = {
),
};
-type BaseProps = Omit, 'onChange' | 'type' | 'value' | 'checked'> & {
+interface IBaseProps {
mod?: Array;
label?: string;
description?: string;
- long?: boolean;
-};
-
-type ControlledProps = BaseProps & {
- checked?: boolean;
- onChange?: (value: boolean, name?: string) => any;
-
- state?: never;
- autoHide?: never;
-};
-
-type ObjectProps = BaseProps & {
- name: TKey;
- state: TState;
- onChange?: (value: boolean, name: TKey) => any;
- autoHide?: boolean;
-
- checked?: never;
-};
+}
interface SwitchType {
- (props: ControlledProps): React.ReactElement | null;
- (props: ObjectProps): React.ReactElement | null;
+ (props: IBaseProps & ICheckboxControlledProps): React.ReactElement | null;
+ (props: IBaseProps & ICheckboxObjectProps): React.ReactElement | null;
}
export const Switch: SwitchType = observer(function Switch({
name,
- id,
+ value,
label,
description,
state,
- checked: checkedControlled,
+ checked,
className,
children,
onChange,
mod = [],
- long,
autoHide,
disabled,
...rest
-}: ControlledProps | ObjectProps) {
- const context = useContext(FormContext);
- const checked = state ? state[name] : checkedControlled;
+}: IBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps)) {
+ const checkboxState = useCheckboxState({ value, checked, state, name, onChange });
const styles = useStyles(
baseFormControlStyles,
switchStyles,
...mod.map(mod => switchMod[mod]),
disabled && switchState.disabled,
- checked && switchState.checked
+ checkboxState.checked && switchState.checked
);
- const handleChange = useCallback((event: React.ChangeEvent) => {
- if (state) {
- state[name] = event.target.checked;
- }
- if (onChange) {
- onChange(event.target.checked, name);
- }
- if (context) {
- context.onChange(event.target.checked, name);
- }
- }, [state, name, context, onChange]);
-
if (autoHide && !isControlPresented(name, state)) {
return null;
}
return styled(styles)(
-
+
{children}
@@ -159,16 +127,16 @@ export const Switch: SwitchType = observer(function Switch({
as='input'
{...rest}
type="checkbox"
- id={id}
+ id={value || name}
role="switch"
- aria-checked={checked}
- checked={checked}
+ aria-checked={checkboxState.checked}
+ checked={checkboxState.checked}
disabled={disabled}
- onChange={handleChange}
+ onChange={checkboxState.change}
/>
-
+
{description}
);
diff --git a/webapp/packages/core-blocks/src/FormControls/SwitchNew.tsx b/webapp/packages/core-blocks/src/FormControls/Checkboxes/SwitchNew.tsx
similarity index 62%
rename from webapp/packages/core-blocks/src/FormControls/SwitchNew.tsx
rename to webapp/packages/core-blocks/src/FormControls/Checkboxes/SwitchNew.tsx
index 84bfb7e342..02a797cb9e 100644
--- a/webapp/packages/core-blocks/src/FormControls/SwitchNew.tsx
+++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/SwitchNew.tsx
@@ -7,14 +7,14 @@
*/
import { observer } from 'mobx-react-lite';
-import { useCallback, useContext } from 'react';
import styled, { css } from 'reshadow';
import { useStyles, composes } from '@cloudbeaver/core-theming';
-import { baseFormControlStylesNew } from './baseFormControlStylesNew';
-import { FormContext } from './FormContext';
-import { isControlPresented } from './isControlPresented';
+import { baseFormControlStylesNew } from '../baseFormControlStylesNew';
+import { isControlPresented } from '../isControlPresented';
+import type { ICheckboxControlledProps, ICheckboxObjectProps } from './Checkbox';
+import { useCheckboxState } from './useCheckboxState';
const switchStyles = composes(
css`
@@ -43,11 +43,13 @@ const switchStyles = composes(
align-items: center;
}
field-label {
+ composes: theme-typography--body1 from global;
+ cursor: pointer;
+ user-select: none;
display: block;
- padding-right: 17px;
+ padding-left: 18px;
min-width: 50px;
white-space: pre-wrap;
- composes: theme-typography--body1 from global;
font-weight: 500;
}
`
@@ -80,39 +82,22 @@ const switchState = {
),
};
-type BaseProps = Omit, 'onChange' | 'type' | 'value' | 'checked'> & {
+interface IBaseProps {
mod?: Array;
description?: string;
-};
-
-type ControlledProps = BaseProps & {
- checked?: boolean;
- onChange?: (value: boolean, name?: string) => any;
-
- state?: never;
- autoHide?: never;
-};
-
-type ObjectProps = BaseProps & {
- name: TKey;
- state: TState;
- onChange?: (value: boolean, name: TKey) => any;
- autoHide?: boolean;
-
- checked?: never;
-};
+}
interface SwitchType {
- (props: ControlledProps): React.ReactElement | null;
- (props: ObjectProps): React.ReactElement | null;
+ (props: IBaseProps & ICheckboxControlledProps): React.ReactElement | null;
+ (props: IBaseProps & ICheckboxObjectProps): React.ReactElement | null;
}
export const SwitchNew: SwitchType = observer(function SwitchNew({
name,
- id,
+ value,
description,
state,
- checked: checkedControlled,
+ checked,
className,
children,
onChange,
@@ -120,29 +105,16 @@ export const SwitchNew: SwitchType = observer(function SwitchNew({
autoHide,
disabled,
...rest
-}: ControlledProps | ObjectProps) {
- const context = useContext(FormContext);
- const checked = state ? state[name] : checkedControlled;
+}: IBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps)) {
+ const checkboxState = useCheckboxState({ value, checked, state, name, onChange });
const styles = useStyles(
baseFormControlStylesNew,
switchStyles,
...mod.map(mod => switchMod[mod]),
disabled && switchState.disabled,
- checked && switchState.checked
+ checkboxState.checked && switchState.checked
);
- const handleChange = useCallback((event: React.ChangeEvent) => {
- if (state) {
- state[name] = event.target.checked;
- }
- if (onChange) {
- onChange(event.target.checked, name);
- }
- if (context) {
- context.onChange(event.target.checked, name);
- }
- }, [state, name, context, onChange]);
-
if (autoHide && !isControlPresented(name, state)) {
return null;
}
@@ -150,7 +122,6 @@ export const SwitchNew: SwitchType = observer(function SwitchNew({
return styled(styles)(
- {children}
@@ -159,15 +130,16 @@ export const SwitchNew: SwitchType = observer(function SwitchNew({
as='input'
{...rest}
type="checkbox"
- id={id}
+ id={value || name}
role="switch"
- aria-checked={checked}
- checked={checked}
+ aria-checked={checkboxState.checked}
+ checked={checkboxState.checked}
disabled={disabled}
- onChange={handleChange}
+ onChange={checkboxState.change}
/>
+ {children}
{description}
diff --git a/webapp/packages/core-blocks/src/FormControls/Checkboxes/useCheckboxState.ts b/webapp/packages/core-blocks/src/FormControls/Checkboxes/useCheckboxState.ts
new file mode 100644
index 0000000000..a2b3396b12
--- /dev/null
+++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/useCheckboxState.ts
@@ -0,0 +1,85 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2021 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import { useContext, useState } from 'react';
+
+import { useObjectRef } from '../../useObjectRef';
+import { FormContext } from '../FormContext';
+
+export type CheckboxOnChangeEvent =(value: boolean, name: T) => void;
+
+export type CheckboxStateOptions = {
+ value: string | undefined;
+ checked: boolean | undefined;
+} & (
+ {
+ state: undefined;
+ name: string | undefined;
+ onChange: CheckboxOnChangeEvent | undefined;
+ }
+ | {
+ state: Record | undefined;
+ name: TKey;
+ onChange: CheckboxOnChangeEvent | undefined;
+ }
+);
+
+interface ICheckboxState {
+ checked: boolean;
+ change: (event: React.ChangeEvent) => void;
+}
+
+export function useCheckboxState(options: CheckboxStateOptions): ICheckboxState {
+ const [count, refresh] = useState(0);
+ const context = useContext(FormContext);
+ const optionsRef = useObjectRef({ ...options, context, count });
+ const { state, name, value } = optionsRef;
+
+ let checked = optionsRef.checked ?? false;
+
+ if (state !== undefined && name !== undefined) {
+ const currentState = state[name as TKey];
+
+ if (typeof value === 'string' && Array.isArray(currentState)) {
+ checked = currentState.includes(value);
+ } else if (typeof currentState === 'string') {
+ checked = currentState.toLowerCase() === 'true';
+ } else {
+ checked = !!currentState;
+ }
+ }
+
+ return useObjectRef({
+ checked,
+ change(event: React.ChangeEvent) {
+ const { state, name, value, onChange, count, context } = optionsRef;
+ const checked = event.target.checked;
+
+ if (state !== undefined && name !== undefined) {
+ const currentState = state[name as TKey];
+
+ if (typeof value === 'string' && Array.isArray(currentState)) {
+ const elementIndex = currentState.indexOf(value);
+
+ if (checked && elementIndex === -1) {
+ currentState.push(value);
+ } else if (elementIndex !== -1) {
+ currentState.splice(elementIndex, 1);
+ }
+ } else {
+ state[name as TKey] = checked;
+ }
+ }
+
+ onChange?.(checked, name as TKey);
+ context?.change(checked, name);
+
+ refresh(count + 1);
+ },
+ }, { checked });
+}
diff --git a/webapp/packages/core-blocks/src/FormControls/Combobox.tsx b/webapp/packages/core-blocks/src/FormControls/Combobox.tsx
index 53d42610a1..182bf557e5 100644
--- a/webapp/packages/core-blocks/src/FormControls/Combobox.tsx
+++ b/webapp/packages/core-blocks/src/FormControls/Combobox.tsx
@@ -177,7 +177,7 @@ export const Combobox: ComboboxType = observer(function Combobox({
onSelect(null, name, value);
}
if (context) {
- context.onChange(null, name);
+ context.change(null, name);
}
setSearchValue('');
},
@@ -195,7 +195,7 @@ export const Combobox: ComboboxType = observer(function Combobox({
onSelect(id, name, value);
}
if (context) {
- context.onChange(id, name);
+ context.change(id, name);
}
},
[value, state, name, menu, context, onSelect]
diff --git a/webapp/packages/core-blocks/src/FormControls/ComboboxNew.tsx b/webapp/packages/core-blocks/src/FormControls/ComboboxNew.tsx
index c97533f8d4..6546b39304 100644
--- a/webapp/packages/core-blocks/src/FormControls/ComboboxNew.tsx
+++ b/webapp/packages/core-blocks/src/FormControls/ComboboxNew.tsx
@@ -20,6 +20,7 @@ import styled, { css, use } from 'reshadow';
import { useStyles, composes } from '@cloudbeaver/core-theming';
+import type { ILayoutSizeProps } from '../Containers/ILayoutSizeProps';
import { IconButton } from '../IconButton';
import { Icon } from '../Icons/Icon';
import { baseFormControlStylesNew } from './baseFormControlStylesNew';
@@ -100,7 +101,7 @@ const styles = composes(
`
);
-type BaseProps = Omit, 'onChange' | 'onSelect' | 'name' | 'value'> & {
+type BaseProps = Omit, 'onChange' | 'onSelect' | 'name' | 'value'> & ILayoutSizeProps & {
propertyName?: string;
items: TValue[];
searchable?: boolean;
@@ -194,7 +195,7 @@ export const ComboboxNew: ComboboxType = observer(function ComboboxNew({
onSelect(null, name, value);
}
if (context) {
- context.onChange(null, name);
+ context.change(null, name);
}
setSearchValue('');
},
@@ -212,7 +213,7 @@ export const ComboboxNew: ComboboxType = observer(function ComboboxNew({
onSelect(id, name, value);
}
if (context) {
- context.onChange(id, name);
+ context.change(id, name);
}
setSearchValue('');
},
@@ -240,6 +241,7 @@ export const ComboboxNew: ComboboxType = observer(function ComboboxNew({
name={name}
title={title}
value={inputValue}
+ disabled={disabled}
readOnly={readOnly}
onChange={handleChange}
{...rest}
diff --git a/webapp/packages/core-blocks/src/FormControls/FormContext.ts b/webapp/packages/core-blocks/src/FormControls/FormContext.ts
index e8c459a9ee..bb99953047 100644
--- a/webapp/packages/core-blocks/src/FormControls/FormContext.ts
+++ b/webapp/packages/core-blocks/src/FormControls/FormContext.ts
@@ -8,8 +8,18 @@
import { createContext } from 'react';
+import type { IExecutor } from '@cloudbeaver/core-executor';
+
+type ChangeHandler = (value: string | number | boolean | null | undefined, name: string | undefined) => void;
+
+export interface IChangeData {
+ value: string | number | boolean | null | undefined;
+ name: string | undefined;
+}
+
export interface IFormContext {
- onChange: (value: string | number | boolean | null | undefined, name: string | undefined) => void;
+ changeExecutor: IExecutor;
+ change: ChangeHandler;
}
export const FormContext = createContext(null);
diff --git a/webapp/packages/core-blocks/src/FormControls/InputField.tsx b/webapp/packages/core-blocks/src/FormControls/InputField.tsx
index 2504899ab0..5cda6bd2c6 100644
--- a/webapp/packages/core-blocks/src/FormControls/InputField.tsx
+++ b/webapp/packages/core-blocks/src/FormControls/InputField.tsx
@@ -72,7 +72,7 @@ export const InputField: InputFieldType = observer(function InputField({
onChange(event.target.value, name);
}
if (context) {
- context.onChange(event.target.value, name);
+ context.change(event.target.value, name);
}
}, [state, name, context, onChange]);
@@ -84,7 +84,7 @@ export const InputField: InputFieldType = observer(function InputField({
return styled(styles)(
- {children} {required && '*'}
+ {children} {required && '*'}
, 'onChange' | 'name' | 'value'> & ILayoutSizeProps & IGridItemsLayoutProps & IFlexItemsLayoutProps & {
+type BaseProps = Omit, 'onChange' | 'name' | 'value'> & ILayoutSizeProps & {
description?: string;
mod?: 'surface';
ref?: React.Ref;
@@ -37,8 +37,10 @@ type BaseProps = Omit, 'onChange' |
type ControlledProps = BaseProps & {
name?: string;
- value?: string;
- onChange?: (value: string, name?: string) => any;
+ value?: string | number;
+ mapState?: (value: string | number) => string | number;
+ mapValue?: (value: string | number) => string | number;
+ onChange?: (value: string | number, name?: string) => any;
state?: never;
autoHide?: never;
};
@@ -46,7 +48,9 @@ type ControlledProps = BaseProps & {
type ObjectProps = BaseProps & {
name: TKey;
state: TState;
- onChange?: (value: string, name: TKey) => any;
+ mapState?: (value: TState[TKey]) => TState[TKey];
+ mapValue?: (value: TState[TKey]) => TState[TKey];
+ onChange?: (value: TState[TKey], name: TKey) => any;
autoHide?: boolean;
value?: never;
};
@@ -62,6 +66,8 @@ export const InputFieldNew: InputFieldType = observer(function InputFieldNew({
value: valueControlled,
required,
state,
+ mapState,
+ mapValue,
children,
className,
description,
@@ -77,26 +83,32 @@ export const InputFieldNew: InputFieldType = observer(function InputFieldNew({
const context = useContext(FormContext);
const handleChange = useCallback((event: React.ChangeEvent) => {
+ const value = mapValue?.(event.target.value) ?? event.target.value;
+
if (state) {
- state[name] = event.target.value;
+ state[name] = value;
}
if (onChange) {
- onChange(event.target.value, name);
+ onChange(value, name);
}
if (context) {
- context.onChange(event.target.value, name);
+ context.change(value, name);
}
}, [state, name, context, onChange]);
- const value = state ? state[name] : valueControlled;
-
if (autoHide && !isControlPresented(name, state)) {
return null;
}
+ let value = state ? state[name] : valueControlled;
+
+ if (mapState) {
+ value = mapState(value);
+ }
+
return styled(styles)(
- {children}{required && ' *'}
+ {children}{required && ' *'}
, HTMLFormElement>, 'onChange'> & {
disabled?: boolean;
@@ -25,14 +28,23 @@ export const SubmittingForm = forwardRef(fun
},
ref
) {
+ const parentContext = useContext(FormContext);
+ const props = useObjectRef({ parentContext, onChange, onSubmit });
+ const [changeExecutor] = useState(() => new Executor());
+
const handleSubmit = useCallback((e: React.FormEvent) => {
e.preventDefault();
- if (onSubmit) {
- onSubmit(e);
- }
- }, [onSubmit]);
+ props.onSubmit?.(e);
+ }, []);
- const context = useMemo(() => ({ onChange }), [onChange]);
+ const context = useObjectRef({
+ changeExecutor,
+ change(value, name) {
+ props.onChange(value, name);
+ props.parentContext?.change(value, name);
+ changeExecutor.execute({ value, name });
+ },
+ }, {});
return (