Feature/dropdown
This commit is contained in:
parent
3b393a8089
commit
9d659ddff0
|
|
@ -25,6 +25,8 @@ export interface ComponentProps {
|
|||
widgetId: string;
|
||||
widgetName?: string;
|
||||
style: BaseStyle;
|
||||
isDisabled?: boolean;
|
||||
isVisibile?: boolean;
|
||||
}
|
||||
|
||||
export default BaseComponent;
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { Text } from "@blueprintjs/core";
|
||||
import styled from "styled-components";
|
||||
import { ComponentProps } from "./BaseComponent";
|
||||
import { Container } from "./ContainerComponent";
|
||||
|
||||
type TextStyleProps = {
|
||||
styleName: "primary" | "secondary" | "error";
|
||||
};
|
||||
|
||||
export const BaseText = styled(Text)<TextStyleProps>`
|
||||
color: ${props => props.theme.colors[props.styleName]};
|
||||
`;
|
||||
|
||||
export interface TextComponentProps extends ComponentProps {
|
||||
text?: string;
|
||||
ellipsize?: boolean;
|
||||
tagName?: keyof JSX.IntrinsicElements;
|
||||
}
|
||||
|
||||
class TextComponent extends React.Component<TextComponentProps> {
|
||||
render() {
|
||||
return (
|
||||
<Container {...this.props}>
|
||||
<Text ellipsize={this.props.ellipsize} tagName={this.props.tagName}>
|
||||
{this.props.text}
|
||||
</Text>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TextComponent;
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { ComponentProps } from "../appsmith/BaseComponent";
|
||||
import { Boundary, Breadcrumbs, IBreadcrumbProps } from "@blueprintjs/core";
|
||||
import { Container } from "../appsmith/ContainerComponent";
|
||||
|
||||
class BreadcrumbsComponent extends React.Component<BreadcrumbsComponentProps> {
|
||||
render() {
|
||||
return (
|
||||
<Container {...this.props}>
|
||||
<Breadcrumbs
|
||||
collapseFrom={this.props.collapseFrom}
|
||||
items={this.props.items}
|
||||
minVisibleItems={this.props.minVisibleItems}
|
||||
className={this.props.className}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface BreadcrumbsComponentProps extends ComponentProps {
|
||||
width?: number;
|
||||
collapseFrom?: Boundary;
|
||||
className?: string;
|
||||
minVisibleItems?: number;
|
||||
items?: IBreadcrumbProps[];
|
||||
}
|
||||
|
||||
export default BreadcrumbsComponent;
|
||||
|
|
@ -2,7 +2,8 @@ import React from "react";
|
|||
import { AnchorButton, IButtonProps, MaybeElement } from "@blueprintjs/core";
|
||||
import styled, { css } from "styled-components";
|
||||
import { Container } from "../appsmith/ContainerComponent";
|
||||
import { TextComponentProps } from "../appsmith/TextViewComponent";
|
||||
import { TextComponentProps } from "./TextComponent";
|
||||
import { ButtonStyle } from "../../widgets/ButtonWidget";
|
||||
|
||||
const ButtonColorStyles = css<ButtonStyleProps>`
|
||||
color: ${props => {
|
||||
|
|
@ -87,8 +88,22 @@ interface ButtonContainerProps extends TextComponentProps {
|
|||
icon?: MaybeElement;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
disabled?: boolean;
|
||||
buttonStyle?: ButtonStyle;
|
||||
}
|
||||
|
||||
const mapButtonStyleToStyleName = (buttonStyle?: ButtonStyle) => {
|
||||
switch (buttonStyle) {
|
||||
case "PRIMARY_BUTTON":
|
||||
return "primary";
|
||||
case "SECONDARY_BUTTON":
|
||||
return "secondary";
|
||||
case "DANGER_BUTTON":
|
||||
return "error";
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// To be used with the canvas
|
||||
const ButtonContainer = (props: ButtonContainerProps & ButtonStyleProps) => {
|
||||
return (
|
||||
|
|
@ -96,7 +111,8 @@ const ButtonContainer = (props: ButtonContainerProps & ButtonStyleProps) => {
|
|||
<BaseButton
|
||||
icon={props.icon}
|
||||
text={props.text}
|
||||
styleName={props.styleName}
|
||||
filled={props.buttonStyle === "PRIMARY_BUTTON"}
|
||||
styleName={mapButtonStyleToStyleName(props.buttonStyle)}
|
||||
onClick={props.onClick}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
|
|
|
|||
125
app/client/src/components/blueprint/DropdownComponent.tsx
Normal file
125
app/client/src/components/blueprint/DropdownComponent.tsx
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import * as React from "react";
|
||||
import { ComponentProps } from "../appsmith/BaseComponent";
|
||||
import { MenuItem, Button } from "@blueprintjs/core";
|
||||
import { Container } from "../appsmith/ContainerComponent";
|
||||
import { SelectionType, DropdownOption } from "../../widgets/DropdownWidget";
|
||||
import {
|
||||
Select,
|
||||
MultiSelect,
|
||||
IItemRendererProps,
|
||||
ItemRenderer,
|
||||
} from "@blueprintjs/select";
|
||||
import _ from "lodash";
|
||||
|
||||
const SingleDropDown = Select.ofType<DropdownOption>();
|
||||
const MultiDropDown = MultiSelect.ofType<DropdownOption>();
|
||||
|
||||
class DropDownComponent extends React.Component<DropDownComponentProps> {
|
||||
constructor(props: DropDownComponentProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const selectedItems = this.props.selectedIndexArr
|
||||
? _.map(this.props.selectedIndexArr, index => {
|
||||
return this.props.options[index];
|
||||
})
|
||||
: [];
|
||||
return (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
{this.props.selectionType === "SINGLE_SELECT" ? (
|
||||
<SingleDropDown
|
||||
items={this.props.options}
|
||||
filterable={false}
|
||||
itemRenderer={this.renderItem}
|
||||
onItemSelect={this.onItemSelect}
|
||||
>
|
||||
<Container {...this.props}>
|
||||
<Button
|
||||
intent={"primary"}
|
||||
text={
|
||||
!_.isEmpty(this.props.options)
|
||||
? this.props.options[this.props.selectedIndex].label
|
||||
: "Add options"
|
||||
}
|
||||
/>
|
||||
</Container>
|
||||
</SingleDropDown>
|
||||
) : (
|
||||
<Container {...this.props}>
|
||||
<MultiDropDown
|
||||
items={this.props.options}
|
||||
placeholder={this.props.placeholder}
|
||||
tagRenderer={this.renderTag}
|
||||
itemRenderer={this.renderItem}
|
||||
selectedItems={selectedItems}
|
||||
tagInputProps={{ onRemove: this.onItemRemoved }}
|
||||
onItemSelect={this.onItemSelect}
|
||||
></MultiDropDown>
|
||||
</Container>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onItemSelect = (
|
||||
item: DropdownOption,
|
||||
event?: React.SyntheticEvent<HTMLElement>,
|
||||
): void => {
|
||||
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
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderItem = (option: DropdownOption, itemProps: IItemRendererProps) => {
|
||||
if (!itemProps.modifiers.matchesPredicate) {
|
||||
return null;
|
||||
}
|
||||
const isSelected: boolean = this.isOptionSelected(option);
|
||||
console.log("is selected " + isSelected);
|
||||
return (
|
||||
<MenuItem
|
||||
icon={isSelected ? "tick" : "blank"}
|
||||
active={itemProps.modifiers.active}
|
||||
key={option.value}
|
||||
onClick={itemProps.handleClick}
|
||||
text={option.label}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export interface DropDownComponentProps extends ComponentProps {
|
||||
selectionType: SelectionType;
|
||||
disabled?: boolean;
|
||||
onOptionSelected: (optionSelected: DropdownOption) => void;
|
||||
onOptionRemoved: (removedIndex: number) => void;
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
selectedIndex: number;
|
||||
selectedIndexArr: number[];
|
||||
options: DropdownOption[];
|
||||
}
|
||||
|
||||
export default DropDownComponent;
|
||||
|
|
@ -6,6 +6,9 @@ import {
|
|||
IconName,
|
||||
InputGroup,
|
||||
Button,
|
||||
Label,
|
||||
Classes,
|
||||
Text,
|
||||
} from "@blueprintjs/core";
|
||||
import { Container } from "../appsmith/ContainerComponent";
|
||||
import { InputType } from "../../widgets/InputWidget";
|
||||
|
|
@ -25,70 +28,99 @@ class InputComponent extends React.Component<
|
|||
this.state = { showPassword: false };
|
||||
}
|
||||
|
||||
onTextChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.props.onValueChange(event.target.value);
|
||||
};
|
||||
|
||||
onNumberChange = (valueAsNum: number, valueAsString: string) => {
|
||||
this.props.onValueChange(valueAsString);
|
||||
};
|
||||
|
||||
isNumberInputType(inputType: InputType) {
|
||||
return (
|
||||
this.props.inputType === "INTEGER" ||
|
||||
this.props.inputType === "PHONE_NUMBER" ||
|
||||
this.props.inputType === "NUMBER" ||
|
||||
this.props.inputType === "CURRENCY"
|
||||
);
|
||||
}
|
||||
|
||||
getIcon(inputType: InputType) {
|
||||
switch (inputType) {
|
||||
case "PHONE_NUMBER":
|
||||
return "phone";
|
||||
case "SEARCH":
|
||||
return "search";
|
||||
case "EMAIL":
|
||||
return "envelope";
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
getType(inputType: InputType) {
|
||||
switch (inputType) {
|
||||
case "PASSWORD":
|
||||
return this.state.showPassword ? "password" : "text";
|
||||
case "EMAIL":
|
||||
return "email";
|
||||
case "SEARCH":
|
||||
return "search";
|
||||
default:
|
||||
return "text";
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container {...this.props}>
|
||||
{this.props.inputType === "INTEGER" ||
|
||||
this.props.inputType === "PHONE_NUMBER" ||
|
||||
this.props.inputType === "NUMBER" ||
|
||||
this.props.inputType === "CURRENCY" ? (
|
||||
<NumericInput
|
||||
placeholder={this.props.placeholder}
|
||||
min={this.props.minNum}
|
||||
max={this.props.maxNum}
|
||||
disabled={this.props.disabled}
|
||||
intent={this.props.intent}
|
||||
defaultValue={this.props.defaultValue}
|
||||
leftIcon={
|
||||
this.props.inputType === "PHONE_NUMBER"
|
||||
? "phone"
|
||||
: this.props.leftIcon
|
||||
}
|
||||
type={this.props.inputType === "PHONE_NUMBER" ? "tel" : undefined}
|
||||
allowNumericCharactersOnly={true}
|
||||
stepSize={this.props.stepSize}
|
||||
/>
|
||||
) : this.props.inputType === "TEXT" ||
|
||||
this.props.inputType === "EMAIL" ||
|
||||
this.props.inputType === "PASSWORD" ||
|
||||
this.props.inputType === "SEARCH" ? (
|
||||
<InputGroup
|
||||
placeholder={this.props.placeholder}
|
||||
disabled={this.props.disabled}
|
||||
intent={this.props.intent}
|
||||
defaultValue={this.props.defaultValue}
|
||||
rightElement={
|
||||
this.props.inputType === "PASSWORD" ? (
|
||||
<Button
|
||||
icon={"lock"}
|
||||
onClick={() => {
|
||||
this.setState({ showPassword: !this.state.showPassword });
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
undefined
|
||||
)
|
||||
}
|
||||
type={
|
||||
this.props.inputType === "PASSWORD" && !this.state.showPassword
|
||||
? "password"
|
||||
: this.props.inputType === "EMAIL"
|
||||
? "email"
|
||||
: this.props.inputType === "SEARCH"
|
||||
? "search"
|
||||
: "text"
|
||||
}
|
||||
leftIcon={
|
||||
this.props.inputType === "SEARCH"
|
||||
? "search"
|
||||
: this.props.inputType === "EMAIL"
|
||||
? "envelope"
|
||||
: this.props.leftIcon
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
undefined
|
||||
)}
|
||||
<Label>
|
||||
{this.props.label}
|
||||
{this.isNumberInputType(this.props.inputType) ? (
|
||||
<NumericInput
|
||||
placeholder={this.props.placeholder}
|
||||
min={this.props.minNum}
|
||||
max={this.props.maxNum}
|
||||
maxLength={this.props.maxChars}
|
||||
disabled={this.props.disabled}
|
||||
intent={this.props.intent}
|
||||
defaultValue={this.props.defaultValue}
|
||||
onValueChange={this.onNumberChange}
|
||||
leftIcon={
|
||||
this.props.inputType === "PHONE_NUMBER"
|
||||
? "phone"
|
||||
: this.props.leftIcon
|
||||
}
|
||||
type={this.props.inputType === "PHONE_NUMBER" ? "tel" : undefined}
|
||||
allowNumericCharactersOnly={true}
|
||||
stepSize={this.props.stepSize}
|
||||
/>
|
||||
) : (
|
||||
<InputGroup
|
||||
placeholder={this.props.placeholder}
|
||||
disabled={this.props.disabled}
|
||||
maxLength={this.props.maxChars}
|
||||
intent={this.props.intent}
|
||||
onChange={this.onTextChange}
|
||||
defaultValue={this.props.defaultValue}
|
||||
rightElement={
|
||||
this.props.inputType === "PASSWORD" ? (
|
||||
<Button
|
||||
icon={"lock"}
|
||||
onClick={() => {
|
||||
this.setState({ showPassword: !this.state.showPassword });
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
undefined
|
||||
)
|
||||
}
|
||||
type={this.getType(this.props.inputType)}
|
||||
leftIcon={this.getIcon(this.props.inputType)}
|
||||
/>
|
||||
)}
|
||||
</Label>
|
||||
<Text>{this.props.errorMessage}</Text>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
@ -99,16 +131,19 @@ export interface InputComponentState {
|
|||
}
|
||||
|
||||
export interface InputComponentProps extends ComponentProps {
|
||||
inputType?: InputType;
|
||||
inputType: InputType;
|
||||
disabled?: boolean;
|
||||
intent?: Intent;
|
||||
defaultValue?: string;
|
||||
label: string;
|
||||
leftIcon?: IconName;
|
||||
allowNumericCharactersOnly?: boolean;
|
||||
fill?: boolean;
|
||||
errorMessage?: string;
|
||||
maxChars?: number;
|
||||
maxNum?: number;
|
||||
minNum?: number;
|
||||
onValueChange?: (valueAsNumber: number, valueAsString: string) => void;
|
||||
onValueChange: (valueAsString: string) => void;
|
||||
stepSize?: number;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { ComponentProps } from "../appsmith/BaseComponent";
|
||||
import { Intent, ITagProps, TagInput, HTMLInputProps } from "@blueprintjs/core";
|
||||
import { Container } from "../appsmith/ContainerComponent";
|
||||
class TagInputComponent extends React.Component<TagInputComponentProps> {
|
||||
render() {
|
||||
return (
|
||||
<Container {...this.props}>
|
||||
<TagInput
|
||||
placeholder={this.props.placeholder}
|
||||
values={this.props.values}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface TagInputComponentProps extends ComponentProps {
|
||||
addOnPaste?: boolean;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
fill?: boolean;
|
||||
inputProps?: HTMLInputProps;
|
||||
inputValue?: string; //Controlled value of the <input> element.
|
||||
intent?: Intent;
|
||||
large?: boolean; //Whether the tag input should use a large size
|
||||
onInputChange?: React.FormEventHandler<HTMLInputElement>;
|
||||
placeholder?: string;
|
||||
rightElement?: JSX.Element;
|
||||
separator?: string | RegExp | false;
|
||||
tagProps?: ITagProps;
|
||||
values?: string[]; //Required field
|
||||
}
|
||||
|
||||
export default TagInputComponent;
|
||||
48
app/client/src/components/blueprint/TextComponent.tsx
Normal file
48
app/client/src/components/blueprint/TextComponent.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import * as React from "react";
|
||||
import { Text, Classes } from "@blueprintjs/core";
|
||||
import styled from "styled-components";
|
||||
import { ComponentProps } from "../appsmith/BaseComponent";
|
||||
import { Container } from "../appsmith/ContainerComponent";
|
||||
import { TextStyle } from "../../widgets/TextWidget";
|
||||
|
||||
type TextStyleProps = {
|
||||
styleName: "primary" | "secondary" | "error";
|
||||
};
|
||||
|
||||
export const BaseText = styled(Text)<TextStyleProps>``;
|
||||
|
||||
export interface TextComponentProps extends ComponentProps {
|
||||
text?: string;
|
||||
ellipsize?: boolean;
|
||||
textStyle?: TextStyle;
|
||||
}
|
||||
|
||||
class TextComponent extends React.Component<TextComponentProps> {
|
||||
getTextClass(textStyle?: TextStyle) {
|
||||
switch (textStyle) {
|
||||
case "HEADING":
|
||||
return Classes.TEXT_LARGE;
|
||||
case "LABEL":
|
||||
return undefined;
|
||||
case "BODY":
|
||||
return Classes.TEXT_SMALL;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container {...this.props}>
|
||||
<Text
|
||||
className={this.getTextClass(this.props.textStyle)}
|
||||
ellipsize={this.props.ellipsize}
|
||||
>
|
||||
{this.props.text}
|
||||
</Text>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TextComponent;
|
||||
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||
import { connect } from "react-redux";
|
||||
import { withRouter, RouteComponentProps } from "react-router";
|
||||
import FormRow from "./FormRow";
|
||||
import { BaseText } from "../appsmith/TextViewComponent";
|
||||
import { BaseText } from "../blueprint/TextComponent";
|
||||
import { BaseTabbedView } from "../appsmith/TabbedView";
|
||||
import styled from "styled-components";
|
||||
import { AppState } from "../../reducers";
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ const DraggableComponent = (props: DraggableComponentProps) => {
|
|||
</Tooltip>
|
||||
</DeleteControl>
|
||||
<EditControl className="control" onClick={togglePropertyEditor}>
|
||||
<Tooltip content="Toggle properties pane" hoverOpenDelay={500}>
|
||||
<Tooltip content="Toggle props" hoverOpenDelay={500}>
|
||||
{editControlIcon}
|
||||
</Tooltip>
|
||||
</EditControl>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
|
|||
propertyName: "text",
|
||||
label: "Button Text",
|
||||
controlType: "INPUT_TEXT",
|
||||
placeholderText: "Enter button text here",
|
||||
placeholderText: "Enter button text",
|
||||
},
|
||||
{
|
||||
id: "1.2",
|
||||
|
|
@ -22,6 +22,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
|
|||
options: [
|
||||
{ label: "Primary Button", value: "PRIMARY_BUTTON" },
|
||||
{ label: "Secondary Button", value: "SECONDARY_BUTTON" },
|
||||
{ label: "Danger Button", value: "DANGER_BUTTON" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -61,7 +62,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
|
|||
propertyName: "text",
|
||||
label: "Text",
|
||||
controlType: "INPUT_TEXT",
|
||||
placeholderText: "Enter your text here",
|
||||
placeholderText: "Enter your text",
|
||||
},
|
||||
{
|
||||
id: "3.2",
|
||||
|
|
@ -72,7 +73,6 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
|
|||
{ label: "Heading", value: "HEADING" },
|
||||
{ label: "Label", value: "LABEL" },
|
||||
{ label: "Body", value: "BODY" },
|
||||
{ label: "Sub text", value: "SUB_TEXT" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -140,7 +140,6 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
|
|||
{ label: "Password", value: "PASSWORD" },
|
||||
{ label: "Phone Number", value: "PHONE_NUMBER" },
|
||||
{ label: "Email", value: "EMAIL" },
|
||||
{ label: "Search", value: "SEARCH" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -148,7 +147,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
|
|||
propertyName: "placeholderText",
|
||||
label: "Placeholder",
|
||||
controlType: "INPUT_TEXT",
|
||||
placeholderText: "Enter your text here",
|
||||
placeholderText: "Enter your text",
|
||||
},
|
||||
{
|
||||
id: "5.4",
|
||||
|
|
@ -156,41 +155,32 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
|
|||
label: "Max Chars",
|
||||
controlType: "INPUT_TEXT",
|
||||
inputType: "INTEGER",
|
||||
placeholderText: "Maximum character length",
|
||||
placeholderText: "Enter the max length",
|
||||
},
|
||||
{
|
||||
id: "5.5",
|
||||
propertyName: "validators",
|
||||
label: "Validators",
|
||||
controlType: "VALIDATION_INPUT",
|
||||
propertyName: "regex",
|
||||
label: "Regex",
|
||||
controlType: "INPUT_TEXT",
|
||||
inputType: "TEXT",
|
||||
placeholderText: "Enter the regex",
|
||||
},
|
||||
{
|
||||
id: "5.6",
|
||||
children: [
|
||||
{
|
||||
id: "5.6.1",
|
||||
propertyName: "focusIndexx",
|
||||
label: "Focus Index",
|
||||
controlType: "INPUT_TEXT",
|
||||
inputType: "INTEGER",
|
||||
placeholderText: "Enter the order of tab focus",
|
||||
},
|
||||
{
|
||||
id: "5.6.2",
|
||||
propertyName: "autoFocus",
|
||||
label: "Auto Focus",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
],
|
||||
propertyName: "errorMessage",
|
||||
label: "Error Message",
|
||||
controlType: "INPUT_TEXT",
|
||||
inputType: "TEXT",
|
||||
placeholderText: "Enter the message",
|
||||
},
|
||||
{
|
||||
id: "5.7",
|
||||
id: "5.8",
|
||||
propertyName: "isVisible",
|
||||
label: "Visibile",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
{
|
||||
id: "5.8",
|
||||
id: "5.9",
|
||||
propertyName: "isDisabled",
|
||||
label: "Disabled",
|
||||
controlType: "SWITCH",
|
||||
|
|
@ -407,7 +397,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
|
|||
children: [
|
||||
{
|
||||
id: "13.1",
|
||||
propertyName: "type",
|
||||
propertyName: "selectionType",
|
||||
label: "Selection Type",
|
||||
controlType: "DROP_DOWN",
|
||||
options: [
|
||||
|
|
@ -415,6 +405,12 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
|
|||
{ label: "Multi Select", value: "MULTI_SELECT" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "13.4",
|
||||
propertyName: "options",
|
||||
label: "Options",
|
||||
controlType: "INPUT_TEXT",
|
||||
},
|
||||
{
|
||||
id: "13.2",
|
||||
propertyName: "label",
|
||||
|
|
@ -429,12 +425,6 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
|
|||
controlType: "INPUT_TEXT",
|
||||
placeholderText: "Enter the placeholder",
|
||||
},
|
||||
{
|
||||
id: "13.4",
|
||||
propertyName: "options",
|
||||
label: "Options",
|
||||
controlType: "OPTION_INPUT",
|
||||
},
|
||||
{
|
||||
id: "13.5",
|
||||
propertyName: "isVisible",
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
isVisible: true,
|
||||
},
|
||||
TEXT_WIDGET: {
|
||||
text: "Not all labels are bad!",
|
||||
text: "Label me",
|
||||
textStyle: "LABEL",
|
||||
rows: 1,
|
||||
columns: 3,
|
||||
columns: 4,
|
||||
widgetName: "Text",
|
||||
},
|
||||
IMAGE_WIDGET: {
|
||||
|
|
@ -30,20 +30,20 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
inputType: "TEXT",
|
||||
label: "Label me",
|
||||
rows: 1,
|
||||
columns: 3,
|
||||
columns: 5,
|
||||
widgetName: "Input",
|
||||
},
|
||||
SWITCH_WIDGET: {
|
||||
isOn: false,
|
||||
label: "Turn me on",
|
||||
label: "Switch",
|
||||
rows: 1,
|
||||
columns: 4,
|
||||
widgetName: "Switch",
|
||||
},
|
||||
CONTAINER_WIDGET: {
|
||||
backgroundColor: "#FFFFFF",
|
||||
rows: 1,
|
||||
columns: 4,
|
||||
rows: 8,
|
||||
columns: 8,
|
||||
widgetName: "Container",
|
||||
},
|
||||
SPINNER_WIDGET: {
|
||||
|
|
@ -62,14 +62,19 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
TABLE_WIDGET: {
|
||||
rows: 5,
|
||||
columns: 7,
|
||||
label: "Don't table me!",
|
||||
label: "Data",
|
||||
widgetName: "Table",
|
||||
},
|
||||
DROP_DOWN_WIDGET: {
|
||||
rows: 1,
|
||||
columns: 3,
|
||||
selectionType: "SINGLE_SELECT",
|
||||
label: "Pick me!",
|
||||
label: "Select",
|
||||
options: [
|
||||
{ label: "Option 1", value: "1" },
|
||||
{ label: "Option 2", value: "2" },
|
||||
{ label: "Option 3", value: "3" },
|
||||
],
|
||||
widgetName: "Dropdown",
|
||||
},
|
||||
CHECKBOX_WIDGET: {
|
||||
|
|
@ -82,7 +87,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
RADIO_GROUP_WIDGET: {
|
||||
rows: 3,
|
||||
columns: 3,
|
||||
label: "Alpha - come in!",
|
||||
label: "Labels",
|
||||
options: [
|
||||
{ label: "Alpha", value: "1" },
|
||||
{ label: "Bravo", value: "2" },
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import {
|
|||
fetchEditorConfigs,
|
||||
} from "../../actions/configsActions";
|
||||
import { ReduxActionTypes } from "../../constants/ReduxActionConstants";
|
||||
import { updateWidgetProperty } from "../../actions/controlActions";
|
||||
|
||||
const EditorWrapper = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -59,6 +60,11 @@ type EditorProps = {
|
|||
fetchCanvasWidgets: Function;
|
||||
executeAction: (actionPayloads?: ActionPayload[]) => void;
|
||||
updateWidget: Function;
|
||||
updateWidgetProperty: (
|
||||
widgetId: string,
|
||||
propertyName: string,
|
||||
propertyValue: any,
|
||||
) => void;
|
||||
savePageLayout: Function;
|
||||
currentPageName: string;
|
||||
currentPageId: string;
|
||||
|
|
@ -92,6 +98,7 @@ class WidgetsEditor extends React.Component<EditorProps> {
|
|||
value={{
|
||||
executeAction: this.props.executeAction,
|
||||
updateWidget: this.props.updateWidget,
|
||||
updateWidgetProperty: this.props.updateWidgetProperty,
|
||||
}}
|
||||
>
|
||||
<EditorWrapper>
|
||||
|
|
@ -123,6 +130,11 @@ const mapStateToProps = (state: AppState) => {
|
|||
|
||||
const mapDispatchToProps = (dispatch: any) => {
|
||||
return {
|
||||
updateWidgetProperty: (
|
||||
widgetId: string,
|
||||
propertyName: string,
|
||||
propertyValue: any,
|
||||
) => dispatch(updateWidgetProperty(widgetId, propertyName, propertyValue)),
|
||||
executeAction: (actionPayloads?: ActionPayload[]) =>
|
||||
dispatch(executeAction(actionPayloads)),
|
||||
fetchCanvasWidgets: (pageId: string) =>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { ControlWrapper, StyledDropDown } from "./StyledControls";
|
|||
class DropDownControl extends BaseControl<DropDownControlProps> {
|
||||
constructor(props: DropDownControlProps) {
|
||||
super(props);
|
||||
this.onItemSelect = this.onItemSelect.bind(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
@ -21,6 +20,7 @@ class DropDownControl extends BaseControl<DropDownControlProps> {
|
|||
<StyledDropDown
|
||||
items={this.props.options}
|
||||
itemPredicate={this.filterOption}
|
||||
filterable={false}
|
||||
itemRenderer={this.renderItem}
|
||||
onItemSelect={this.onItemSelect}
|
||||
noResults={<MenuItem disabled={true} text="No results." />}
|
||||
|
|
@ -34,14 +34,14 @@ class DropDownControl extends BaseControl<DropDownControlProps> {
|
|||
);
|
||||
}
|
||||
|
||||
onItemSelect(
|
||||
onItemSelect = (
|
||||
item: DropdownOption,
|
||||
event?: SyntheticEvent<HTMLElement>,
|
||||
): void {
|
||||
): void => {
|
||||
this.updateProperty(this.props.propertyName, item.value);
|
||||
}
|
||||
};
|
||||
|
||||
renderItem(option: DropdownOption, itemProps: IItemRendererProps) {
|
||||
renderItem = (option: DropdownOption, itemProps: IItemRendererProps) => {
|
||||
if (!itemProps.modifiers.matchesPredicate) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -49,12 +49,11 @@ class DropDownControl extends BaseControl<DropDownControlProps> {
|
|||
<MenuItem
|
||||
active={itemProps.modifiers.active}
|
||||
key={option.value}
|
||||
label={option.label}
|
||||
onClick={itemProps.handleClick}
|
||||
text={option.label}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
filterOption(query: string, option: DropdownOption): boolean {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -10,13 +10,27 @@ class InputTextControl extends BaseControl<InputControlProps> {
|
|||
<ControlWrapper>
|
||||
<label>{this.props.label}</label>
|
||||
<StyledInputGroup
|
||||
type={this.isNumberType(this.props.inputType) ? "number" : "text"}
|
||||
onChange={this.onTextChange}
|
||||
placeholder={this.props.placeholderText}
|
||||
defaultValue={this.props.propertyValue}
|
||||
/>
|
||||
</ControlWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
isNumberType(inputType: InputType): boolean {
|
||||
switch (inputType) {
|
||||
case "CURRENCY":
|
||||
case "INTEGER":
|
||||
case "NUMBER":
|
||||
case "PHONE_NUMBER":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
onTextChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.updateProperty(this.props.propertyName, event.target.value);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ export const StyledSwitch = styled(Switch)`
|
|||
|
||||
export const StyledInputGroup = styled(InputGroup)`
|
||||
& > input {
|
||||
placeholderText: ${props => props.placeholder}
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
background: ${props => props.theme.colors.paneInputBG};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,12 +43,16 @@ export interface PropertyPaneConfigState {
|
|||
config: PropertyConfig;
|
||||
configVersion: number;
|
||||
}
|
||||
/**
|
||||
* Todo: Remove hardcoding of config response
|
||||
*/
|
||||
|
||||
const propertyPaneConfigReducer = createReducer(initialState, {
|
||||
[ReduxActionTypes.FETCH_PROPERTY_PANE_CONFIGS_SUCCESS]: (
|
||||
state: PropertyPaneConfigState,
|
||||
action: ReduxAction<PropertyPaneConfigState>,
|
||||
) => {
|
||||
return { ...PropertyPaneConfigResponse };
|
||||
return { ...action.payload };
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import RadioGroupWidget, {
|
|||
import WidgetFactory from "./WidgetFactory";
|
||||
import React from "react";
|
||||
import ButtonWidget, { ButtonWidgetProps } from "../widgets/ButtonWidget";
|
||||
import DropdownWidget, { DropdownWidgetProps } from "../widgets/DropdownWidget";
|
||||
|
||||
class WidgetBuilderRegistry {
|
||||
static registerWidgetBuilders() {
|
||||
|
|
@ -51,6 +52,12 @@ class WidgetBuilderRegistry {
|
|||
},
|
||||
});
|
||||
|
||||
WidgetFactory.registerWidgetBuilder("DROP_DOWN_WIDGET", {
|
||||
buildWidget(widgetData: DropdownWidgetProps): JSX.Element {
|
||||
return <DropdownWidget {...widgetData} />;
|
||||
},
|
||||
});
|
||||
|
||||
WidgetFactory.registerWidgetBuilder("RADIO_GROUP_WIDGET", {
|
||||
buildWidget(widgetData: RadioGroupWidgetProps): JSX.Element {
|
||||
return <RadioGroupWidget {...widgetData} />;
|
||||
|
|
|
|||
|
|
@ -173,6 +173,11 @@ export interface WidgetDataProps {
|
|||
export interface WidgetFunctions {
|
||||
executeAction?: (actionPayloads?: ActionPayload[]) => void;
|
||||
updateWidget?: Function;
|
||||
updateWidgetProperty?: (
|
||||
widgetId: string,
|
||||
propertyName: string,
|
||||
propertyValue: any,
|
||||
) => void;
|
||||
}
|
||||
|
||||
export interface WidgetCardProps {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import React from "react";
|
||||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import { WidgetType } from "../constants/WidgetConstants";
|
||||
import ButtonComponent, {
|
||||
ButtonStyleName,
|
||||
} from "../components/blueprint/ButtonComponent";
|
||||
import ButtonComponent from "../components/blueprint/ButtonComponent";
|
||||
import { ActionPayload } from "../constants/ActionConstants";
|
||||
|
||||
class ButtonWidget extends BaseWidget<ButtonWidgetProps, WidgetState> {
|
||||
|
|
@ -19,14 +17,10 @@ class ButtonWidget extends BaseWidget<ButtonWidgetProps, WidgetState> {
|
|||
}
|
||||
|
||||
getPageView() {
|
||||
// TODO(abhinav): This is a hack. Need to standardize the style names
|
||||
const translatedButtonStyleName: ButtonStyleName | undefined =
|
||||
this.props.buttonStyle &&
|
||||
(this.props.buttonStyle.split("_")[0].toLowerCase() as ButtonStyleName);
|
||||
return (
|
||||
<ButtonComponent
|
||||
style={this.getPositionStyle()}
|
||||
styleName={translatedButtonStyleName}
|
||||
buttonStyle={this.props.buttonStyle}
|
||||
widgetId={this.props.widgetId}
|
||||
widgetName={this.props.widgetName}
|
||||
key={this.props.widgetId}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,73 @@ import React from "react";
|
|||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import { WidgetType } from "../constants/WidgetConstants";
|
||||
import { ActionPayload } from "../constants/ActionConstants";
|
||||
import DropDownComponent from "../components/blueprint/DropdownComponent";
|
||||
import _ from "lodash";
|
||||
|
||||
class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
|
||||
getPageView() {
|
||||
return <div />;
|
||||
constructor(props: DropdownWidgetProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
getPageView() {
|
||||
return (
|
||||
<DropDownComponent
|
||||
onOptionSelected={this.onOptionSelected}
|
||||
onOptionRemoved={this.onOptionRemoved}
|
||||
widgetId={this.props.widgetId}
|
||||
style={this.getPositionStyle()}
|
||||
placeholder={this.props.placeholderText}
|
||||
options={this.props.options || []}
|
||||
selectionType={this.props.selectionType}
|
||||
selectedIndex={this.props.selectedIndex || 0}
|
||||
selectedIndexArr={this.props.selectedIndexArr || []}
|
||||
label={this.props.label}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
onOptionSelected = (selectedOption: DropdownOption) => {
|
||||
const selectedIndex = _.findIndex(this.props.options, option => {
|
||||
return option.value === selectedOption.value;
|
||||
});
|
||||
if (this.props.selectionType === "SINGLE_SELECT") {
|
||||
this.context.updateWidgetProperty(
|
||||
this.props.widgetId,
|
||||
"selectedIndex",
|
||||
selectedIndex,
|
||||
);
|
||||
} else if (this.props.selectionType === "MULTI_SELECT") {
|
||||
const selectedIndexArr = this.props.selectedIndexArr || [];
|
||||
const isAlreadySelected =
|
||||
_.find(selectedIndexArr, index => {
|
||||
return index === selectedIndex;
|
||||
}) !== undefined;
|
||||
if (isAlreadySelected) {
|
||||
this.onOptionRemoved(selectedIndex);
|
||||
} else {
|
||||
selectedIndexArr.push(selectedIndex);
|
||||
this.context.updateWidgetProperty(
|
||||
this.props.widgetId,
|
||||
"selectedIndexArr",
|
||||
selectedIndexArr,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onOptionRemoved = (removedIndex: number) => {
|
||||
const updateIndexArr = this.props.selectedIndexArr
|
||||
? this.props.selectedIndexArr.filter(index => {
|
||||
return removedIndex !== index;
|
||||
})
|
||||
: [];
|
||||
this.context.updateWidgetProperty(
|
||||
this.props.widgetId,
|
||||
"selectedIndexArr",
|
||||
updateIndexArr,
|
||||
);
|
||||
};
|
||||
|
||||
getWidgetType(): WidgetType {
|
||||
return "DROP_DOWN_WIDGET";
|
||||
}
|
||||
|
|
@ -22,6 +83,8 @@ export interface DropdownOption {
|
|||
export interface DropdownWidgetProps extends WidgetProps {
|
||||
placeholderText?: string;
|
||||
label?: string;
|
||||
selectedIndex?: number;
|
||||
selectedIndexArr?: number[];
|
||||
selectionType: SelectionType;
|
||||
options?: DropdownOption[];
|
||||
onOptionSelected?: ActionPayload[];
|
||||
|
|
|
|||
|
|
@ -4,13 +4,55 @@ import { WidgetType } from "../constants/WidgetConstants";
|
|||
import InputComponent from "../components/blueprint/InputComponent";
|
||||
|
||||
class InputWidget extends BaseWidget<InputWidgetProps, WidgetState> {
|
||||
regex = new RegExp("");
|
||||
|
||||
constructor(props: InputWidgetProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
if (this.props.regex) {
|
||||
try {
|
||||
this.regex = new RegExp(this.props.regex);
|
||||
} catch (e) {
|
||||
console.log("invalid regex");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: InputWidgetProps) {
|
||||
super.componentDidUpdate(prevProps);
|
||||
if (this.props.regex !== prevProps.regex && this.props.regex) {
|
||||
try {
|
||||
this.regex = new RegExp(this.props.regex);
|
||||
} catch (e) {
|
||||
console.log("invalid regex");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onValueChange = (value: string) => {
|
||||
this.context.updateWidgetProperty(this.props.widgetId, "text", value);
|
||||
};
|
||||
|
||||
getPageView() {
|
||||
return (
|
||||
<InputComponent
|
||||
onValueChange={this.onValueChange}
|
||||
style={this.getPositionStyle()}
|
||||
widgetId={this.props.widgetId}
|
||||
errorMessage={
|
||||
this.props.regex &&
|
||||
this.props.text &&
|
||||
!this.regex.test(this.props.text)
|
||||
? this.props.errorMessage
|
||||
: undefined
|
||||
}
|
||||
inputType={this.props.inputType}
|
||||
disabled={this.props.isDisabled}
|
||||
maxChars={this.props.maxChars}
|
||||
label={this.props.label}
|
||||
defaultValue={this.props.defaultText}
|
||||
maxNum={this.props.maxNum}
|
||||
minNum={this.props.minNum}
|
||||
|
|
@ -43,6 +85,9 @@ export interface InputWidgetProps extends WidgetProps {
|
|||
inputType: InputType;
|
||||
defaultText?: string;
|
||||
isDisabled?: boolean;
|
||||
text?: string;
|
||||
regex?: string;
|
||||
errorMessage?: string;
|
||||
placeholderText?: string;
|
||||
maxChars?: number;
|
||||
minNum?: number;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import React from "react";
|
||||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import { WidgetType } from "../constants/WidgetConstants";
|
||||
import TextViewComponent from "../components/appsmith/TextViewComponent";
|
||||
import TextComponent from "../components/blueprint/TextComponent";
|
||||
|
||||
class TextWidget extends BaseWidget<TextWidgetProps, WidgetState> {
|
||||
getPageView() {
|
||||
return (
|
||||
<TextViewComponent
|
||||
<TextComponent
|
||||
style={this.getPositionStyle()}
|
||||
widgetId={this.props.widgetId}
|
||||
key={this.props.widgetId}
|
||||
textStyle={this.props.textStyle}
|
||||
text={this.props.text}
|
||||
/>
|
||||
);
|
||||
|
|
@ -24,7 +25,7 @@ export type TextStyle = "BODY" | "HEADING" | "LABEL" | "SUB_TEXT";
|
|||
|
||||
export interface TextWidgetProps extends WidgetProps {
|
||||
text?: string;
|
||||
textStyle?: TextStyle;
|
||||
textStyle: TextStyle;
|
||||
}
|
||||
|
||||
export default TextWidget;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user