diff --git a/webapp/packages/core-blocks/src/FormControls/Checkboxes/Checkbox.tsx b/webapp/packages/core-blocks/src/FormControls/Checkboxes/Checkbox.tsx index f7b575057a..2bbc0d2509 100644 --- a/webapp/packages/core-blocks/src/FormControls/Checkboxes/Checkbox.tsx +++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/Checkbox.tsx @@ -16,17 +16,20 @@ import { CheckboxMarkup, CheckboxMod } from './CheckboxMarkup'; import { CheckboxOnChangeEvent, useCheckboxState } from './useCheckboxState'; export interface CheckboxBaseProps { - label?: string; mod?: CheckboxMod[]; ripple?: boolean; indeterminate?: boolean; style?: ComponentStyle; } -export type CheckboxInputProps = Omit, 'onChange' | 'type' | 'value' | 'checked' | 'id' | 'style'> & ILayoutSizeProps; +export type CheckboxInputProps = Omit, 'onChange' | 'type' | 'value' | 'defaultValue' | 'checked' | 'defaultChecked' | 'style'> & ILayoutSizeProps & { + value?: string; + defaultValue?: string; + defaultChecked?: boolean; + label?: string; +}; export interface ICheckboxControlledProps extends CheckboxInputProps { - value?: string; state?: never; checked?: boolean; onChange?: CheckboxOnChangeEvent; @@ -34,7 +37,6 @@ export interface ICheckboxControlledProps extends CheckboxInputProps { } export interface ICheckboxObjectProps extends CheckboxInputProps { - value?: string; state: Partial>; checked?: never; onChange?: CheckboxOnChangeEvent; @@ -50,9 +52,11 @@ export interface CheckboxType { export const Checkbox: CheckboxType = observer(function Checkbox({ name, value, + defaultValue, state, label, checked, + defaultChecked, children, mod, ripple, @@ -61,7 +65,15 @@ export const Checkbox: CheckboxType = observer(function Checkbox({ onChange, ...rest }: CheckboxBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps)) { - const checkboxState = useCheckboxState({ value, checked, state, name, onChange }); + const checkboxState = useCheckboxState({ + value, + defaultValue, + checked, + defaultChecked, + state, + name, + onChange, + }); if (autoHide && !isControlPresented(name, state)) { return null; @@ -71,7 +83,6 @@ export const Checkbox: CheckboxType = observer(function Checkbox({ = function CheckboxMarkup({ - label, className, title, mod = ['primary'], ripple = true, style, ...rest + id, label, className, title, mod = ['primary'], ripple = true, style, ...rest }) { return styled( useStyles( @@ -123,7 +123,7 @@ export const CheckboxMarkup: React.FC = function CheckboxM )( - + @@ -134,7 +134,7 @@ export const CheckboxMarkup: React.FC = function CheckboxM )} - {label && rest.id && {label}} + {label && (id || rest.name) && {label}} ); }; diff --git a/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckboxNew.tsx b/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckboxNew.tsx index 82c135094e..0687244421 100644 --- a/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckboxNew.tsx +++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/FieldCheckboxNew.tsx @@ -46,7 +46,7 @@ export const FieldCheckboxNew: CheckboxType = function FieldCheckboxNew({ return styled(styles)( - {children} + {children} ); }; diff --git a/webapp/packages/core-blocks/src/FormControls/Checkboxes/Switch.tsx b/webapp/packages/core-blocks/src/FormControls/Checkboxes/Switch.tsx index 75e8698515..67e3dee19a 100644 --- a/webapp/packages/core-blocks/src/FormControls/Checkboxes/Switch.tsx +++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/Switch.tsx @@ -79,7 +79,6 @@ const switchState = { interface IBaseProps { mod?: Array; - label?: string; description?: string; } @@ -91,10 +90,12 @@ interface SwitchType { export const Switch: SwitchType = observer(function Switch({ name, value, + defaultValue, label, description, state, checked, + defaultChecked, className, children, onChange, @@ -103,7 +104,15 @@ export const Switch: SwitchType = observer(function Switch({ disabled, ...rest }: IBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps)) { - const checkboxState = useCheckboxState({ value, checked, state, name, onChange }); + const checkboxState = useCheckboxState({ + value, + defaultValue, + checked, + defaultChecked, + state, + name, + onChange, + }); const styles = useStyles( baseFormControlStyles, switchStyles, diff --git a/webapp/packages/core-blocks/src/FormControls/Checkboxes/SwitchNew.tsx b/webapp/packages/core-blocks/src/FormControls/Checkboxes/SwitchNew.tsx index 02a797cb9e..e3d959ea0d 100644 --- a/webapp/packages/core-blocks/src/FormControls/Checkboxes/SwitchNew.tsx +++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/SwitchNew.tsx @@ -95,9 +95,11 @@ interface SwitchType { export const SwitchNew: SwitchType = observer(function SwitchNew({ name, value, + defaultValue, description, state, checked, + defaultChecked, className, children, onChange, @@ -106,7 +108,15 @@ export const SwitchNew: SwitchType = observer(function SwitchNew({ disabled, ...rest }: IBaseProps & (ICheckboxControlledProps | ICheckboxObjectProps)) { - const checkboxState = useCheckboxState({ value, checked, state, name, onChange }); + const checkboxState = useCheckboxState({ + value, + defaultValue, + checked, + defaultChecked, + state, + name, + onChange, + }); const styles = useStyles( baseFormControlStylesNew, switchStyles, diff --git a/webapp/packages/core-blocks/src/FormControls/Checkboxes/useCheckboxState.ts b/webapp/packages/core-blocks/src/FormControls/Checkboxes/useCheckboxState.ts index a2b3396b12..cce3385db3 100644 --- a/webapp/packages/core-blocks/src/FormControls/Checkboxes/useCheckboxState.ts +++ b/webapp/packages/core-blocks/src/FormControls/Checkboxes/useCheckboxState.ts @@ -11,11 +11,13 @@ import { useContext, useState } from 'react'; import { useObjectRef } from '../../useObjectRef'; import { FormContext } from '../FormContext'; -export type CheckboxOnChangeEvent =(value: boolean, name: T) => void; +export type CheckboxOnChangeEvent = (value: boolean, name: T) => void; export type CheckboxStateOptions = { value: string | undefined; + defaultValue: string | undefined; checked: boolean | undefined; + defaultChecked: boolean | undefined; } & ( { state: undefined; @@ -30,23 +32,24 @@ export type CheckboxStateOptions = { ); interface ICheckboxState { - checked: boolean; + checked: boolean | undefined; 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 controlledValue = options.value ?? options.defaultValue ?? undefined; + const optionsRef = useObjectRef({ ...options, context, count, value: controlledValue }); const { state, name, value } = optionsRef; - let checked = optionsRef.checked ?? false; + let checked = optionsRef.checked ?? optionsRef.defaultChecked ?? undefined; - if (state !== undefined && name !== undefined) { + if (state !== undefined && name !== undefined && name in state) { const currentState = state[name as TKey]; - if (typeof value === 'string' && Array.isArray(currentState)) { - checked = currentState.includes(value); + if (typeof value === 'string') { + checked = Array.isArray(currentState) ? currentState.includes(value) : currentState === value; } else if (typeof currentState === 'string') { checked = currentState.toLowerCase() === 'true'; } else { @@ -54,7 +57,7 @@ export function useCheckboxState(options: CheckboxStateOpti } } - return useObjectRef({ + return useObjectRef({ checked, change(event: React.ChangeEvent) { const { state, name, value, onChange, count, context } = optionsRef; @@ -63,13 +66,16 @@ export function useCheckboxState(options: CheckboxStateOpti 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); + if (typeof value === 'string') { + if (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] = value; } } else { state[name as TKey] = checked; diff --git a/webapp/packages/core-blocks/src/FormControls/ComboboxNew.tsx b/webapp/packages/core-blocks/src/FormControls/ComboboxNew.tsx index a76d3da5cd..bd735691c6 100644 --- a/webapp/packages/core-blocks/src/FormControls/ComboboxNew.tsx +++ b/webapp/packages/core-blocks/src/FormControls/ComboboxNew.tsx @@ -97,10 +97,11 @@ const styles = composes( ` ); -type BaseProps = Omit, 'onChange' | 'onSelect' | 'name' | 'value'> & ILayoutSizeProps & { +type BaseProps = Omit, 'onChange' | 'onSelect' | 'name' | 'value' | 'defaultValue'> & ILayoutSizeProps & { propertyName?: string; items: TValue[]; searchable?: boolean; + defaultValue?: string; keySelector: (item: TValue) => TKey; valueSelector: (item: TValue) => string; onSwitch?: (state: boolean) => void; @@ -129,6 +130,7 @@ interface ComboboxType { export const ComboboxNew: ComboboxType = observer(function ComboboxNew({ value: controlledValue, + defaultValue, name, state, propertyName, @@ -154,14 +156,10 @@ export const ComboboxNew: ComboboxType = observer(function ComboboxNew({ gutter: 4, }); const [searchValue, setSearchValue] = useState(''); - let value: string | number | readonly string[] | undefined = controlledValue; + let value: string | number | readonly string[] | undefined = controlledValue ?? defaultValue ?? undefined; - if (state) { - if (name in state) { - value = state[name]; - } else if (rest.defaultValue !== undefined) { - value = rest.defaultValue; - } + if (state && name !== undefined && name in state) { + value = state[name]; } const selectedItem = items.find(item => keySelector(item) === value); diff --git a/webapp/packages/core-blocks/src/FormControls/InputField.tsx b/webapp/packages/core-blocks/src/FormControls/InputField.tsx index 5cda6bd2c6..ef82d0a25e 100644 --- a/webapp/packages/core-blocks/src/FormControls/InputField.tsx +++ b/webapp/packages/core-blocks/src/FormControls/InputField.tsx @@ -27,7 +27,6 @@ type ControlledProps = BaseProps & { name?: string; value?: string; onChange?: (value: string, name?: string) => any; - state?: never; autoHide?: never; }; @@ -37,7 +36,6 @@ type ObjectProps = BaseProps & { state: TState; onChange?: (value: string, name: TKey) => any; autoHide?: boolean; - value?: never; }; @@ -49,6 +47,7 @@ interface InputFieldType { export const InputField: InputFieldType = observer(function InputField({ name, value: valueControlled, + defaultValue, required, state, children, @@ -76,9 +75,13 @@ export const InputField: InputFieldType = observer(function InputField({ } }, [state, name, context, onChange]); - const value = state ? state[name] : valueControlled; + let value: any = valueControlled ?? defaultValue ?? undefined; - if (autoHide && !isControlPresented(name, state)) { + if (state && name !== undefined && name in state) { + value = state[name]; + } + + if (autoHide && !isControlPresented(name, state, defaultValue)) { return null; } diff --git a/webapp/packages/core-blocks/src/FormControls/InputFieldNew.tsx b/webapp/packages/core-blocks/src/FormControls/InputFieldNew.tsx index eab775a1ec..ea0207cd8a 100644 --- a/webapp/packages/core-blocks/src/FormControls/InputFieldNew.tsx +++ b/webapp/packages/core-blocks/src/FormControls/InputFieldNew.tsx @@ -64,6 +64,7 @@ export const InputFieldNew: InputFieldType = observer(function InputFieldNew({ name, style, value: valueControlled, + defaultValue, required, state, mapState, @@ -96,11 +97,15 @@ export const InputFieldNew: InputFieldType = observer(function InputFieldNew({ } }, [state, name, context, onChange]); - if (autoHide && !isControlPresented(name, state)) { + if (autoHide && !isControlPresented(name, state, defaultValue)) { return null; } - let value = state ? state[name] : valueControlled; + let value: any = valueControlled ?? defaultValue ?? undefined; + + if (state && name !== undefined && name in state) { + value = state[name]; + } if (mapState) { value = mapState(value); diff --git a/webapp/packages/core-blocks/src/FormControls/isControlPresented.ts b/webapp/packages/core-blocks/src/FormControls/isControlPresented.ts index 63a89795c1..16ba499ac9 100644 --- a/webapp/packages/core-blocks/src/FormControls/isControlPresented.ts +++ b/webapp/packages/core-blocks/src/FormControls/isControlPresented.ts @@ -6,9 +6,14 @@ * you may not use this file except in compliance with the License. */ -export function isControlPresented(name: string | undefined, state: any): boolean { +export function isControlPresented( + name: string | undefined, state: any, defaultValue?: string | number | readonly string[] +): boolean { if (state !== undefined && name !== undefined) { - return name in state && state[name] !== null; + if (name in state) { + return state[name] !== null; + } + return defaultValue !== undefined; } return true; } diff --git a/webapp/packages/core-blocks/src/ObjectPropertyInfoForm/ObjectPropertyInfoForm.tsx b/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/ObjectPropertyInfoForm.tsx similarity index 90% rename from webapp/packages/core-blocks/src/ObjectPropertyInfoForm/ObjectPropertyInfoForm.tsx rename to webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/ObjectPropertyInfoForm.tsx index 5d465f85c0..915857484e 100644 --- a/webapp/packages/core-blocks/src/ObjectPropertyInfoForm/ObjectPropertyInfoForm.tsx +++ b/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/ObjectPropertyInfoForm.tsx @@ -14,13 +14,13 @@ import { InputField } from '@cloudbeaver/core-blocks'; import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; import { useStyles } from '@cloudbeaver/core-theming'; -import { FieldCheckbox } from '../FormControls/Checkboxes/FieldCheckbox'; -import { Combobox } from '../FormControls/Combobox'; -import { FormFieldDescription } from '../FormControls/FormFieldDescription'; -import { FormGroup } from '../FormControls/FormGroup'; -import { isControlPresented } from '../FormControls/isControlPresented'; -import { Link } from '../Link'; -import { TextPlaceholder } from '../TextPlaceholder'; +import { FieldCheckbox } from '../../FormControls/Checkboxes/FieldCheckbox'; +import { Combobox } from '../../FormControls/Combobox'; +import { FormFieldDescription } from '../../FormControls/FormFieldDescription'; +import { FormGroup } from '../../FormControls/FormGroup'; +import { isControlPresented } from '../../FormControls/isControlPresented'; +import { Link } from '../../Link'; +import { TextPlaceholder } from '../../TextPlaceholder'; import { formStyles } from './formStyles'; const RESERVED_KEYWORDS = ['no', 'off', 'new-password']; @@ -80,6 +80,7 @@ const RenderField: React.FC = observer(function RenderField({ if (checkbox) { return ( ; + category?: string | null; + editable?: boolean; + autofillToken?: string; + className?: string; + disabled?: boolean; + readOnly?: boolean; + autoHide?: boolean; + showRememberTip?: boolean; + onFocus?: (name: string) => void; +} + +export const ObjectPropertyInfoFormNew: React.FC = observer(function ObjectPropertyInfoFormNew({ + properties, + state, + category, + editable = true, + className, + autofillToken = '', + disabled, + readOnly, + autoHide, + showRememberTip, + onFocus, +}) { + const handleFocus = useCallback((e: React.FocusEvent) => { + if (onFocus) { + onFocus(e.target.name); + } + }, [onFocus]); + + if (properties.length === 0) { + return Properties empty; + } + + return ( + <> + {properties.map(property => { + if (category !== undefined && property.category !== category) { + return null; + } + return ( + + ); + })} + + ); +}); diff --git a/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/RenderField.tsx b/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/RenderField.tsx new file mode 100644 index 0000000000..d2ff2688b7 --- /dev/null +++ b/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/RenderField.tsx @@ -0,0 +1,212 @@ +/* + * 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 { observer } from 'mobx-react-lite'; + +import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; + +import { FieldCheckboxNew } from '../../FormControls/Checkboxes/FieldCheckboxNew'; +import { ComboboxNew } from '../../FormControls/ComboboxNew'; +import { FormFieldDescriptionNew } from '../../FormControls/FormFieldDescriptionNew'; +import { InputFieldNew } from '../../FormControls/InputFieldNew'; +import { isControlPresented } from '../../FormControls/isControlPresented'; +import { Link } from '../../Link'; + +const RESERVED_KEYWORDS = ['no', 'off', 'new-password']; + +interface RenderFieldProps { + property: ObjectPropertyInfo; + state?: Record; + editable?: boolean; + autofillToken?: string; + disabled?: boolean; + readOnly?: boolean; + autoHide?: boolean; + showRememberTip?: boolean; + onFocus?: (event: React.FocusEvent) => void; + className?: string; +} + +type ControlType = 'checkbox' | 'combobox' | 'link' | 'input'; + +function getControlTypeFor(property: ObjectPropertyInfo): ControlType { + const dataType = property.dataType?.toLowerCase(); + + if (dataType === 'boolean') { + return 'checkbox'; + } else if (property.validValues && property.validValues.length > 0) { + return 'combobox'; + } else if (property.features.includes('href')) { + return 'link'; + } + + return 'input'; +} + +function getValue(value: any, controlType: ControlType) { + const checkbox = controlType === 'checkbox'; + + if (value === null || value === undefined) { + return checkbox ? false : ''; + } + + if (typeof value === 'string') { + return checkbox ? value.toLowerCase() === 'true' : value; + } + + return value.displayName || value.value || JSON.stringify(value); +} + +export const RenderField: React.FC = observer(function RenderField({ + property, + state, + editable = true, + autofillToken = '', + disabled, + readOnly, + autoHide, + showRememberTip, + onFocus, + className, +}) { + const controltype = getControlTypeFor(property); + const password = property.features.includes('password'); + + const value = getValue(property.value, controltype); + const defaultValue = getValue(property.defaultValue, controltype); + let description: string | undefined; + + if (controltype === 'link') { + return ( + + {property.description} + + ); + } + + if (!editable) { + if (autoHide && !isControlPresented(property.id!, state)) { + return null; + } + return ( + + {state?.[property.id!]} + + ); + } + + if (showRememberTip && password && property.value) { + description = 'Password saved'; + } + + if (controltype === 'checkbox') { + if (state !== undefined) { + return ( + + {property.displayName ?? ''} + + ); + } + return ( + + {property.displayName ?? ''} + + ); + } + + if (controltype === 'combobox') { + if (state !== undefined) { + return ( + value} + valueSelector={value => value} + defaultValue={defaultValue} + title={property.description} + disabled={disabled} + className={className} + > + {property.displayName ?? ''} + + ); + } + + return ( + value} + valueSelector={value => value} + defaultValue={defaultValue} + title={property.description} + disabled={disabled} + className={className} + > + {property.displayName ?? ''} + + ); + } + + if (state !== undefined) { + return ( + + {property.displayName} + + ); + } + return ( + + {property.displayName} + + ); +}); diff --git a/webapp/packages/core-blocks/src/ObjectPropertyInfoForm/formStyles.ts b/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/formStyles.ts similarity index 100% rename from webapp/packages/core-blocks/src/ObjectPropertyInfoForm/formStyles.ts rename to webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/formStyles.ts diff --git a/webapp/packages/core-blocks/src/ObjectPropertyInfo/useObjectPropertyCategories.tsx b/webapp/packages/core-blocks/src/ObjectPropertyInfo/useObjectPropertyCategories.tsx new file mode 100644 index 0000000000..aa61a391a2 --- /dev/null +++ b/webapp/packages/core-blocks/src/ObjectPropertyInfo/useObjectPropertyCategories.tsx @@ -0,0 +1,42 @@ +/* + * 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 { computed, IComputedValue } from 'mobx'; +import { useMemo } from 'react'; + +import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; + +interface ICategoriesInfo { + categories: string[]; + isUncategorizedExists: boolean; +} + +export function useObjectPropertyCategories(properties: ObjectPropertyInfo[]) { + const categories: IComputedValue = useMemo(() => computed(() => { + const result: ICategoriesInfo = { + categories: [], + isUncategorizedExists: false, + }; + + for (const property of properties) { + const category = property.category; + if (!category) { + result.isUncategorizedExists = true; + continue; + } + + if (!result.categories.includes(category)) { + result.categories.push(category); + } + } + + return result; + }), [properties]); + + return categories.get(); +} diff --git a/webapp/packages/core-blocks/src/ObjectPropertyInfoForm/ObjectPropertyInfoFormNew.tsx b/webapp/packages/core-blocks/src/ObjectPropertyInfoForm/ObjectPropertyInfoFormNew.tsx deleted file mode 100644 index 0947969e50..0000000000 --- a/webapp/packages/core-blocks/src/ObjectPropertyInfoForm/ObjectPropertyInfoFormNew.tsx +++ /dev/null @@ -1,185 +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 { observer } from 'mobx-react-lite'; -import { useCallback } from 'react'; - -import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; - -import type { ILayoutSizeProps } from '../Containers/ILayoutSizeProps'; -import { FieldCheckboxNew } from '../FormControls/Checkboxes/FieldCheckboxNew'; -import { ComboboxNew } from '../FormControls/ComboboxNew'; -import { FormFieldDescriptionNew } from '../FormControls/FormFieldDescriptionNew'; -import { InputFieldNew } from '../FormControls/InputFieldNew'; -import { isControlPresented } from '../FormControls/isControlPresented'; -import { Link } from '../Link'; -import { TextPlaceholder } from '../TextPlaceholder'; - -const RESERVED_KEYWORDS = ['no', 'off', 'new-password']; - -interface RenderFieldProps { - property: ObjectPropertyInfo; - state: Record; - editable?: boolean; - autofillToken?: string; - disabled?: boolean; - readOnly?: boolean; - autoHide?: boolean; - showRememberTip?: boolean; - onFocus?: (event: React.FocusEvent) => void; - className?: string; -} - -const RenderField: React.FC = observer(function RenderField({ - property, - state, - editable = true, - autofillToken = '', - disabled, - readOnly, - autoHide, - showRememberTip, - onFocus, - className, -}) { - const href = property.features.includes('href'); - const password = property.features.includes('password'); - const checkbox = property.dataType === 'Boolean'; - const combobox = property.validValues && property.validValues.length > 0; - let description: string | undefined; - - if (href) { - return ( - - {property.description} - - ); - } - - if (!editable) { - if (autoHide && !isControlPresented(property.id!, state)) { - return null; - } - return ( - - {state[property.id!]} - - ); - } - - if (showRememberTip && password && property.value) { - description = 'Password saved'; - } - - if (checkbox) { - return ( - - {property.displayName ?? ''} - - ); - } - - if (combobox) { - return ( - value} - valueSelector={value => value} - defaultValue={property.defaultValue} - title={property.description} - disabled={disabled} - className={className} - > - {property.displayName ?? ''} - - ); - } - - return ( - - {property.displayName} - - ); -}); - -interface ObjectPropertyFormProps extends ILayoutSizeProps { - properties: ObjectPropertyInfo[] | undefined; - state: Record; - editable?: boolean; - autofillToken?: string; - className?: string; - disabled?: boolean; - readOnly?: boolean; - autoHide?: boolean; - showRememberTip?: boolean; - onFocus?: (name: string) => void; -} - -export const ObjectPropertyInfoFormNew: React.FC = observer(function ObjectPropertyInfoFormNew({ - properties, - state, - editable = true, - className, - autofillToken = '', - disabled, - readOnly, - autoHide, - showRememberTip, - onFocus, -}) { - const handleFocus = useCallback((e: React.FocusEvent) => { - if (onFocus) { - onFocus(e.target.name); - } - }, [onFocus]); - - if (!properties || properties.length === 0) { - return Properties empty; - } - - return ( - <> - {properties.map(property => ( - - ))} - - ); -}); diff --git a/webapp/packages/core-blocks/src/index.ts b/webapp/packages/core-blocks/src/index.ts index 5098008ea8..e2cbae0663 100644 --- a/webapp/packages/core-blocks/src/index.ts +++ b/webapp/packages/core-blocks/src/index.ts @@ -4,8 +4,9 @@ export * from './ItemList'; export * from './layout/AppLogo'; export * from './layout/TopAppBar'; export * from './Loader/Loader'; -export * from './ObjectPropertyInfoForm/ObjectPropertyInfoForm'; -export * from './ObjectPropertyInfoForm/ObjectPropertyInfoFormNew'; +export * from './ObjectPropertyInfo/ObjectPropertyInfoForm/ObjectPropertyInfoForm'; +export * from './ObjectPropertyInfo/ObjectPropertyInfoForm/ObjectPropertyInfoFormNew'; +export * from './ObjectPropertyInfo/useObjectPropertyCategories'; export * from './Placeholder/Placeholder'; export * from './Placeholder/PlaceholderContainer'; diff --git a/webapp/packages/core-connections/src/ConnectionForm/Options/Options.tsx b/webapp/packages/core-connections/src/ConnectionForm/Options/Options.tsx index 6cd6f0f1e8..2c64fd191f 100644 --- a/webapp/packages/core-connections/src/ConnectionForm/Options/Options.tsx +++ b/webapp/packages/core-connections/src/ConnectionForm/Options/Options.tsx @@ -205,8 +205,8 @@ export const Options: TabContainerPanelComponent = observe )} {admin && originLocal && ( = observe - {(authModel && !driver.data?.anonymousAccess) && ( + {(authModel && !driver.data?.anonymousAccess && properties) && ( {translate('connections_connection_edit_authentication')} @@ -243,8 +243,8 @@ export const Options: TabContainerPanelComponent = observe {credentialsSavingEnabled && ( {translate('connections_connection_edit_save_credentials')} diff --git a/webapp/packages/core-connections/src/ConnectionForm/SSH/SSH.tsx b/webapp/packages/core-connections/src/ConnectionForm/SSH/SSH.tsx index 96625d5c42..6cadb49651 100644 --- a/webapp/packages/core-connections/src/ConnectionForm/SSH/SSH.tsx +++ b/webapp/packages/core-connections/src/ConnectionForm/SSH/SSH.tsx @@ -161,8 +161,8 @@ export const SSH: TabContainerPanelComponent = observer(fu {credentialsSavingEnabled && ( {translate('connections_connection_edit_save_credentials')} diff --git a/webapp/packages/core-connections/src/DatabaseAuthDialog/DatabaseAuthDialog.tsx b/webapp/packages/core-connections/src/DatabaseAuthDialog/DatabaseAuthDialog.tsx index cd00a14054..e8d4aae7a1 100644 --- a/webapp/packages/core-connections/src/DatabaseAuthDialog/DatabaseAuthDialog.tsx +++ b/webapp/packages/core-connections/src/DatabaseAuthDialog/DatabaseAuthDialog.tsx @@ -122,8 +122,8 @@ export const DatabaseAuthDialog = observer(function DatabaseAuthDialog({ {credentialsSavingEnabled && ( = observer(fun {controller.roles.map((role, i) => ( ; +const emptyArray: ObjectPropertyInfo[] = []; + export const ObjectProperties = observer(function ObjectProperties({ objectId, }: ObjectPropertiesProps) { const translate = useTranslate(); const { dbObject, isLoading } = useDatabaseObjectInfo(objectId); const styles = useStyles(BASE_CONTAINERS_STYLES); + const { categories, isUncategorizedExists } = useObjectPropertyCategories(dbObject?.properties ?? emptyArray); + const properties = dbObject?.properties; - if (!dbObject?.properties && isLoading) { + if (!properties && isLoading) { return ; } - if (!dbObject?.properties || dbObject.properties.length === 0) { + if (!properties || properties.length === 0) { return {translate('plugin_object_viewer_table_no_items')}; } return styled(styles)( - - - {dbObject.properties.map(v => ( - - ))} - + + {isUncategorizedExists && ( + + + + )} + {categories.map(category => ( + + {category} + + + ))} ); });