diff --git a/webapp/packages/core-app/src/NavigationTree/ElementsTree.tsx b/webapp/packages/core-app/src/NavigationTree/ElementsTree.tsx index 5d0230a3bc..0763829520 100644 --- a/webapp/packages/core-app/src/NavigationTree/ElementsTree.tsx +++ b/webapp/packages/core-app/src/NavigationTree/ElementsTree.tsx @@ -32,14 +32,14 @@ const styles = css` } `; -export interface INodeState { +export interface ITreeNodeState { filter: string; } interface Props { root?: string; selectionTree?: boolean; - localState?: MetadataMap; + localState?: MetadataMap; control?: React.FC<{ node: NavNode; }>; @@ -65,24 +65,26 @@ export const ElementsTree: React.FC = observer(function ElementsTree({ }) { const nodeChildren = useChildren(root); const Placeholder = emptyPlaceholder; - const [state] = useState(() => new MetadataMap(() => ({ filter: '' }))); + const [localTreeNodesState] = useState(() => new MetadataMap(() => ({ filter: '' }))); - const elementsTreeState = localState || state; + const treeNodesState = localState || localTreeNodesState; - const getMetadata = useCallback((node: NavNode) => elementsTreeState.get(node.id), [elementsTreeState]); + const getTreeNodeState = useCallback((node: NavNode) => + treeNodesState.get(node.id), [treeNodesState]); const onFilterHandler = useCallback((node: NavNode, value: string) => { - elementsTreeState.get(node.id).filter = value; + const treeNodeState = treeNodesState.get(node.id); + treeNodeState.filter = value; if (onFilter) { onFilter(node, value); } - }, [elementsTreeState, onFilter]); + }, [treeNodesState, onFilter]); const context = useMemo( () => ({ - state: elementsTreeState, - getMetadata, + treeNodesState, + getTreeNodeState, selectionTree, control, onOpen, @@ -90,7 +92,7 @@ export const ElementsTree: React.FC = observer(function ElementsTree({ isSelected, onFilter: onFilterHandler, }), - [control, selectionTree, onOpen, onSelect, isSelected, onFilterHandler, elementsTreeState, getMetadata] + [control, selectionTree, onOpen, onSelect, isSelected, onFilterHandler, treeNodesState, getTreeNodeState] ); if (!nodeChildren.children || nodeChildren.children.length === 0) { diff --git a/webapp/packages/core-app/src/NavigationTree/NavigationTreeNode/useNavigationNode.ts b/webapp/packages/core-app/src/NavigationTree/NavigationTreeNode/useNavigationNode.ts index 81834af5f1..5dc903fd17 100644 --- a/webapp/packages/core-app/src/NavigationTree/NavigationTreeNode/useNavigationNode.ts +++ b/webapp/packages/core-app/src/NavigationTree/NavigationTreeNode/useNavigationNode.ts @@ -111,7 +111,7 @@ export function useNavigationNode(node: NavNode): INavigationNode { // TODO: seems like selection & expand should be specific for separate tree definitions navigationTreeService.expandNode(node.id, false); - context?.state?.delete(node.id); + context?.treeNodesState?.delete(node.id); }, [node.id]); return { @@ -124,7 +124,7 @@ export function useNavigationNode(node: NavNode): INavigationNode { handleOpen, handleSelect, handleFilter, - filterValue: context?.state?.get(node.id).filter || '', + filterValue: context?.treeNodesState?.get(node.id).filter || '', }; } diff --git a/webapp/packages/core-app/src/NavigationTree/TreeContext.ts b/webapp/packages/core-app/src/NavigationTree/TreeContext.ts index 99410286db..feda4b1ee3 100644 --- a/webapp/packages/core-app/src/NavigationTree/TreeContext.ts +++ b/webapp/packages/core-app/src/NavigationTree/TreeContext.ts @@ -11,11 +11,11 @@ import { createContext } from 'react'; import type { MetadataMap } from '@cloudbeaver/core-utils'; import type { NavNode } from '../shared/NodesManager/EntityTypes'; -import type { INodeState } from './ElementsTree'; +import type { ITreeNodeState } from './ElementsTree'; export interface ITreeContext { - state?: MetadataMap; - getMetadata?: (node: NavNode) => INodeState; + treeNodesState?: MetadataMap; + getTreeNodeState?: (node: NavNode) => ITreeNodeState; selectionTree: boolean; control?: React.FC<{ node: NavNode; diff --git a/webapp/packages/core-blocks/src/FormControls/InputFieldNew.tsx b/webapp/packages/core-blocks/src/FormControls/InputFieldNew.tsx index 3001c550fa..46ad1781dc 100644 --- a/webapp/packages/core-blocks/src/FormControls/InputFieldNew.tsx +++ b/webapp/packages/core-blocks/src/FormControls/InputFieldNew.tsx @@ -10,7 +10,7 @@ import { observer } from 'mobx-react-lite'; import { useCallback, useContext } from 'react'; import styled, { use, css } from 'reshadow'; -import { useStyles } from '@cloudbeaver/core-theming'; +import { ComponentStyle, useStyles } from '@cloudbeaver/core-theming'; import type { IFlexItemsLayoutProps, IGridItemsLayoutProps, ILayoutSizeProps } from '../Containers/LayoutProps'; import { baseFormControlStylesNew } from './baseFormControlStylesNew'; @@ -20,16 +20,19 @@ import { isControlPresented } from './isControlPresented'; const INPUT_FIELD_STYLES = css` field-label { display: block; - padding-bottom: 10px; composes: theme-typography--body1 from global; font-weight: 500; } + field-label:not(:empty) { + padding-bottom: 10px; + } `; type BaseProps = Omit, 'onChange' | 'name' | 'value'> & ILayoutSizeProps & IGridItemsLayoutProps & IFlexItemsLayoutProps & { description?: string; mod?: 'surface'; ref?: React.Ref; + style?: ComponentStyle; }; type ControlledProps = BaseProps & { @@ -55,6 +58,7 @@ interface InputFieldType { export const InputFieldNew: InputFieldType = observer(function InputFieldNew({ name, + style, value: valueControlled, required, state, @@ -69,7 +73,7 @@ export const InputFieldNew: InputFieldType = observer(function InputFieldNew({ onChange, ...rest }: ControlledProps | ObjectProps, ref: React.Ref) { - const styles = useStyles(baseFormControlStylesNew, INPUT_FIELD_STYLES); + const styles = useStyles(baseFormControlStylesNew, INPUT_FIELD_STYLES, style); const context = useContext(FormContext); const handleChange = useCallback((event: React.ChangeEvent) => { @@ -92,7 +96,7 @@ export const InputFieldNew: InputFieldType = observer(function InputFieldNew({ return styled(styles)( - {children} {required && '*'} + {children}{required && ' *'} :global(input) { - height: 28px; - padding-right: 24px !important; - } - & > :global(label) { - padding-bottom: 0; - } } } - IconButton { position: absolute; - left: 0; + right: 2px; margin: 0; - background: #cccccc; width: 24px; height: 24px; cursor: pointer; - &[|filterMode] { - /* 24px icon width + 2px input border */ - left: calc(100% - 26px); + border-radius: 2px; + &[|filterEnabled] { + border-radius: unset; } } - `; +`); -const filterButtonMode = { - filter: composes( - css` - IconButton { - composes: theme-background-primary theme-text-on-primary from global; - } - `), - passive: - css` - IconButton { - color: #398fca; - background-color: #cccccc; - border-radius: 2px; - } - `, -}; +const innerInputStyle = css` + input { + height: 28px; + padding-right: 24px !important; + } +`; interface Props { disabled?: boolean; @@ -74,9 +64,9 @@ export const TreeNodeFilter: React.FC = function TreeNodeFilter({ disabled, className, }) { - const [focusedRef] = useFocus({}); + const [inputRef] = useFocus({}); const context = useContext(TreeNodeContext); - const [filterMode, setFilterMode] = useState(!!context?.filterValue); + const [filterEnabled, setFilterEnabled] = useState(!!context?.filterValue); if (!context) { throw new Error('Context not provided'); @@ -92,21 +82,22 @@ export const TreeNodeFilter: React.FC = function TreeNodeFilter({ }; useEffect(() => { - if (filterMode) { - focusedRef.current?.focus(); + if (filterEnabled) { + inputRef.current?.focus(); } - }, [filterMode]); + }, [filterEnabled]); - return styled(useStyles(styles, filterMode ? filterButtonMode.filter : filterButtonMode.passive))( + return styled(useStyles(styles))( context.filter(value.trim())} /> - setFilterMode(!filterMode)} {...use({ filterMode })} /> + setFilterEnabled(!filterEnabled)} {...use({ filterEnabled })} /> ); }; diff --git a/webapp/packages/core-theming/src/styles/_theme-dark.scss b/webapp/packages/core-theming/src/styles/_theme-dark.scss index 42e8018b9e..e187e7fe1d 100644 --- a/webapp/packages/core-theming/src/styles/_theme-dark.scss +++ b/webapp/packages/core-theming/src/styles/_theme-dark.scss @@ -26,6 +26,8 @@ $input-background: $mdc-theme-secondary; $input-background-disabled: $input-background; $input-background-readonly: $input-background; +$button-background-disabled: #cccccc; + $mdc-theme-text-colors: ( dark: ( primary: white, @@ -44,19 +46,19 @@ $mdc-theme-text-colors: ( ); $mdc-ripple-dark-ink-opacities: ( - hover: .04, - focus: .12, - press: .12, - selected: .08, - activated: .12 + hover: 0.04, + focus: 0.12, + press: 0.12, + selected: 0.08, + activated: 0.12, ) !default; $mdc-ripple-light-ink-opacities: ( - hover: .04, - focus: .12, - press: .12, - selected: .08, - activated: .12 + hover: 0.04, + focus: 0.12, + press: 0.12, + selected: 0.08, + activated: 0.12, ) !default; $theme-name: "dark"; diff --git a/webapp/packages/core-theming/src/styles/_theme-light.scss b/webapp/packages/core-theming/src/styles/_theme-light.scss index 0bd1e8ae03..8ab24c7d10 100644 --- a/webapp/packages/core-theming/src/styles/_theme-light.scss +++ b/webapp/packages/core-theming/src/styles/_theme-light.scss @@ -25,6 +25,8 @@ $input-background: $mdc-theme-surface; $input-background-disabled: $mdc-theme-secondary; $input-background-readonly: $mdc-theme-secondary; +$button-background-disabled: #cccccc; + $theme-name: "light"; @import "mixins"; diff --git a/webapp/packages/core-theming/src/styles/_variables.scss b/webapp/packages/core-theming/src/styles/_variables.scss index 2677b508f9..200be6563b 100644 --- a/webapp/packages/core-theming/src/styles/_variables.scss +++ b/webapp/packages/core-theming/src/styles/_variables.scss @@ -16,6 +16,7 @@ $mdc-theme-extra-property-values: ( input-background: $input-background, input-background-disabled: $input-background-disabled, input-background-readonly: $input-background-readonly, + button-background-disabled: $button-background-disabled, ); $mdc-theme-property-values: map-merge($mdc-theme-property-values, $mdc-theme-extra-property-values);