feat: CB-1890 CB-1848 indeterminate state for checkbox

This commit is contained in:
Wroud
2022-04-05 13:18:02 +03:00
parent 9226afc230
commit 152c1fb8e3
8 changed files with 48 additions and 2 deletions
@@ -34,6 +34,7 @@ export const NavigationNode: NavigationNodeComponent = observer(function Navigat
control,
disabled,
selected,
indeterminateSelected,
loading,
showInFilter,
expanded,
@@ -70,6 +71,7 @@ export const NavigationNode: NavigationNodeComponent = observer(function Navigat
loading={loading}
disabled={disabled}
selected={selected}
indeterminateSelected={indeterminateSelected}
expanded={expanded}
showInFilter={showInFilter}
externalExpanded={expandedExternal}
@@ -26,6 +26,7 @@ interface INavigationNode {
group: boolean;
showInFilter: boolean;
selected: boolean;
indeterminateSelected: boolean;
loading: boolean;
expanded: boolean;
leaf: boolean;
@@ -54,6 +55,9 @@ export function useNavigationNode(node: NavNode, path: string[]): INavigationNod
const control = getComputed(() => contextRef.context?.control);
const disabled = getComputed(() => contextRef.context?.tree.disabled || false);
const selected = getComputed(() => contextRef.context?.tree.isNodeSelected(node.id) || false);
const indeterminateSelected = getComputed(
() => contextRef.context?.tree.isNodeIndeterminateSelected(node.id) || false
);
const handleClick = async (leaf: boolean) => await contextRef.context?.onClick?.(node, path, leaf);
const handleOpen = async (leaf: boolean) => await contextRef.context?.onOpen?.(node, path, leaf);
@@ -94,6 +98,7 @@ export function useNavigationNode(node: NavNode, path: string[]): INavigationNod
control,
disabled,
selected,
indeterminateSelected,
loading,
expanded,
leaf,
@@ -89,6 +89,7 @@ export interface IElementsTree {
getNodeState: (nodeId: string) => ITreeNodeState;
isNodeExpanded: (nodeId: string, ignoreFilter?: boolean) => boolean;
isNodeSelected: (nodeId: string) => boolean;
isNodeIndeterminateSelected: (nodeId: string) => boolean;
getNodeChildren: (nodeId: string) => string[];
isGroup?: (node: NavNode) => boolean;
setFilter: (value: string) => Promise<void>;
@@ -196,7 +197,6 @@ export function useElementsTree(options: IOptions): IElementsTree {
}
}
},
async setSelection(nodeId: string, selected: boolean): Promise<void> {
const node = navNodeInfoResource.get(nodeId);
@@ -303,6 +303,25 @@ export function useElementsTree(options: IOptions): IElementsTree {
return this.getNodeState(nodeId).selected;
},
isNodeIndeterminateSelected(nodeId: string): boolean {
if (this.isNodeSelected(nodeId)) {
return false;
}
const node = navNodeInfoResource.get(nodeId);
if (node && elementsTree.isGroup?.(node)) {
const children = options.getChildren(nodeId) || [];
if (children.length > 0) {
return children.some(child => this.isNodeSelected(child) || this.isNodeIndeterminateSelected(child));
}
return false;
}
return false;
},
getNodeChildren(nodeId: string): string[] {
const node = navNodeInfoResource.get(nodeId);
@@ -330,6 +349,7 @@ export function useElementsTree(options: IOptions): IElementsTree {
collapse() {
for (const state of this.state.values()) {
state.expanded = false;
state.showInFilter = false;
}
},
async show(nodeId: string, path: string[]): Promise<void> {
@@ -6,6 +6,7 @@
* you may not use this file except in compliance with the License.
*/
import { useLayoutEffect, useRef } from 'react';
import styled, { css } from 'reshadow';
import { ComponentStyle, useStyles } from '@cloudbeaver/core-theming';
@@ -96,6 +97,14 @@ interface ICheckboxMarkupProps extends Omit<React.InputHTMLAttributes<HTMLInputE
export const CheckboxMarkup: React.FC<ICheckboxMarkupProps> = function CheckboxMarkup({
id, label, className, title, mod = ['primary'], ripple = true, style, ...rest
}) {
const checkboxRef = useRef<HTMLInputElement>(null);
useLayoutEffect(() => {
if (checkboxRef.current) {
checkboxRef.current.indeterminate = rest.indeterminate || false;
}
});
return styled(
useStyles(
checkboxStyles,
@@ -107,7 +116,7 @@ export const CheckboxMarkup: React.FC<ICheckboxMarkupProps> = function CheckboxM
)(
<checkbox-container className={className} title={title}>
<checkbox>
<checkbox-input as='input' type='checkbox' {...rest} id={id || rest.name} />
<checkbox-input ref={checkboxRef} as='input' type='checkbox' {...rest} id={id || rest.name} />
<checkbox-background>
<checkbox-checkmark as='svg' viewBox='0 0 24 24'>
<checkbox-checkmark-path as='path' fill='none' d='M1.73,12.91 8.1,19.28 22.79,4.59' />
@@ -11,6 +11,7 @@ export interface ITreeNodeState {
disabled?: boolean;
loading?: boolean;
selected?: boolean;
indeterminateSelected?: boolean;
externalExpanded?: boolean;
expanded?: boolean;
showInFilter?: boolean;
@@ -34,6 +34,7 @@ export const TreeNode = observer<Props, HTMLDivElement | null>(function TreeNode
group = false,
loading = false,
selected = false,
indeterminateSelected = false,
disabled = false,
expanded = false,
showInFilter = false,
@@ -87,14 +88,17 @@ export const TreeNode = observer<Props, HTMLDivElement | null>(function TreeNode
inProgress: observable.ref,
loading: observable.ref,
selected: observable.ref,
indeterminateSelected: observable.ref,
expanded: observable.ref,
externalExpanded: observable.ref,
showInFilter: observable.ref,
leaf: observable.ref,
}, {
group,
disabled,
loading,
selected,
indeterminateSelected,
expanded,
showInFilter,
externalExpanded: externalExpanded || false,
@@ -16,6 +16,7 @@ export interface ITreeNodeContext {
showInFilter: boolean;
loading: boolean;
selected: boolean;
indeterminateSelected: boolean;
leaf: boolean;
group: boolean;
readonly select: (multiple?: boolean, nested?: boolean) => Promise<void>;
@@ -28,6 +28,7 @@ interface Props {
group?: boolean;
onSelect?: () => Promise<boolean | void> | boolean | void;
selected?: boolean;
indeterminate?: boolean;
disabled?: boolean;
loadIndicator?: boolean;
className?: string;
@@ -37,6 +38,7 @@ export const TreeNodeSelect = observer<Props>(function TreeNodeSelect({
onSelect,
group,
selected,
indeterminate,
disabled,
loadIndicator,
className,
@@ -51,6 +53,7 @@ export const TreeNodeSelect = observer<Props>(function TreeNodeSelect({
group = group ?? context.group;
const loading = loadIndicator && context.loading;
selected = selected ?? context.selected;
indeterminate = indeterminate ?? context.indeterminateSelected;
async function handleSelect() {
await onSelect?.();
@@ -72,6 +75,7 @@ export const TreeNodeSelect = observer<Props>(function TreeNodeSelect({
: (
<Checkbox
checked={selected}
indeterminate={indeterminate}
disabled={disabled}
onChange={handleSelect}
/>