lightning menu moved to dynamic auto complete

This commit is contained in:
vicky_primathon.in 2020-05-20 21:14:52 +05:30
parent acd18bafd5
commit 69e2de1d38
8 changed files with 190 additions and 167 deletions

View File

@ -22,6 +22,7 @@ const buttonStyles = css<{
filled?: string;
fluid?: boolean;
themeType?: string;
iconAlignment?: Direction;
}>`
${BlueprintButtonIntentsCSS}
&&&& {
@ -34,18 +35,24 @@ const buttonStyles = css<{
props.filled || props.outline ? "inherit" : "transparent"};
width: ${props => (props.fluid ? "100%" : "auto")};
color: ${props =>
props.themeType === "dark"
? props.theme.colors.textOnDarkBG
: props.theme.colors.textDefault};
}
&&&&&& {
&.bp3-button span {
font-weight: ${props => (props.themeType === "dark" ? 400 : 700)};
font-weight: ${props => (props.themeType ? 400 : 700)};
}
.bp3-icon svg {
width: ${props => (props.themeType === "dark" ? 14 : 16)}px;
height: ${props => (props.themeType === "dark" ? 14 : 16)}px;
width: ${props => (props.themeType ? 14 : 16)}px;
height: ${props => (props.themeType ? 14 : 16)}px;
}
&.bp3-button {
${props =>
props.themeType !== undefined
? props.iconAlignment === "right"
? "display: flex; justify-content: space-between;width: 100%;"
: props.iconAlignment === "left"
? "display: flex; justify-content: flex-start;width: 100%;"
: ""
: ""};
}
}
${props => (props.outline ? outline : "")}
@ -55,6 +62,7 @@ const StyledButton = styled(BlueprintButton)<{
intent?: Intent;
filled?: string;
themeType?: string;
iconAlignment?: Direction;
}>`
${buttonStyles}
`;
@ -63,6 +71,7 @@ const StyledAnchorButton = styled(BlueprintAnchorButton)<{
intent?: Intent;
filled?: string;
themeType?: string;
iconAlignment?: Direction;
}>`
${buttonStyles}
`;
@ -111,6 +120,7 @@ export const Button = (props: ButtonProps) => {
className: props.className,
fluid: props.fluid ? props.fluid.toString() : undefined,
themeType: props.themeType ? props.themeType : undefined,
iconAlignment: props.iconAlignment ? props.iconAlignment : undefined,
};
if (props.href) {
return (

View File

@ -1,4 +1,4 @@
import React, { Component } from "react";
import React, { Component, lazy, Suspense } from "react";
import { connect } from "react-redux";
import { AppState } from "reducers";
import styled, { createGlobalStyle } from "styled-components";
@ -21,6 +21,9 @@ import { parseDynamicString } from "utils/DynamicBindingUtils";
import { DataTree } from "entities/DataTree/dataTreeFactory";
import { Theme } from "constants/DefaultTheme";
import AnalyticsUtil from "utils/AnalyticsUtil";
const LightningMenu = lazy(() =>
import("components/editorComponents/LightningMenu"),
);
require("codemirror/mode/javascript/javascript");
require("codemirror/mode/sql/sql");
require("codemirror/addon/hint/sql-hint");
@ -233,6 +236,18 @@ const IconContainer = styled.div`
}
`;
const DynamicAutocompleteInputWrapper = styled.div`
width: 100%;
position: relative;
& > span:first-of-type {
position: absolute;
right: 0;
top: 2px;
width: 14px;
z-index: 10;
}
`;
const THEMES = {
LIGHT: "LIGHT",
DARK: "DARK",
@ -264,7 +279,7 @@ export type DynamicAutocompleteInputProps = {
link?: string;
baseMode?: string | object;
setMaxHeight?: boolean;
defaultValue?: string;
showLightningMenu?: boolean;
};
type Props = ReduxStateProps &
@ -292,6 +307,7 @@ class DynamicAutocompleteInput extends Component<Props, State> {
componentDidMount(): void {
if (this.textArea.current) {
const options: EditorConfiguration = {};
//use this for lightning menu theme
if (this.props.theme === "DARK") options.theme = "monokai";
if (!this.props.input.onChange || this.props.disabled) {
options.readOnly = true;
@ -366,19 +382,6 @@ class DynamicAutocompleteInput extends Component<Props, State> {
}
}
}
if (prevProps.defaultValue !== this.props.defaultValue) {
const cursorPosition = this.props.defaultValue
? this.props.defaultValue.length + 1
: 1;
this.editor.setValue(
this.props.defaultValue ? this.props.defaultValue : "",
);
this.editor.focus();
this.editor.setCursor({
line: 1,
ch: cursorPosition,
});
}
}
handleEditorFocus = () => {
@ -479,6 +482,15 @@ class DynamicAutocompleteInput extends Component<Props, State> {
}
};
updatePropertyValue = (value: string, cursor: number) => {
this.editor.setValue(value);
this.editor.focus();
this.editor.setCursor({
line: 1,
ch: cursor,
});
};
render() {
const {
input,
@ -488,6 +500,7 @@ class DynamicAutocompleteInput extends Component<Props, State> {
disabled,
className,
setMaxHeight,
showLightningMenu,
} = this.props;
const hasError = !!(meta && meta.error);
let showError = false;
@ -495,57 +508,65 @@ class DynamicAutocompleteInput extends Component<Props, State> {
showError =
hasError && this.state.isFocused && !this.state.autoCompleteVisible;
}
console.log(className);
const themeType = this.props.theme === "DARK" ? "dark" : "light";
return (
<ErrorTooltip message={meta ? meta.error : ""} isOpen={showError}>
<Wrapper
editorTheme={theme}
hasError={hasError}
singleLine={singleLine}
isFocused={this.state.isFocused}
disabled={disabled}
className={className}
setMaxHeight={setMaxHeight}
>
<HintStyles />
<IconContainer>
{this.props.leftIcon && <this.props.leftIcon />}
</IconContainer>
{this.props.leftImage && (
<img
src={this.props.leftImage}
alt="img"
className="leftImageStyles"
/>
)}
<textarea
ref={this.textArea}
{..._.omit(this.props.input, ["onChange", "value"])}
defaultValue={input.value}
placeholder={this.props.placeholder}
<DynamicAutocompleteInputWrapper>
{(showLightningMenu === undefined || showLightningMenu === true) && (
<LightningMenu
themeType={themeType}
updatePropertyValue={this.updatePropertyValue}
/>
{this.props.link && (
<React.Fragment>
<a
href={this.props.link}
target="_blank"
className="linkStyles"
rel="noopener noreferrer"
>
API documentation
</a>
</React.Fragment>
)}
{this.props.rightIcon && (
<HelperTooltip
description={this.props.description}
rightIcon={this.props.rightIcon}
)}
<ErrorTooltip message={meta ? meta.error : ""} isOpen={showError}>
<Wrapper
editorTheme={theme}
hasError={hasError}
singleLine={singleLine}
isFocused={this.state.isFocused}
disabled={disabled}
className={className}
setMaxHeight={setMaxHeight}
>
<HintStyles />
<IconContainer>
{this.props.leftIcon && <this.props.leftIcon />}
</IconContainer>
{this.props.leftImage && (
<img
src={this.props.leftImage}
alt="img"
className="leftImageStyles"
/>
)}
<textarea
ref={this.textArea}
{..._.omit(this.props.input, ["onChange", "value"])}
defaultValue={input.value}
placeholder={this.props.placeholder}
/>
)}
</Wrapper>
</ErrorTooltip>
{this.props.link && (
<React.Fragment>
<a
href={this.props.link}
target="_blank"
className="linkStyles"
rel="noopener noreferrer"
>
API documentation
</a>
</React.Fragment>
)}
{this.props.rightIcon && (
<HelperTooltip
description={this.props.description}
rightIcon={this.props.rightIcon}
/>
)}
</Wrapper>
</ErrorTooltip>
</DynamicAutocompleteInputWrapper>
);
}
}

View File

@ -10,7 +10,11 @@ import { RestAction } from "api/ActionAPI";
import { WidgetProps } from "widgets/BaseWidget";
import { noop } from "lodash";
const getApiOptions = (apis: RestAction[]) => ({
const getApiOptions = (
themeType: string,
apis: RestAction[],
updatePropertyValue: (value: string, cursor: number) => void,
) => ({
sections: [
{
isSticky: true,
@ -21,7 +25,7 @@ const getApiOptions = (apis: RestAction[]) => ({
text="Create new API"
icon="plus"
iconAlignment="left"
themeType="dark"
themeType={themeType}
/>
),
},
@ -30,7 +34,10 @@ const getApiOptions = (apis: RestAction[]) => ({
{
options: apis.map(api => ({
content: api.name,
onSelect: noop,
onSelect: () => {
const value = `{{${api.name}.data}}`;
updatePropertyValue(value, value.length + 1);
},
})),
},
],
@ -39,10 +46,14 @@ const getApiOptions = (apis: RestAction[]) => ({
},
openDirection: Directions.RIGHT,
openOnHover: false,
themeType: "dark",
themeType: themeType,
});
const getQueryOptions = (queries: RestAction[]) => ({
const getQueryOptions = (
themeType: string,
queries: RestAction[],
updatePropertyValue: (value: string, cursor: number) => void,
) => ({
sections: [
{
isSticky: true,
@ -53,7 +64,7 @@ const getQueryOptions = (queries: RestAction[]) => ({
text="Create new Query"
icon="plus"
iconAlignment="left"
themeType="dark"
themeType={themeType}
/>
),
},
@ -62,7 +73,10 @@ const getQueryOptions = (queries: RestAction[]) => ({
{
options: queries.map(query => ({
content: query.name,
onSelect: noop,
onSelect: () => {
const value = `{{${query.name}.data}}`;
updatePropertyValue(value, value.length + 1);
},
})),
},
],
@ -71,33 +85,21 @@ const getQueryOptions = (queries: RestAction[]) => ({
},
openDirection: Directions.RIGHT,
openOnHover: false,
themeType: "dark",
themeType: themeType,
});
const getWidgetOptions = (
themeType: string,
widgets: WidgetProps[],
updatePropertyValue: (value: string) => void,
updatePropertyValue: (value: string, cursor: number) => void,
) => ({
sections: [
// {
// isSticky: true,
// options: [
// {
// content: (
// <Button
// text="Create new widget"
// icon="plus"
// iconAlignment="left"
// themeType="dark"
// />
// ),
// },
// ],
// },
{
options: widgets.map(widget => ({
content: (
<CustomizedDropdown {...getWidgetData(widget, updatePropertyValue)} />
<CustomizedDropdown
{...getWidgetData(themeType, widget, updatePropertyValue)}
/>
),
disabled: false,
shouldCloseDropdown: false,
@ -109,19 +111,21 @@ const getWidgetOptions = (
},
openDirection: Directions.RIGHT,
openOnHover: false,
themeType: "dark",
themeType: themeType,
});
const getWidgetData = (
themeType: string,
widget: WidgetProps,
updatePropertyValue: (value: string) => void,
updatePropertyValue: (value: string, cursor: number) => void,
) => ({
sections: [
{
options: Object.keys(widget).map(widgetProp => ({
content: widgetProp,
onSelect: () => {
updatePropertyValue(`{{${widget.widgetName}.${widgetProp}}}`);
const value = `{{${widget.widgetName}.${widgetProp}}}`;
updatePropertyValue(value, value.length + 1);
},
})),
},
@ -131,14 +135,15 @@ const getWidgetData = (
},
openDirection: Directions.RIGHT,
openOnHover: false,
themeType: "dark",
themeType: themeType,
});
const lightningMenuOptions = (
themeType: string,
apis: RestAction[],
queries: RestAction[],
widgets: WidgetProps[],
updatePropertyValue: (value: string) => void,
updatePropertyValue: (value: string, cursor: number) => void,
): CustomizedDropdownProps => ({
sections: [
{
@ -148,23 +153,31 @@ const lightningMenuOptions = (
disabled: false,
shouldCloseDropdown: true,
onSelect: () => {
updatePropertyValue("Plain Text");
updatePropertyValue("", 1);
},
},
{
content: <CustomizedDropdown {...getApiOptions(apis)} />,
disabled: false,
shouldCloseDropdown: false,
},
{
content: <CustomizedDropdown {...getQueryOptions(queries)} />,
content: (
<CustomizedDropdown
{...getApiOptions(themeType, apis, updatePropertyValue)}
/>
),
disabled: false,
shouldCloseDropdown: false,
},
{
content: (
<CustomizedDropdown
{...getWidgetOptions(widgets, updatePropertyValue)}
{...getQueryOptions(themeType, queries, updatePropertyValue)}
/>
),
disabled: false,
shouldCloseDropdown: false,
},
{
content: (
<CustomizedDropdown
{...getWidgetOptions(themeType, widgets, updatePropertyValue)}
/>
),
disabled: false,
@ -175,7 +188,7 @@ const lightningMenuOptions = (
disabled: false,
shouldCloseDropdown: true,
onSelect: () => {
updatePropertyValue("{{}}");
updatePropertyValue("{{}}", 3);
},
},
{
@ -183,7 +196,7 @@ const lightningMenuOptions = (
disabled: false,
shouldCloseDropdown: true,
onSelect: () => {
updatePropertyValue("<p></p>");
updatePropertyValue("<p></p>", 4);
},
},
],
@ -194,12 +207,13 @@ const lightningMenuOptions = (
trigger: {
text: "",
},
themeType: "dark",
themeType: themeType,
});
type LightningMenuProps = {
onSelect?: (value: string) => void;
updatePropertyValue: (value: string) => void;
updatePropertyValue: (value: string, cursor: number) => void;
themeType: string;
};
export const LightningMenu = (props: LightningMenuProps) => {
@ -213,16 +227,14 @@ export const LightningMenu = (props: LightningMenuProps) => {
const canvasWidgets = [];
for (const i in state.entities.canvasWidgets) {
if (
!["CONTAINER_WIDGET", "CANVAS_WIDGET"].includes(
state.entities.canvasWidgets[i].type,
)
!state.entities.canvasWidgets[i].children ||
state.entities.canvasWidgets[i].children?.length === 0
) {
canvasWidgets.push(state.entities.canvasWidgets[i]);
}
}
return canvasWidgets;
});
console.log("widgets", widgets);
const apis = actions
.filter(action => action.config.pluginType === "API")
.map(action => action.config);
@ -233,6 +245,7 @@ export const LightningMenu = (props: LightningMenuProps) => {
return (
<CustomizedDropdown
{...lightningMenuOptions(
props.themeType,
apis,
queries,
widgets,

View File

@ -42,6 +42,7 @@ const KeyValueRow = (props: Props & WrappedFieldArrayProps) => {
placeholder="Key"
singleLine
setMaxHeight
showLightningMenu={false}
/>
{!props.actionConfig && (
<DynamicTextField

View File

@ -1,24 +1,8 @@
import React, { lazy, Suspense } from "react";
import styled from "styled-components";
import React from "react";
import BaseControl, { ControlProps } from "./BaseControl";
import { StyledDynamicInput } from "./StyledControls";
import { InputType } from "widgets/InputWidget";
import DynamicAutocompleteInput from "components/editorComponents/DynamicAutocompleteInput";
import CodeMirror from "codemirror";
const LightningMenu = lazy(() =>
import("components/editorComponents/LightningMenu"),
);
const InputControlWrapper = styled.div`
width: 100%;
position: relative;
& > span:first-of-type {
position: absolute;
right: 0;
top: 2px;
width: 14px;
z-index: 10;
}
`;
export function InputText(props: {
label: string;
@ -27,16 +11,8 @@ export function InputText(props: {
isValid: boolean;
validationMessage?: string;
placeholder?: string;
defaultValue?: string;
}) {
const {
validationMessage,
value,
isValid,
onChange,
placeholder,
defaultValue,
} = props;
const { validationMessage, value, isValid, onChange, placeholder } = props;
return (
<StyledDynamicInput>
<DynamicAutocompleteInput
@ -51,20 +27,12 @@ export function InputText(props: {
theme={"DARK"}
singleLine={false}
placeholder={placeholder}
defaultValue={defaultValue}
/>
</StyledDynamicInput>
);
}
class InputTextControl extends BaseControl<InputControlProps> {
state = {
defaultValue: "",
};
updatePropertyValue = (value: string) => {
this.setState({ defaultValue: value });
};
render() {
const {
validationMessage,
@ -74,20 +42,14 @@ class InputTextControl extends BaseControl<InputControlProps> {
placeholderText,
} = this.props;
return (
<InputControlWrapper>
<Suspense fallback={<div />}>
<LightningMenu updatePropertyValue={this.updatePropertyValue} />
</Suspense>
<InputText
label={label}
value={propertyValue}
onChange={this.onTextChange}
isValid={isValid}
validationMessage={validationMessage}
placeholder={placeholderText}
defaultValue={this.state.defaultValue}
/>
</InputControlWrapper>
<InputText
label={label}
value={propertyValue}
onChange={this.onTextChange}
isValid={isValid}
validationMessage={validationMessage}
placeholder={placeholderText}
/>
);
}

View File

@ -80,6 +80,7 @@ export const EditorHeader = (props: EditorHeaderProps) => {
text="Manage Pages"
icon="page-layout"
iconAlignment="left"
themeType="light"
/>
),
onSelect: () =>

View File

@ -1,7 +1,7 @@
import styled, { css } from "styled-components";
import { Intent, IntentColors } from "constants/DefaultTheme";
export const DropdownTrigger = styled.div`
export const DropdownTrigger = styled.div<{ themeType: string }>`
display: flex;
flex-direction: row;
justify-content: space-between;
@ -17,8 +17,13 @@ export const DropdownTrigger = styled.div`
justify-content: space-between;
outline: 0;
span {
color: inherit;
font-weight: 400;
color: ${props =>
props.themeType === "dark"
? props.theme.colors.textOnDarkBG
: props.themeType === "light"
? props.theme.colors.defaultText
: "initial"};
}
&:hover {
background: inherit;
@ -96,4 +101,14 @@ export const Option = styled.div<{
${props => (!props.disabled ? highlightOption : ``)};
}
${props => (props.active && !props.disabled ? highlightOption : ``)};
&&& button {
span {
color: ${props =>
props.themeType === "dark"
? props.theme.colors.textOnDarkBG
: props.themeType === "light"
? props.theme.colors.defaultText
: "initial"};
}
}
`;

View File

@ -145,7 +145,7 @@ export const CustomizedDropdown = (
minimal
enforceFocus={false}
>
<DropdownTrigger>{trigger}</DropdownTrigger>
<DropdownTrigger themeType={themeType}>{trigger}</DropdownTrigger>
<DropdownContent themeType={themeType}>{content}</DropdownContent>
</Popover>
);