* updated design as per figma * updated design, fix cypress test * item width fix as required * label functionality in select, tree-select wids * changes as required * label, placeholder and default focused item logic changes for select * updated border color for invalid selected value * fix for colors file
212 lines
5.3 KiB
TypeScript
212 lines
5.3 KiB
TypeScript
import React, {
|
|
ReactNode,
|
|
useCallback,
|
|
useEffect,
|
|
useRef,
|
|
useState,
|
|
} from "react";
|
|
import TreeSelect, { TreeSelectProps as SelectProps } from "rc-tree-select";
|
|
import {
|
|
TreeSelectContainer,
|
|
DropdownStyles,
|
|
StyledIcon,
|
|
StyledLabel,
|
|
TextLabelWrapper,
|
|
} from "./index.styled";
|
|
import "rc-tree-select/assets/index.less";
|
|
import { DefaultValueType } from "rc-tree-select/lib/interface";
|
|
import { TreeNodeProps } from "rc-tree-select/lib/TreeNode";
|
|
import { CheckedStrategy } from "rc-tree-select/lib/utils/strategyUtil";
|
|
import {
|
|
CANVAS_CLASSNAME,
|
|
MODAL_PORTAL_CLASSNAME,
|
|
TextSize,
|
|
} from "constants/WidgetConstants";
|
|
import { Classes } from "@blueprintjs/core";
|
|
import Icon from "components/ads/Icon";
|
|
import { Colors } from "constants/Colors";
|
|
|
|
export interface TreeSelectProps
|
|
extends Required<
|
|
Pick<
|
|
SelectProps,
|
|
| "disabled"
|
|
| "options"
|
|
| "placeholder"
|
|
| "loading"
|
|
| "dropdownStyle"
|
|
| "allowClear"
|
|
>
|
|
> {
|
|
value?: DefaultValueType;
|
|
onChange: (value?: DefaultValueType, labelList?: ReactNode[]) => void;
|
|
expandAll: boolean;
|
|
mode: CheckedStrategy;
|
|
labelText?: string;
|
|
labelTextColor?: string;
|
|
labelTextSize?: TextSize;
|
|
labelStyle?: string;
|
|
compactMode: boolean;
|
|
isValid: boolean;
|
|
}
|
|
|
|
const getSvg = (expanded: boolean) => (
|
|
<i
|
|
style={{
|
|
cursor: "pointer",
|
|
backgroundColor: "transparent",
|
|
display: "inline-flex",
|
|
width: "14px",
|
|
height: "100%",
|
|
}}
|
|
>
|
|
<StyledIcon
|
|
className="switcher-icon"
|
|
expanded={expanded}
|
|
fillColor={Colors.GREY_10}
|
|
name="dropdown"
|
|
/>
|
|
</i>
|
|
);
|
|
|
|
const switcherIcon = (treeNode: TreeNodeProps) => {
|
|
if (treeNode.isLeaf) {
|
|
return (
|
|
<i
|
|
style={{
|
|
cursor: "pointer",
|
|
backgroundColor: "white",
|
|
display: "inline-flex",
|
|
width: "14px",
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
return getSvg(treeNode.expanded);
|
|
};
|
|
|
|
function MultiTreeSelectComponent({
|
|
allowClear,
|
|
compactMode,
|
|
disabled,
|
|
dropdownStyle,
|
|
expandAll,
|
|
isValid,
|
|
labelStyle,
|
|
labelText,
|
|
labelTextColor,
|
|
labelTextSize,
|
|
loading,
|
|
mode,
|
|
onChange,
|
|
options,
|
|
placeholder,
|
|
value,
|
|
}: TreeSelectProps): JSX.Element {
|
|
const [key, setKey] = useState(Math.random());
|
|
const _menu = useRef<HTMLElement | null>(null);
|
|
|
|
// treeDefaultExpandAll is uncontrolled after first render,
|
|
// using this to force render to respond to changes in expandAll
|
|
useEffect(() => {
|
|
setKey(Math.random());
|
|
}, [expandAll]);
|
|
|
|
const getDropdownPosition = useCallback(() => {
|
|
const node = _menu.current;
|
|
if (Boolean(node?.closest(`.${MODAL_PORTAL_CLASSNAME}`))) {
|
|
return document.querySelector(
|
|
`.${MODAL_PORTAL_CLASSNAME}`,
|
|
) as HTMLElement;
|
|
}
|
|
return document.querySelector(`.${CANVAS_CLASSNAME}`) as HTMLElement;
|
|
}, []);
|
|
|
|
const onClear = useCallback(() => onChange([], []), []);
|
|
|
|
return (
|
|
<TreeSelectContainer
|
|
allowClear={allowClear}
|
|
compactMode={compactMode}
|
|
isValid={isValid}
|
|
ref={_menu as React.RefObject<HTMLDivElement>}
|
|
>
|
|
<DropdownStyles />
|
|
{labelText && (
|
|
<TextLabelWrapper compactMode={compactMode}>
|
|
<StyledLabel
|
|
$compactMode={compactMode}
|
|
$disabled={disabled}
|
|
$labelStyle={labelStyle}
|
|
$labelText={labelText}
|
|
$labelTextColor={labelTextColor}
|
|
$labelTextSize={labelTextSize}
|
|
className={`tree-select-label ${
|
|
loading ? Classes.SKELETON : Classes.TEXT_OVERFLOW_ELLIPSIS
|
|
}`}
|
|
disabled={disabled}
|
|
>
|
|
{labelText}
|
|
</StyledLabel>
|
|
</TextLabelWrapper>
|
|
)}
|
|
<TreeSelect
|
|
allowClear={allowClear}
|
|
animation="slide-up"
|
|
choiceTransitionName="rc-tree-select-selection__choice-zoom"
|
|
className="rc-tree-select"
|
|
clearIcon={
|
|
<Icon
|
|
className="clear-icon"
|
|
fillColor={Colors.GREY_10}
|
|
name="close-x"
|
|
/>
|
|
}
|
|
disabled={disabled}
|
|
dropdownClassName="tree-multiselect-dropdown"
|
|
dropdownStyle={dropdownStyle}
|
|
getPopupContainer={getDropdownPosition}
|
|
inputIcon={
|
|
<Icon
|
|
className="dropdown-icon"
|
|
fillColor={disabled ? Colors.GREY_7 : Colors.GREY_10}
|
|
name="dropdown"
|
|
/>
|
|
}
|
|
key={key}
|
|
loading={loading}
|
|
maxTagCount={"responsive"}
|
|
maxTagPlaceholder={(e) => `+${e.length} more`}
|
|
multiple
|
|
notFoundContent="No Results Found"
|
|
onChange={onChange}
|
|
onClear={onClear}
|
|
placeholder={placeholder}
|
|
removeIcon={
|
|
<Icon
|
|
className="remove-icon"
|
|
fillColor={Colors.GREY_10}
|
|
name="close-x"
|
|
/>
|
|
}
|
|
showArrow
|
|
showCheckedStrategy={mode}
|
|
showSearch
|
|
style={{ width: "100%" }}
|
|
switcherIcon={switcherIcon}
|
|
transitionName="rc-tree-select-dropdown-slide-up"
|
|
treeCheckable={
|
|
<span className={`rc-tree-select-tree-checkbox-inner`} />
|
|
}
|
|
treeData={options}
|
|
treeDefaultExpandAll={expandAll}
|
|
treeIcon
|
|
treeNodeFilterProp="label"
|
|
value={value}
|
|
/>
|
|
</TreeSelectContainer>
|
|
);
|
|
}
|
|
|
|
export default MultiTreeSelectComponent;
|