PromucFlow_constructor/app/client/src/components/ads/MultiselectDropdown.tsx
Satish Gandham 7f7f6f666b
Development: Add eslint rules for code consistency (#4083)
Co-authored-by: Satish Gandham <satish@appsmith.com>
Co-authored-by: Abhinav Jha <abhinav@appsmith.com>
2021-04-28 15:58:39 +05:30

294 lines
8.3 KiB
TypeScript

import React, { useState, useCallback } from "react";
import Icon, { IconName, IconSize } from "./Icon";
import { Classes, CommonComponentProps } from "./common";
import Text, { TextType } from "./Text";
import { Popover, Position } from "@blueprintjs/core";
import styled from "constants/DefaultTheme";
import _ from "lodash";
const DropdownContainer = styled.div<{ width?: string }>`
width: ${(props) => props.width || "260px"};
position: relative;
`;
const Selected = styled.div<{ isOpen: boolean; disabled?: boolean }>`
height: 38px;
padding: ${(props) => props.theme.spaces[2]}px
${(props) => props.theme.spaces[3]}px;
background: ${(props) =>
props.disabled
? props.theme.colors.dropdown.header.disabledBg
: props.theme.colors.dropdown.header.bg};
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
cursor: pointer;
${(props) =>
props.isOpen
? `border: 1px solid ${props.theme.colors.info.main}`
: props.disabled
? `border: 1px solid ${props.theme.colors.dropdown.header.disabledBg}`
: `border: 1px solid ${props.theme.colors.dropdown.header.bg}`};
${(props) =>
props.isOpen && !props.disabled ? "box-sizing: border-box" : null};
${(props) =>
props.isOpen && !props.disabled
? "box-shadow: 0px 0px 4px 4px rgba(203, 72, 16, 0.18)"
: null};
.${Classes.TEXT} {
${(props) =>
props.disabled
? `color: ${props.theme.colors.dropdown.header.disabledText}`
: `color: ${props.theme.colors.dropdown.header.text}`};
}
`;
const DropdownWrapper = styled.div<{
width?: string;
}>`
width: ${(props) => props.width || "260px"};
z-index: 1;
background-color: ${(props) => props.theme.colors.propertyPane.radioGroupBg};
box-shadow: ${(props) =>
`0px 2px 4px ${props.theme.colors.dropdown.menuShadow}`};
margin-top: ${(props) => -props.theme.spaces[3]}px;
padding: ${(props) => props.theme.spaces[3]}px 0;
`;
const OptionWrapper = styled.div<{
selected: boolean;
}>`
padding: ${(props) => props.theme.spaces[2] - 1}px
${(props) => props.theme.spaces[5]}px;
cursor: pointer;
display: flex;
align-items: center;
background-color: ${(props) =>
props.selected ? props.theme.colors.propertyPane.dropdownSelectBg : null};
.${Classes.TEXT} {
color: ${(props) => props.theme.colors.propertyPane.label};
}
.${Classes.ICON} {
margin-right: ${(props) => props.theme.spaces[5]}px;
svg {
path {
${(props) =>
props.selected
? `fill: ${props.theme.colors.dropdown.selected.icon}`
: `fill: ${props.theme.colors.dropdown.icon}`};
}
}
}
&:hover {
background-color: ${(props) => props.theme.colors.dropdown.hovered.bg};
.${Classes.TEXT} {
color: ${(props) => props.theme.colors.textOnDarkBG};
}
.${Classes.ICON} {
svg {
path {
fill: ${(props) => props.theme.colors.dropdown.selected.icon};
}
}
}
}
`;
const LabelWrapper = styled.div<{ label?: string }>`
display: flex;
flex-direction: column;
align-items: flex-start;
span:last-child {
margin-top: ${(props) => props.theme.spaces[2] - 1}px;
}
&:hover {
.${Classes.TEXT} {
color: ${(props) => props.theme.colors.dropdown.selected.text};
}
}
`;
const MultiOptionWrapper = styled(OptionWrapper)`
background-color: transparent;
.${Classes.MULTI_SELECT_BOX} {
background-color: ${(props) =>
props.selected ? props.theme.colors.dropdown.hovered.bg : "transparent"};
border-color: ${(props) =>
props.selected
? props.theme.colors.dropdown.hovered.bg
: props.theme.colors.propertyPane.jsIconBg};
}
&:hover {
.${Classes.MULTI_SELECT_BOX} {
border-color: ${(props) =>
props.selected
? props.theme.colors.propertyPane.multiDropdownBoxHoverBg
: props.theme.colors.textOnDarkBG};
background-color: ${(props) =>
props.selected
? props.theme.colors.propertyPane.multiDropdownBoxHoverBg
: "transparent"};
}
}
`;
const SquareBox = styled.div`
width: 14px;
height: 14px;
margin-right: 8px;
border: 1px solid;
box-sizing: border-box;
`;
export type DropdownOption = {
label?: string;
value?: string;
id?: string;
icon?: IconName;
};
type DropdownProps = CommonComponentProps & {
options: DropdownOption[];
selected: string[];
onSelect: (value: string[]) => void;
width?: string;
showLabelOnly?: boolean;
optionWidth?: string;
selectAll?: boolean;
selectAllQuantifier?: string;
};
function MultiSelectDropdown(props: DropdownProps) {
const [isOpen, setIsOpen] = useState<boolean>(false);
const [containerWidth, setContainerWidth] = useState<string>("0px");
const measuredRef = useCallback((node) => {
if (node !== null && !props.optionWidth) {
setContainerWidth(`${node.getBoundingClientRect().width}px`);
}
}, []);
const optionClickHandler = (option: string) => {
const currentIndex = _.findIndex(props.selected, (value) => {
return value === option;
});
let selectedOption = [...props.selected];
if (currentIndex === -1) {
selectedOption.push(option);
} else {
selectedOption.splice(currentIndex, 1);
}
if (props.selectAll) {
const isAllSelectorPresent = props.selected.includes(
props.selectAllQuantifier as string,
);
const isAllSelectorSelected =
props.selectAllQuantifier && option === props.selectAllQuantifier;
if (isAllSelectorSelected) {
if (isAllSelectorPresent) {
selectedOption = [];
} else {
selectedOption = _.map(
props.options,
(item) => item.value,
) as string[];
}
} else if (isAllSelectorPresent) {
selectedOption = selectedOption.filter(
(item) => item !== props.selectAllQuantifier,
);
} else if (
!isAllSelectorPresent &&
selectedOption.length === props.options.length - 1
) {
selectedOption = _.map(props.options, (item) => item.value) as string[];
}
}
props.onSelect && props.onSelect([...selectedOption]);
};
const isItemSelected = (item?: string) => {
if (!item) {
return false;
}
return props.selected.includes(item);
};
return (
<DropdownContainer
data-cy={props.cypressSelector}
ref={measuredRef}
tabIndex={0}
width={props.width}
>
<Popover
boundary="scrollParent"
isOpen={isOpen && !props.disabled}
minimal
onInteraction={(state) => setIsOpen(state)}
position={Position.TOP_LEFT}
>
<Selected
className={props.className}
disabled={props.disabled}
isOpen={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
<Text type={TextType.P1}>
{props.selected.length
? `${props.selected.length} Selected`
: "Select file types"}
</Text>
<Icon name="downArrow" size={IconSize.XXS} />
</Selected>
<DropdownWrapper
width={props.optionWidth ? props.optionWidth : containerWidth}
>
{props.options.map((option: DropdownOption, index: number) => {
return (
<MultiOptionWrapper
className="t--multi-dropdown-option"
key={index}
onClick={() => {
optionClickHandler(option.value as string);
}}
selected={isItemSelected(option.value)}
>
<SquareBox className={Classes.MULTI_SELECT_BOX} />
{option.icon ? (
<Icon name={option.icon} size={IconSize.LARGE} />
) : null}
{props.showLabelOnly ? (
<Text type={TextType.P3}>{option.label}</Text>
) : option.label && option.value ? (
<LabelWrapper className="label-container">
<Text type={TextType.H5}>{option.value}</Text>
<Text type={TextType.P3}>{option.label}</Text>
</LabelWrapper>
) : (
<Text type={TextType.P3}>{option.value}</Text>
)}
</MultiOptionWrapper>
);
})}
</DropdownWrapper>
</Popover>
</DropdownContainer>
);
}
export default MultiSelectDropdown;