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";
|
2020-01-21 12:48:42 +00:00
|
|
|
import {
|
|
|
|
|
MenuItem,
|
|
|
|
|
Button,
|
|
|
|
|
ControlGroup,
|
|
|
|
|
Label,
|
|
|
|
|
Classes,
|
2020-02-06 07:01:25 +00:00
|
|
|
Checkbox,
|
|
|
|
|
Icon,
|
2020-01-21 12:48:42 +00:00
|
|
|
} 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";
|
2020-01-21 12:48:42 +00:00
|
|
|
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, {
|
2020-03-13 07:24:03 +00:00
|
|
|
createGlobalStyle,
|
2020-02-06 07:01:25 +00:00
|
|
|
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 {
|
2020-01-21 12:48:42 +00:00
|
|
|
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-24 07:30:53 +00:00
|
|
|
box-shadow: none;
|
|
|
|
|
background: white;
|
2019-12-18 07:23:28 +00:00
|
|
|
}
|
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-27 07:14:32 +00:00
|
|
|
const StyledControlGroup = styled(ControlGroup)<{ haslabel: string }>`
|
2020-02-06 07:01:25 +00:00
|
|
|
&&& > {
|
2020-01-21 12:48:42 +00:00
|
|
|
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;
|
2020-01-21 12:48:42 +00:00
|
|
|
flex: 0 1 30%;
|
2020-02-06 07:01:25 +00:00
|
|
|
max-width: calc(30% - ${WIDGET_PADDING}px);
|
2020-01-21 12:48:42 +00:00
|
|
|
text-align: right;
|
|
|
|
|
}
|
2020-02-06 07:01:25 +00:00
|
|
|
span {
|
|
|
|
|
max-width: ${props =>
|
2020-02-27 07:14:32 +00:00
|
|
|
props.haslabel === "true" ? `calc(70% - ${WIDGET_PADDING}px)` : "100%"};
|
2020-02-06 07:01:25 +00:00
|
|
|
}
|
2020-01-21 12:48:42 +00:00
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
2020-03-13 07:24:03 +00:00
|
|
|
const DropdownStyles = createGlobalStyle`
|
|
|
|
|
.select-popover-wrapper {
|
2020-02-06 07:01:25 +00:00
|
|
|
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;
|
2020-02-18 10:41:52 +00:00
|
|
|
background: white;
|
2020-03-13 07:24:03 +00:00
|
|
|
&& .${Classes.MENU} {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
max-height: auto;
|
|
|
|
|
}
|
|
|
|
|
&&&& .${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.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};
|
2020-02-06 07:01:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-13 07:24:03 +00:00
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
const DropdownContainer = styled.div`
|
|
|
|
|
${BlueprintCSSTransform}
|
2020-01-21 12:48:42 +00:00
|
|
|
`;
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
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 (
|
2020-01-21 12:48:42 +00:00
|
|
|
<DropdownContainer>
|
2020-03-13 07:24:03 +00:00
|
|
|
<DropdownStyles />
|
2020-02-27 07:14:32 +00:00
|
|
|
<StyledControlGroup
|
|
|
|
|
fill
|
|
|
|
|
haslabel={!!this.props.label ? "true" : "false"}
|
|
|
|
|
>
|
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>
|
|
|
|
|
)}
|
2020-01-21 12:48:42 +00:00
|
|
|
{this.props.selectionType === "SINGLE_SELECT" ? (
|
|
|
|
|
<StyledSingleDropDown
|
2020-02-06 07:01:25 +00:00
|
|
|
className={this.props.isLoading ? Classes.SKELETON : ""}
|
2020-01-21 12:48:42 +00:00
|
|
|
items={this.props.options}
|
|
|
|
|
filterable={false}
|
2020-02-06 07:01:25 +00:00
|
|
|
itemRenderer={this.renderSingleSelectItem}
|
2020-01-21 12:48:42 +00:00
|
|
|
onItemSelect={this.onItemSelect}
|
2020-02-06 07:01:25 +00:00
|
|
|
popoverProps={{
|
|
|
|
|
minimal: true,
|
2020-03-13 07:24:03 +00:00
|
|
|
usePortal: true,
|
|
|
|
|
popoverClassName: "select-popover-wrapper",
|
2020-02-06 07:01:25 +00:00
|
|
|
}}
|
2020-01-21 12:48:42 +00:00
|
|
|
>
|
|
|
|
|
<Button
|
2020-02-06 07:01:25 +00:00
|
|
|
rightIcon={IconNames.CHEVRON_DOWN}
|
2020-01-21 12:48:42 +00:00
|
|
|
text={
|
2020-02-11 11:03:10 +00:00
|
|
|
!_.isEmpty(this.props.options) &&
|
|
|
|
|
this.props.selectedIndex !== undefined
|
2020-01-21 12:48:42 +00:00
|
|
|
? this.props.options[this.props.selectedIndex].label
|
2020-01-28 11:46:04 +00:00
|
|
|
: "-- Empty --"
|
2020-01-21 12:48:42 +00:00
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</StyledSingleDropDown>
|
|
|
|
|
) : (
|
|
|
|
|
<StyledMultiDropDown
|
2020-02-06 07:01:25 +00:00
|
|
|
className={this.props.isLoading ? Classes.SKELETON : ""}
|
2020-01-21 12:48:42 +00:00
|
|
|
items={this.props.options}
|
|
|
|
|
placeholder={this.props.placeholder}
|
|
|
|
|
tagRenderer={this.renderTag}
|
2020-02-06 07:01:25 +00:00
|
|
|
itemRenderer={this.renderMultiSelectItem}
|
2020-01-21 12:48:42 +00:00
|
|
|
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} />,
|
|
|
|
|
}}
|
2020-01-21 12:48:42 +00:00
|
|
|
onItemSelect={this.onItemSelect}
|
2020-02-06 07:01:25 +00:00
|
|
|
popoverProps={{
|
|
|
|
|
minimal: true,
|
2020-03-13 07:24:03 +00:00
|
|
|
usePortal: true,
|
|
|
|
|
popoverClassName: "select-popover-wrapper",
|
2020-02-06 07:01:25 +00:00
|
|
|
}}
|
2020-03-13 07:24:03 +00:00
|
|
|
/>
|
2020-01-21 12:48:42 +00:00
|
|
|
)}
|
|
|
|
|
</StyledControlGroup>
|
|
|
|
|
</DropdownContainer>
|
2019-10-31 05:28:11 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-31 09:04:19 +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;
|