PromucFlow_constructor/app/client/src/components/designSystems/blueprint/DropdownComponent.tsx

355 lines
9.1 KiB
TypeScript
Raw Normal View History

2020-02-06 07:01:25 +00:00
import React, { ReactNode } from "react";
2019-11-25 05:07:27 +00:00
import { ComponentProps } from "components/designSystems/appsmith/BaseComponent";
import {
MenuItem,
Button,
ControlGroup,
Label,
Classes,
2020-02-06 07:01:25 +00:00
Checkbox,
Icon,
} from "@blueprintjs/core";
2020-02-06 07:01:25 +00:00
import { IconNames } from "@blueprintjs/icons";
2019-11-25 05:07:27 +00:00
import { SelectionType, DropdownOption } from "widgets/DropdownWidget";
2020-02-06 07:01:25 +00:00
import {
Select,
MultiSelect,
IItemRendererProps,
Classes as MultiSelectClasses,
} from "@blueprintjs/select";
2019-10-31 05:28:11 +00:00
import _ from "lodash";
import { WIDGET_PADDING } from "constants/WidgetConstants";
2019-11-06 12:12:41 +00:00
import "../../../../node_modules/@blueprintjs/select/lib/css/blueprint-select.css";
2020-02-06 07:01:25 +00:00
import styled, {
labelStyle,
BlueprintCSSTransform,
BlueprintInputTransform,
} from "constants/DefaultTheme";
import { Colors } from "constants/Colors";
2019-10-31 05:28:11 +00:00
const SingleDropDown = Select.ofType<DropdownOption>();
const MultiDropDown = MultiSelect.ofType<DropdownOption>();
2019-12-18 07:23:28 +00:00
const StyledSingleDropDown = styled(SingleDropDown)`
div {
flex: 1 1 auto;
2019-12-18 07:23:28 +00:00
}
span {
width: 100%;
2020-02-06 07:01:25 +00:00
position: relative;
2019-12-18 07:23:28 +00:00
}
2020-02-06 07:01:25 +00:00
.${Classes.BUTTON} {
2019-12-18 07:23:28 +00:00
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
}
2020-02-06 07:01:25 +00:00
.${Classes.BUTTON_TEXT} {
2019-12-18 07:23:28 +00:00
text-overflow: ellipsis;
2020-02-06 07:01:25 +00:00
text-align: left;
2019-12-18 07:23:28 +00:00
overflow: hidden;
2020-02-06 07:01:25 +00:00
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
2019-12-18 07:23:28 +00:00
}
2020-02-06 07:01:25 +00:00
&& {
.${Classes.ICON} {
width: fit-content;
color: ${Colors.SLATE_GRAY};
}
2019-12-18 07:23:28 +00:00
}
`;
2019-12-18 17:05:28 +00:00
2020-02-06 07:01:25 +00:00
const StyledControlGroup = styled(ControlGroup)<{ hasLabel: boolean }>`
&&& > {
label {
2020-01-28 08:21:22 +00:00
${labelStyle}
2020-02-06 07:01:25 +00:00
margin: 7px ${WIDGET_PADDING * 2}px 0 0;
align-self: flex-start;
flex: 0 1 30%;
2020-02-06 07:01:25 +00:00
max-width: calc(30% - ${WIDGET_PADDING}px);
text-align: right;
}
2020-02-06 07:01:25 +00:00
span {
max-width: ${props =>
props.hasLabel ? `calc(70% - ${WIDGET_PADDING}px)` : "100%"};
}
}
`;
const DropdownContainer = styled.div`
2020-02-06 07:01:25 +00:00
${BlueprintCSSTransform}
&&&& .${Classes.MENU_ITEM} {
border-radius: ${props => props.theme.radii[1]}px;
&:hover{
background: ${Colors.POLAR};
}
&.${Classes.ACTIVE} {
background: ${Colors.POLAR};
color: ${props => props.theme.colors.textDefault};
position:relative;
&.single-select{
&:before{
left: 0;
top: -2px;
position: absolute;
content: "";
background: ${props => props.theme.colors.primary};
border-radius: 4px 0 0 4px;
width: 4px;
height:100%;
}
}
}
}
&& .${Classes.POPOVER} {
width: 100%;
border-radius: ${props => props.theme.radii[1]}px;
box-shadow: 0px 2px 4px rgba(67, 70, 74, 0.14);
padding: ${props => props.theme.spaces[3]}px;
background:white;
}
&& .${Classes.POPOVER_WRAPPER} {
position:relative;
.${Classes.OVERLAY} {
position: absolute;
.${Classes.TRANSITION_CONTAINER} {
width: 100%;
}
}
}
&& .${Classes.MENU} {
max-width: 100%;
max-height: auto;
}
width: 100%;
`;
2019-12-18 17:05:28 +00:00
const StyledMultiDropDown = styled(MultiDropDown)`
2020-02-06 07:01:25 +00:00
div {
flex: 1 1 auto;
}
.${MultiSelectClasses.MULTISELECT} {
position: relative;
2019-12-18 17:05:28 +00:00
min-width: 0;
}
2020-02-06 07:01:25 +00:00
&& {
${BlueprintInputTransform}
.${Classes.TAG_INPUT} {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
text-overflow: ellipsis;
overflow: hidden;
.${Classes.TAG_INPUT_VALUES} {
margin-top: 0;
overflow: hidden;
padding: 2px 0;
}
.${Classes.TAG} {
background: none;
border: 1px solid #D0D7DD;
border-radius: 2px;
margin-bottom: 0;
max-width: 100px;
}
& > .${Classes.ICON} {
align-self: center;
margin-right: 10px;
color: ${Colors.SLATE_GRAY};
}
.${Classes.INPUT_GHOST} {
width: 0px;
flex: 0 0 auto;
}
}
}
&&&& {
.${Classes.CONTROL} .${Classes.CONTROL_INDICATOR} {
background: white;
box-shadow: none;
border-width: 2px;
border-style: solid;
border-color: ${Colors.GEYSER};
&::before {
width: auto;
height: 1em;
}
}
.${Classes.CONTROL} input:checked ~ .${Classes.CONTROL_INDICATOR} {
background: ${props => props.theme.colors.primary};
color: ${props => props.theme.colors.textOnDarkBG};
border-color: ${props => props.theme.colors.primary};
}
}
`;
const StyledCheckbox = styled(Checkbox)`
&&.${Classes.CHECKBOX}.${Classes.CONTROL} {
margin: 0;
}
2019-12-18 17:05:28 +00:00
`;
2020-02-06 07:01:25 +00:00
2019-10-31 05:28:11 +00:00
class DropDownComponent extends React.Component<DropDownComponentProps> {
render() {
const selectedItems = this.props.selectedIndexArr
? _.map(this.props.selectedIndexArr, index => {
return this.props.options[index];
})
: [];
return (
<DropdownContainer>
2020-02-06 07:01:25 +00:00
<StyledControlGroup fill hasLabel={!!this.props.label}>
2020-01-28 11:46:04 +00:00
{this.props.label && (
2020-01-31 11:13:16 +00:00
<Label
className={
this.props.isLoading
? Classes.SKELETON
: Classes.TEXT_OVERFLOW_ELLIPSIS
}
>
2020-01-28 11:46:04 +00:00
{this.props.label}
</Label>
)}
{this.props.selectionType === "SINGLE_SELECT" ? (
<StyledSingleDropDown
2020-02-06 07:01:25 +00:00
className={this.props.isLoading ? Classes.SKELETON : ""}
items={this.props.options}
filterable={false}
2020-02-06 07:01:25 +00:00
itemRenderer={this.renderSingleSelectItem}
onItemSelect={this.onItemSelect}
2020-02-06 07:01:25 +00:00
popoverProps={{
minimal: true,
usePortal: false,
}}
>
<Button
2020-02-06 07:01:25 +00:00
rightIcon={IconNames.CHEVRON_DOWN}
text={
2020-02-11 11:03:10 +00:00
!_.isEmpty(this.props.options) &&
this.props.selectedIndex !== undefined
? this.props.options[this.props.selectedIndex].label
2020-01-28 11:46:04 +00:00
: "-- Empty --"
}
/>
</StyledSingleDropDown>
) : (
<StyledMultiDropDown
2020-02-06 07:01:25 +00:00
className={this.props.isLoading ? Classes.SKELETON : ""}
items={this.props.options}
placeholder={this.props.placeholder}
tagRenderer={this.renderTag}
2020-02-06 07:01:25 +00:00
itemRenderer={this.renderMultiSelectItem}
selectedItems={selectedItems}
2020-02-06 07:01:25 +00:00
tagInputProps={{
onRemove: this.onItemRemoved,
tagProps: { minimal: true },
inputProps: { readOnly: true },
rightElement: <Icon icon={IconNames.CHEVRON_DOWN} />,
}}
onItemSelect={this.onItemSelect}
2020-02-06 07:01:25 +00:00
popoverProps={{
minimal: true,
usePortal: false,
}}
></StyledMultiDropDown>
)}
</StyledControlGroup>
</DropdownContainer>
2019-10-31 05:28:11 +00:00
);
}
onItemSelect = (item: DropdownOption): void => {
2019-10-31 05:28:11 +00:00
this.props.onOptionSelected(item);
};
onItemRemoved = (_tag: string, index: number) => {
this.props.onOptionRemoved(index);
};
renderTag = (option: DropdownOption) => {
return option.label;
};
isOptionSelected = (selectedOption: DropdownOption) => {
const optionIndex = _.findIndex(this.props.options, option => {
return option.value === selectedOption.value;
});
if (this.props.selectionType === "SINGLE_SELECT") {
return optionIndex === this.props.selectedIndex;
} else {
return (
_.findIndex(this.props.selectedIndexArr, index => {
return index === optionIndex;
}) !== -1
);
}
};
2020-02-06 07:01:25 +00:00
renderSingleSelectItem = (
option: DropdownOption,
itemProps: IItemRendererProps,
) => {
2019-10-31 05:28:11 +00:00
if (!itemProps.modifiers.matchesPredicate) {
return null;
}
const isSelected: boolean = this.isOptionSelected(option);
return (
<MenuItem
2020-02-06 07:01:25 +00:00
className="single-select"
active={isSelected}
2019-10-31 05:28:11 +00:00
key={option.value}
onClick={itemProps.handleClick}
text={option.label}
/>
);
};
2020-02-06 07:01:25 +00:00
renderMultiSelectItem = (
option: DropdownOption,
itemProps: IItemRendererProps,
) => {
if (!itemProps.modifiers.matchesPredicate) {
return null;
}
const isSelected: boolean = this.isOptionSelected(option);
const content: ReactNode = (
<React.Fragment>
<StyledCheckbox
checked={isSelected}
label={option.label}
alignIndicator="left"
onChange={(e: any) => itemProps.handleClick(e)}
/>
</React.Fragment>
);
return (
<MenuItem
className="multi-select"
active={isSelected}
key={option.value}
text={content}
/>
);
};
2019-10-31 05:28:11 +00:00
}
export interface DropDownComponentProps extends ComponentProps {
selectionType: SelectionType;
disabled?: boolean;
onOptionSelected: (optionSelected: DropdownOption) => void;
onOptionRemoved: (removedIndex: number) => void;
placeholder?: string;
label?: string;
2020-02-11 11:03:10 +00:00
selectedIndex?: number;
2019-10-31 05:28:11 +00:00
selectedIndexArr: number[];
options: DropdownOption[];
2019-12-03 04:41:10 +00:00
isLoading: boolean;
2019-10-31 05:28:11 +00:00
}
export default DropDownComponent;