Property Pane Controls
- Fixes #121, #122, #123, #124, #90, #46, #65, #100, #101, #68, #102
This commit is contained in:
parent
b27dbaa470
commit
99ce65c756
|
|
@ -13,9 +13,7 @@
|
||||||
"@blueprintjs/select": "^3.10.0",
|
"@blueprintjs/select": "^3.10.0",
|
||||||
"@blueprintjs/table": "^3.7.1",
|
"@blueprintjs/table": "^3.7.1",
|
||||||
"@sentry/browser": "^5.6.3",
|
"@sentry/browser": "^5.6.3",
|
||||||
"@types/axios": "^0.14.0",
|
|
||||||
"@types/fontfaceobserver": "^0.0.6",
|
"@types/fontfaceobserver": "^0.0.6",
|
||||||
"@types/jest": "^24.0.18",
|
|
||||||
"@types/lodash": "^4.14.120",
|
"@types/lodash": "^4.14.120",
|
||||||
"@types/moment-timezone": "^0.5.10",
|
"@types/moment-timezone": "^0.5.10",
|
||||||
"@types/nanoid": "^2.0.0",
|
"@types/nanoid": "^2.0.0",
|
||||||
|
|
@ -71,7 +69,7 @@
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"flow": "flow"
|
"flow": "flow"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolution": {
|
||||||
"jest": "24.8.0"
|
"jest": "24.8.0"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Button, IButtonProps, MaybeElement } from "@blueprintjs/core";
|
import { AnchorButton, IButtonProps, MaybeElement } from "@blueprintjs/core";
|
||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
import { Container } from "../../editorComponents/ContainerComponent";
|
import { Container } from "../../editorComponents/ContainerComponent";
|
||||||
import { TextComponentProps } from "./TextViewComponent";
|
import { TextComponentProps } from "./TextViewComponent";
|
||||||
|
|
@ -15,7 +15,7 @@ const ButtonColorStyles = css<ButtonStyleProps>`
|
||||||
}};
|
}};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ButtonWrapper = styled(Button)<ButtonStyleProps>`
|
const ButtonWrapper = styled(AnchorButton)<ButtonStyleProps>`
|
||||||
&& {
|
&& {
|
||||||
${ButtonColorStyles};
|
${ButtonColorStyles};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -64,9 +64,10 @@ const ButtonWrapper = styled(Button)<ButtonStyleProps>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
export type ButtonStyleName = "primary" | "secondary" | "error";
|
||||||
|
|
||||||
type ButtonStyleProps = {
|
type ButtonStyleProps = {
|
||||||
styleName?: "primary" | "secondary" | "error";
|
styleName?: ButtonStyleName;
|
||||||
filled?: boolean;
|
filled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -77,6 +78,7 @@ export const BaseButton = (props: IButtonProps & ButtonStyleProps) => {
|
||||||
|
|
||||||
BaseButton.defaultProps = {
|
BaseButton.defaultProps = {
|
||||||
styleName: "secondary",
|
styleName: "secondary",
|
||||||
|
disabled: false,
|
||||||
text: "Button Text",
|
text: "Button Text",
|
||||||
minimal: true,
|
minimal: true,
|
||||||
};
|
};
|
||||||
|
|
@ -84,13 +86,20 @@ BaseButton.defaultProps = {
|
||||||
interface ButtonContainerProps extends TextComponentProps {
|
interface ButtonContainerProps extends TextComponentProps {
|
||||||
icon?: MaybeElement;
|
icon?: MaybeElement;
|
||||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// To be used with the canvas
|
// To be used with the canvas
|
||||||
const ButtonContainer = (props: ButtonContainerProps) => {
|
const ButtonContainer = (props: ButtonContainerProps & ButtonStyleProps) => {
|
||||||
return (
|
return (
|
||||||
<Container {...props}>
|
<Container {...props}>
|
||||||
<BaseButton icon={props.icon} text={props.text} onClick={props.onClick} />
|
<BaseButton
|
||||||
|
styleName={props.styleName}
|
||||||
|
icon={props.icon}
|
||||||
|
text={props.text}
|
||||||
|
onClick={props.onClick}
|
||||||
|
disabled={props.disabled}
|
||||||
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ export const Colors: Record<string, string> = {
|
||||||
CONCRETE: "#F3F3F3",
|
CONCRETE: "#F3F3F3",
|
||||||
MYSTIC: "#E1E8ED",
|
MYSTIC: "#E1E8ED",
|
||||||
AQUA_HAZE: "#EEF2F5",
|
AQUA_HAZE: "#EEF2F5",
|
||||||
|
GRAY_CHATEAU: "#A2A6A8",
|
||||||
|
|
||||||
BLACK: "#000000",
|
BLACK: "#000000",
|
||||||
BLACK_PEARL: "#040627",
|
BLACK_PEARL: "#040627",
|
||||||
|
|
@ -27,6 +28,7 @@ export const Colors: Record<string, string> = {
|
||||||
PURPLE: "#6871EF",
|
PURPLE: "#6871EF",
|
||||||
OXFORD_BLUE: "#2E3D49",
|
OXFORD_BLUE: "#2E3D49",
|
||||||
FRENCH_PASS: "#BBE8FE",
|
FRENCH_PASS: "#BBE8FE",
|
||||||
|
CADET_BLUE: "#A3B3BF",
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Color = (typeof Colors)[keyof typeof Colors];
|
export type Color = (typeof Colors)[keyof typeof Colors];
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,10 @@ export const theme: Theme = {
|
||||||
textAnchor: Colors.PURPLE,
|
textAnchor: Colors.PURPLE,
|
||||||
border: Colors.GEYSER,
|
border: Colors.GEYSER,
|
||||||
paneCard: Colors.SHARK,
|
paneCard: Colors.SHARK,
|
||||||
|
paneInputBG: Colors.SHARK,
|
||||||
paneBG: Colors.OUTER_SPACE,
|
paneBG: Colors.OUTER_SPACE,
|
||||||
|
paneText: Colors.GRAY_CHATEAU,
|
||||||
|
paneSectionLabel: Colors.CADET_BLUE,
|
||||||
navBG: Colors.DEEP_SPACE,
|
navBG: Colors.DEEP_SPACE,
|
||||||
grid: Colors.GEYSER,
|
grid: Colors.GEYSER,
|
||||||
containerBorder: Colors.FRENCH_PASS,
|
containerBorder: Colors.FRENCH_PASS,
|
||||||
|
|
@ -101,6 +104,11 @@ export const theme: Theme = {
|
||||||
style: "solid",
|
style: "solid",
|
||||||
color: Colors.FRENCH_PASS,
|
color: Colors.FRENCH_PASS,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
thickness: "1px",
|
||||||
|
style: "solid",
|
||||||
|
color: Colors.GEYSER_LIGHT,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
sidebarWidth: "300px",
|
sidebarWidth: "300px",
|
||||||
headerHeight: "50px",
|
headerHeight: "50px",
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ export const ReduxActionErrorTypes: { [key: string]: string } = {
|
||||||
FETCH_CONFIGS_ERROR: "FETCH_CONFIGS_ERROR",
|
FETCH_CONFIGS_ERROR: "FETCH_CONFIGS_ERROR",
|
||||||
PROPERTY_PANE_ERROR: "PROPERTY_PANE_ERROR",
|
PROPERTY_PANE_ERROR: "PROPERTY_PANE_ERROR",
|
||||||
FETCH_ACTIONS_ERROR: "FETCH_ACTIONS_ERROR",
|
FETCH_ACTIONS_ERROR: "FETCH_ACTIONS_ERROR",
|
||||||
|
UPDATE_WIDGET_PROPERTY_ERROR: "UPDATE_WIDGET_PROPERTY_ERROR",
|
||||||
FETCH_RESOURCES_ERROR: "FETCH_RESOURCES_ERROR",
|
FETCH_RESOURCES_ERROR: "FETCH_RESOURCES_ERROR",
|
||||||
CREATE_RESOURCE_ERROR: "CREATE_RESOURCE_ERROR",
|
CREATE_RESOURCE_ERROR: "CREATE_RESOURCE_ERROR",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ export interface BaseStyle {
|
||||||
heightUnit?: CSSUnit;
|
heightUnit?: CSSUnit;
|
||||||
widthUnit?: CSSUnit;
|
widthUnit?: CSSUnit;
|
||||||
backgroundColor?: Color;
|
backgroundColor?: Color;
|
||||||
|
border?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ComponentProps {
|
export interface ComponentProps {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,21 @@
|
||||||
import { ComponentProps } from "./BaseComponent";
|
import { ComponentProps } from "./BaseComponent";
|
||||||
import { ContainerOrientation } from "../constants/WidgetConstants";
|
import { ContainerOrientation } from "../constants/WidgetConstants";
|
||||||
import styled from "../constants/DefaultTheme";
|
import styled from "../constants/DefaultTheme";
|
||||||
import React, { createContext, Context, useRef } from "react";
|
import React, {
|
||||||
|
createContext,
|
||||||
|
Context,
|
||||||
|
useRef,
|
||||||
|
useContext,
|
||||||
|
forwardRef,
|
||||||
|
} from "react";
|
||||||
|
import { FocusContext } from "../pages/Editor/Canvas";
|
||||||
|
import { getBorderCSSShorthand } from "../constants/DefaultTheme";
|
||||||
|
|
||||||
export const Container = styled("div")<ContainerProps>`
|
type StyledContainerProps = ContainerProps & {
|
||||||
|
focus?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StyledContainer = styled("div")<StyledContainerProps>`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: ${props => {
|
flex-direction: ${props => {
|
||||||
return props.orientation === "HORIZONTAL" ? "row" : "column";
|
return props.orientation === "HORIZONTAL" ? "row" : "column";
|
||||||
|
|
@ -19,26 +31,49 @@ export const Container = styled("div")<ContainerProps>`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: ${props => props.theme.spaces[1]}px;
|
padding: ${props => props.theme.spaces[1]}px;
|
||||||
&:after {
|
&:after {
|
||||||
content: "${props => props.widgetName}";
|
content: "${props => (props.focus ? props.widgetName : "")}";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -${props => props.theme.spaces[8]}px;
|
top: -${props => props.theme.spaces[8]}px;
|
||||||
font-size: ${props => props.theme.fontSizes[2]}px;
|
font-size: ${props => props.theme.fontSizes[2]}px;
|
||||||
color: ${props => props.theme.colors.containerBorder};
|
color: ${props => props.theme.colors.containerBorder};
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}`;
|
||||||
`;
|
|
||||||
|
/* eslint-disable react/display-name */
|
||||||
|
/* eslint-disable react/prop-types */
|
||||||
|
type Ref = HTMLDivElement;
|
||||||
|
export const Container = forwardRef<Ref, ContainerProps>((props, ref) => {
|
||||||
|
const { isFocused } = useContext(FocusContext);
|
||||||
|
const focus = isFocused === props.widgetId;
|
||||||
|
|
||||||
|
return <StyledContainer ref={ref} {...props} focus={focus} />;
|
||||||
|
});
|
||||||
|
|
||||||
export const ParentBoundsContext: Context<{
|
export const ParentBoundsContext: Context<{
|
||||||
boundingParent?: React.RefObject<HTMLDivElement>;
|
boundingParent?: React.RefObject<HTMLDivElement>;
|
||||||
}> = createContext({});
|
}> = createContext({});
|
||||||
|
type ContainerComponentWrapperProps = ContainerStyleProps & {
|
||||||
|
isRoot?: boolean;
|
||||||
|
};
|
||||||
|
const ContainerComponentWrapper = styled.div<ContainerComponentWrapperProps>`
|
||||||
|
/* TODO(abhinav)(Issue: #107): this will changed based on the ContainerStyleProps */
|
||||||
|
border: ${props =>
|
||||||
|
!props.isRoot && getBorderCSSShorthand(props.theme.borders[2])};
|
||||||
|
box-shadow: ${props =>
|
||||||
|
!props.isRoot ? "0px 2px 4px rgba(67, 70, 74, 0.14)" : "none"};
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
const ContainerComponent = (props: ContainerProps) => {
|
const ContainerComponent = (props: ContainerProps) => {
|
||||||
const container = useRef(null);
|
const container = useRef(null);
|
||||||
return (
|
return (
|
||||||
<ParentBoundsContext.Provider value={{ boundingParent: container }}>
|
<ParentBoundsContext.Provider value={{ boundingParent: container }}>
|
||||||
<Container ref={container} {...props}>
|
<Container ref={container} {...props}>
|
||||||
{props.children}
|
<ContainerComponentWrapper isRoot={props.isRoot}>
|
||||||
|
{props.children}
|
||||||
|
</ContainerComponentWrapper>
|
||||||
</Container>
|
</Container>
|
||||||
</ParentBoundsContext.Provider>
|
</ParentBoundsContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
@ -50,4 +85,8 @@ export interface ContainerProps extends ComponentProps {
|
||||||
isRoot?: boolean;
|
isRoot?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ContainerStyleProps = {
|
||||||
|
styleName?: "border" | "card" | "rounded-border";
|
||||||
|
};
|
||||||
|
|
||||||
export default ContainerComponent;
|
export default ContainerComponent;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import { WidgetFunctionsContext } from "../pages/Editor/WidgetsEditor";
|
||||||
import { ControlIcons } from "../icons/ControlIcons";
|
import { ControlIcons } from "../icons/ControlIcons";
|
||||||
import { theme } from "../constants/DefaultTheme";
|
import { theme } from "../constants/DefaultTheme";
|
||||||
import { ResizingContext } from "./DropTargetComponent";
|
import { ResizingContext } from "./DropTargetComponent";
|
||||||
|
import { Tooltip } from "@blueprintjs/core";
|
||||||
|
|
||||||
// FontSizes array in DefaultTheme.tsx
|
// FontSizes array in DefaultTheme.tsx
|
||||||
// Change this to toggle the size of delete and move handles.
|
// Change this to toggle the size of delete and move handles.
|
||||||
|
|
@ -124,18 +125,18 @@ const DraggableComponent = (props: DraggableComponentProps) => {
|
||||||
collect: (monitor: DragSourceMonitor) => ({
|
collect: (monitor: DragSourceMonitor) => ({
|
||||||
isDragging: monitor.isDragging(),
|
isDragging: monitor.isDragging(),
|
||||||
}),
|
}),
|
||||||
end: (widget, monitor) => {
|
|
||||||
if (monitor.didDrop()) {
|
|
||||||
if (isFocused === props.widgetId && showPropertyPane && currentNode) {
|
|
||||||
showPropertyPane(props.widgetId, currentNode, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
begin: () => {
|
begin: () => {
|
||||||
if (isFocused === props.widgetId && showPropertyPane && currentNode) {
|
if (isFocused === props.widgetId && showPropertyPane && currentNode) {
|
||||||
showPropertyPane(props.widgetId, undefined);
|
showPropertyPane(props.widgetId, undefined);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
end: (widget, monitor) => {
|
||||||
|
if (monitor.didDrop()) {
|
||||||
|
if (isFocused === props.widgetId && showPropertyPane && currentNode) {
|
||||||
|
showPropertyPane(props.widgetId, currentNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
canDrag: () => {
|
canDrag: () => {
|
||||||
return !isResizing && !!isFocused && isFocused === props.widgetId;
|
return !isResizing && !!isFocused && isFocused === props.widgetId;
|
||||||
},
|
},
|
||||||
|
|
@ -175,13 +176,19 @@ const DraggableComponent = (props: DraggableComponentProps) => {
|
||||||
<DraggableMask ref={referenceRef} />
|
<DraggableMask ref={referenceRef} />
|
||||||
{props.children}
|
{props.children}
|
||||||
<DragHandle className="control" ref={drag}>
|
<DragHandle className="control" ref={drag}>
|
||||||
{moveControlIcon}
|
<Tooltip content="Move" hoverOpenDelay={500}>
|
||||||
|
{moveControlIcon}
|
||||||
|
</Tooltip>
|
||||||
</DragHandle>
|
</DragHandle>
|
||||||
<DeleteControl className="control" onClick={deleteWidget}>
|
<DeleteControl className="control" onClick={deleteWidget}>
|
||||||
{deleteControlIcon}
|
<Tooltip content="Delete" hoverOpenDelay={500}>
|
||||||
|
{deleteControlIcon}
|
||||||
|
</Tooltip>
|
||||||
</DeleteControl>
|
</DeleteControl>
|
||||||
<EditControl className="control" onClick={togglePropertyEditor}>
|
<EditControl className="control" onClick={togglePropertyEditor}>
|
||||||
{editControlIcon}
|
<Tooltip content="Toggle properties pane" hoverOpenDelay={500}>
|
||||||
|
{editControlIcon}
|
||||||
|
</Tooltip>
|
||||||
</EditControl>
|
</EditControl>
|
||||||
</DraggableWrapper>
|
</DraggableWrapper>
|
||||||
</DraggableComponentContext.Provider>
|
</DraggableComponentContext.Provider>
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ type DropTargetBounds = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ResizingContext: Context<{
|
export const ResizingContext: Context<{
|
||||||
isResizing?: boolean;
|
isResizing?: boolean | string;
|
||||||
setIsResizing?: Function;
|
setIsResizing?: Function;
|
||||||
}> = createContext({});
|
}> = createContext({});
|
||||||
|
|
||||||
|
|
@ -36,8 +36,7 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
|
||||||
const [isResizing, setIsResizing] = useState(false);
|
const [isResizing, setIsResizing] = useState(false);
|
||||||
const { updateWidget } = useContext(WidgetFunctionsContext);
|
const { updateWidget } = useContext(WidgetFunctionsContext);
|
||||||
const occupiedSpaces = useContext(OccupiedSpaceContext);
|
const occupiedSpaces = useContext(OccupiedSpaceContext);
|
||||||
const { setFocus } = useContext(FocusContext);
|
const { setFocus, showPropertyPane } = useContext(FocusContext);
|
||||||
|
|
||||||
// Make this component a drop target
|
// Make this component a drop target
|
||||||
const [{ isOver, isExactlyOver }, drop] = useDrop({
|
const [{ isOver, isExactlyOver }, drop] = useDrop({
|
||||||
accept: Object.values(WidgetFactory.getWidgetTypes()),
|
accept: Object.values(WidgetFactory.getWidgetTypes()),
|
||||||
|
|
@ -99,6 +98,7 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
|
||||||
const handleFocus = () => {
|
const handleFocus = () => {
|
||||||
if (props.isRoot) {
|
if (props.isRoot) {
|
||||||
setFocus && setFocus(props.widgetId);
|
setFocus && setFocus(props.widgetId);
|
||||||
|
showPropertyPane && showPropertyPane();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,8 @@ const ResizableContainer = styled(Rnd)`
|
||||||
width: ${props => props.theme.spaces[2]}px;
|
width: ${props => props.theme.spaces[2]}px;
|
||||||
height: ${props => props.theme.spaces[2]}px;
|
height: ${props => props.theme.spaces[2]}px;
|
||||||
border-radius: ${props => props.theme.radii[5]}%;
|
border-radius: ${props => props.theme.radii[5]}%;
|
||||||
background: ${props => props.theme.colors.containerBorder};
|
background: ${props =>
|
||||||
|
props.isfocused && props.theme.colors.containerBorder};
|
||||||
}
|
}
|
||||||
&:after {
|
&:after {
|
||||||
right: -${props => props.theme.spaces[1]}px;
|
right: -${props => props.theme.spaces[1]}px;
|
||||||
|
|
@ -88,11 +89,11 @@ const ResizableContainer = styled(Rnd)`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ResizableComponent = (props: ResizableComponentProps) => {
|
export const ResizableComponent = (props: ResizableComponentProps) => {
|
||||||
const { isDragging } = useContext(DraggableComponentContext);
|
const { isDragging, widgetNode } = useContext(DraggableComponentContext);
|
||||||
const { setIsResizing } = useContext(ResizingContext);
|
const { setIsResizing } = useContext(ResizingContext);
|
||||||
const { boundingParent } = useContext(ParentBoundsContext);
|
const { boundingParent } = useContext(ParentBoundsContext);
|
||||||
const { updateWidget } = useContext(WidgetFunctionsContext);
|
const { updateWidget } = useContext(WidgetFunctionsContext);
|
||||||
const { isFocused, setFocus, showPropertyPane } = useContext(FocusContext);
|
const { showPropertyPane, isFocused, setFocus } = useContext(FocusContext);
|
||||||
const occupiedSpaces = useContext(OccupiedSpaceContext);
|
const occupiedSpaces = useContext(OccupiedSpaceContext);
|
||||||
|
|
||||||
const [isColliding, setIsColliding] = useState(false);
|
const [isColliding, setIsColliding] = useState(false);
|
||||||
|
|
@ -145,6 +146,7 @@ export const ResizableComponent = (props: ResizableComponentProps) => {
|
||||||
) => {
|
) => {
|
||||||
setIsResizing && setIsResizing(false);
|
setIsResizing && setIsResizing(false);
|
||||||
setFocus && setFocus(props.widgetId);
|
setFocus && setFocus(props.widgetId);
|
||||||
|
showPropertyPane && showPropertyPane(props.widgetId, widgetNode);
|
||||||
|
|
||||||
const leftColumn = props.leftColumn + position.x / props.parentColumnSpace;
|
const leftColumn = props.leftColumn + position.x / props.parentColumnSpace;
|
||||||
const topRow = props.topRow + position.y / props.parentRowSpace;
|
const topRow = props.topRow + position.y / props.parentRowSpace;
|
||||||
|
|
@ -174,6 +176,7 @@ export const ResizableComponent = (props: ResizableComponentProps) => {
|
||||||
const canResize = !isDragging && isFocused === props.widgetId;
|
const canResize = !isDragging && isFocused === props.widgetId;
|
||||||
return (
|
return (
|
||||||
<ResizableContainer
|
<ResizableContainer
|
||||||
|
isfocused={isFocused === props.widgetId ? "true" : undefined}
|
||||||
position={{
|
position={{
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
|
@ -193,13 +196,14 @@ export const ResizableComponent = (props: ResizableComponentProps) => {
|
||||||
border:
|
border:
|
||||||
isFocused === props.widgetId
|
isFocused === props.widgetId
|
||||||
? getBorderCSSShorthand(theme.borders[1])
|
? getBorderCSSShorthand(theme.borders[1])
|
||||||
: getBorderCSSShorthand(theme.borders[0]),
|
: "none",
|
||||||
|
boxSizing: "content-box",
|
||||||
}}
|
}}
|
||||||
onResizeStop={updateSize}
|
onResizeStop={updateSize}
|
||||||
onResize={checkForCollision}
|
onResize={checkForCollision}
|
||||||
onResizeStart={() => {
|
onResizeStart={() => {
|
||||||
setIsResizing && setIsResizing(true);
|
setIsResizing && setIsResizing(true);
|
||||||
showPropertyPane && showPropertyPane(props.widgetId, undefined);
|
showPropertyPane && showPropertyPane(props.widgetId);
|
||||||
}}
|
}}
|
||||||
resizeGrid={[props.parentColumnSpace, props.parentRowSpace]}
|
resizeGrid={[props.parentColumnSpace, props.parentRowSpace]}
|
||||||
bounds={bounds}
|
bounds={bounds}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
||||||
rows: 1,
|
rows: 1,
|
||||||
columns: 2,
|
columns: 2,
|
||||||
widgetName: "Button",
|
widgetName: "Button",
|
||||||
|
isDisabled: false,
|
||||||
|
isVisible: true,
|
||||||
},
|
},
|
||||||
TEXT_WIDGET: {
|
TEXT_WIDGET: {
|
||||||
text: "Not all labels are bad!",
|
text: "Not all labels are bad!",
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ type PopperProps = {
|
||||||
const PopperWrapper = styled(PaneWrapper)`
|
const PopperWrapper = styled(PaneWrapper)`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
height: ${props => props.theme.propertyPane.height}px;
|
max-height: ${props => props.theme.propertyPane.height}px;
|
||||||
width: ${props => props.theme.propertyPane.width}px;
|
width: ${props => props.theme.propertyPane.width}px;
|
||||||
margin: ${props => props.theme.spaces[6]}px;
|
margin: ${props => props.theme.spaces[6]}px;
|
||||||
`;
|
`;
|
||||||
|
|
@ -23,27 +23,27 @@ export default (props: PopperProps) => {
|
||||||
const contentRef = useRef(null);
|
const contentRef = useRef(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//TODO(abhinav): optimize this, remove previous Popper instance.
|
//TODO(abhinav): optimize this, remove previous Popper instance.
|
||||||
new PopperJS(
|
const parentElement = props.targetRefNode.parentElement;
|
||||||
props.targetRefNode,
|
if (parentElement && parentElement.parentElement) {
|
||||||
(contentRef.current as unknown) as Element,
|
new PopperJS(
|
||||||
{
|
props.targetRefNode,
|
||||||
placement: "right",
|
(contentRef.current as unknown) as Element,
|
||||||
modifiers: {
|
{
|
||||||
flip: {
|
placement: "right-start",
|
||||||
behavior: ["right", "left", "bottom", "top"],
|
modifiers: {
|
||||||
},
|
flip: {
|
||||||
keepTogether: {
|
behavior: ["right", "left", "bottom", "top"],
|
||||||
enabled: false,
|
},
|
||||||
},
|
keepTogether: {
|
||||||
arrow: {
|
enabled: false,
|
||||||
enabled: false,
|
},
|
||||||
},
|
arrow: {
|
||||||
preventOverflow: {
|
enabled: false,
|
||||||
boundariesElement: "viewport",
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
);
|
||||||
);
|
}
|
||||||
}, [props.targetRefNode]);
|
}, [props.targetRefNode]);
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<PopperWrapper ref={contentRef}>{props.children}</PopperWrapper>,
|
<PopperWrapper ref={contentRef}>{props.children}</PopperWrapper>,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { AppState } from "../../reducers";
|
import { AppState } from "../../reducers";
|
||||||
import PropertyControlFactory from "../../utils/PropertyControlFactory";
|
import PropertyControlFactory from "../../utils/PropertyControlFactory";
|
||||||
|
|
@ -11,10 +12,21 @@ import {
|
||||||
getCurrentReferenceNode,
|
getCurrentReferenceNode,
|
||||||
getPropertyConfig,
|
getPropertyConfig,
|
||||||
getIsPropertyPaneVisible,
|
getIsPropertyPaneVisible,
|
||||||
|
getCurrentWidgetProperties,
|
||||||
} from "../../selectors/propertyPaneSelectors";
|
} from "../../selectors/propertyPaneSelectors";
|
||||||
|
|
||||||
import Popper from "./Popper";
|
import Popper from "./Popper";
|
||||||
|
|
||||||
|
const PropertySectionLabel = styled.div`
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: ${props => props.theme.colors.paneSectionLabel};
|
||||||
|
padding: ${props => props.theme.spaces[5]}px 0;
|
||||||
|
font-size: ${props => props.theme.fontSizes[2]}px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
class PropertyPane extends Component<
|
class PropertyPane extends Component<
|
||||||
PropertyPaneProps & PropertyPaneFunctions
|
PropertyPaneProps & PropertyPaneFunctions
|
||||||
> {
|
> {
|
||||||
|
|
@ -24,12 +36,7 @@ class PropertyPane extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (
|
if (this.props.isVisible && this.props.widgetId && this.props.targetNode) {
|
||||||
this.props.isVisible &&
|
|
||||||
this.props.widgetId &&
|
|
||||||
this.props.targetNode &&
|
|
||||||
this.props.propertySections
|
|
||||||
) {
|
|
||||||
const content = this.renderPropertyPane(this.props.propertySections);
|
const content = this.renderPropertyPane(this.props.propertySections);
|
||||||
return (
|
return (
|
||||||
<Popper isOpen={true} targetRefNode={this.props.targetNode}>
|
<Popper isOpen={true} targetRefNode={this.props.targetNode}>
|
||||||
|
|
@ -48,7 +55,7 @@ class PropertyPane extends Component<
|
||||||
? _.map(propertySections, (propertySection: PropertySection) => {
|
? _.map(propertySections, (propertySection: PropertySection) => {
|
||||||
return this.renderPropertySection(
|
return this.renderPropertySection(
|
||||||
propertySection,
|
propertySection,
|
||||||
propertySection.id,
|
this.props.widgetId + propertySection.id,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
: undefined}
|
: undefined}
|
||||||
|
|
@ -60,7 +67,9 @@ class PropertyPane extends Component<
|
||||||
return (
|
return (
|
||||||
<div key={key}>
|
<div key={key}>
|
||||||
{!_.isNil(propertySection) ? (
|
{!_.isNil(propertySection) ? (
|
||||||
<div>{propertySection.sectionName}</div>
|
<PropertySectionLabel>
|
||||||
|
{propertySection.sectionName}
|
||||||
|
</PropertySectionLabel>
|
||||||
) : (
|
) : (
|
||||||
undefined
|
undefined
|
||||||
)}
|
)}
|
||||||
|
|
@ -81,6 +90,9 @@ class PropertyPane extends Component<
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
propertyControlOrSection.propertyValue = this.props.widgetProperties[
|
||||||
|
propertyControlOrSection.propertyName
|
||||||
|
];
|
||||||
return PropertyControlFactory.createControl(
|
return PropertyControlFactory.createControl(
|
||||||
propertyControlOrSection,
|
propertyControlOrSection,
|
||||||
{ onPropertyChange: this.onPropertyChange },
|
{ onPropertyChange: this.onPropertyChange },
|
||||||
|
|
@ -97,11 +109,11 @@ class PropertyPane extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
onPropertyChange(propertyName: string, propertyValue: any) {
|
onPropertyChange(propertyName: string, propertyValue: any) {
|
||||||
// this.props.updateWidgetProperty(
|
this.props.updateWidgetProperty(
|
||||||
// this.props.widgetId,
|
this.props.widgetId,
|
||||||
// propertyName,
|
propertyName,
|
||||||
// propertyValue,
|
propertyValue,
|
||||||
// );
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,6 +121,7 @@ const mapStateToProps = (state: AppState): PropertyPaneProps => {
|
||||||
return {
|
return {
|
||||||
propertySections: getPropertyConfig(state),
|
propertySections: getPropertyConfig(state),
|
||||||
widgetId: getCurrentWidgetId(state),
|
widgetId: getCurrentWidgetId(state),
|
||||||
|
widgetProperties: getCurrentWidgetProperties(state),
|
||||||
isVisible: getIsPropertyPaneVisible(state),
|
isVisible: getIsPropertyPaneVisible(state),
|
||||||
targetNode: getCurrentReferenceNode(state),
|
targetNode: getCurrentReferenceNode(state),
|
||||||
};
|
};
|
||||||
|
|
@ -127,6 +140,7 @@ const mapDispatchToProps = (dispatch: any): PropertyPaneFunctions => {
|
||||||
export interface PropertyPaneProps {
|
export interface PropertyPaneProps {
|
||||||
propertySections?: PropertySection[];
|
propertySections?: PropertySection[];
|
||||||
widgetId?: string;
|
widgetId?: string;
|
||||||
|
widgetProperties?: any; //TODO(abhinav): Secure type definition
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
targetNode?: HTMLDivElement;
|
targetNode?: HTMLDivElement;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ export default styled.div`
|
||||||
background-color: ${props => props.theme.colors.paneBG};
|
background-color: ${props => props.theme.colors.paneBG};
|
||||||
border-radius: ${props => props.theme.radii[2]}px;
|
border-radius: ${props => props.theme.radii[2]}px;
|
||||||
box-shadow: 0px 0px 3px ${props => props.theme.colors.paneBG};
|
box-shadow: 0px 0px 3px ${props => props.theme.colors.paneBG};
|
||||||
padding: 5px 10px;
|
padding: ${props => props.theme.spaces[5]}px
|
||||||
|
${props => props.theme.spaces[7]}px;
|
||||||
color: ${props => props.theme.colors.textOnDarkBG};
|
color: ${props => props.theme.colors.textOnDarkBG};
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ export interface ControlData {
|
||||||
label: string;
|
label: string;
|
||||||
propertyName: string;
|
propertyName: string;
|
||||||
controlType: ControlType;
|
controlType: ControlType;
|
||||||
|
propertyValue?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ControlFunctions {
|
export interface ControlFunctions {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ import React, { SyntheticEvent } from "react";
|
||||||
import BaseControl, { ControlProps } from "./BaseControl";
|
import BaseControl, { ControlProps } from "./BaseControl";
|
||||||
import { ControlType } from "../constants/PropertyControlConstants";
|
import { ControlType } from "../constants/PropertyControlConstants";
|
||||||
import { Button, MenuItem } from "@blueprintjs/core";
|
import { Button, MenuItem } from "@blueprintjs/core";
|
||||||
import { Select, IItemRendererProps } from "@blueprintjs/select";
|
import { IItemRendererProps } from "@blueprintjs/select";
|
||||||
|
import { ControlWrapper, StyledDropDown } from "./StyledControls";
|
||||||
|
|
||||||
class DropDownControl extends BaseControl<DropDownControlProps> {
|
class DropDownControl extends BaseControl<DropDownControlProps> {
|
||||||
constructor(props: DropDownControlProps) {
|
constructor(props: DropDownControlProps) {
|
||||||
|
|
@ -11,20 +12,25 @@ class DropDownControl extends BaseControl<DropDownControlProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const DropDown = Select.ofType<DropdownOption>();
|
const selected: DropdownOption | undefined = this.props.options.find(
|
||||||
|
option => option.value === this.props.propertyValue,
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<DropDown
|
<ControlWrapper>
|
||||||
items={this.props.options}
|
<label>{this.props.label}</label>
|
||||||
itemPredicate={this.filterOption}
|
<StyledDropDown
|
||||||
itemRenderer={this.renderItem}
|
items={this.props.options}
|
||||||
onItemSelect={this.onItemSelect}
|
itemPredicate={this.filterOption}
|
||||||
noResults={<MenuItem disabled={true} text="No results." />}
|
itemRenderer={this.renderItem}
|
||||||
>
|
onItemSelect={this.onItemSelect}
|
||||||
<Button
|
noResults={<MenuItem disabled={true} text="No results." />}
|
||||||
text={this.props.options[0].label}
|
>
|
||||||
rightIcon="double-caret-vertical"
|
<Button
|
||||||
/>
|
text={selected ? selected.label : ""}
|
||||||
</DropDown>
|
rightIcon="chevron-down"
|
||||||
|
/>
|
||||||
|
</StyledDropDown>
|
||||||
|
</ControlWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import BaseControl, { ControlProps } from "./BaseControl";
|
import BaseControl, { ControlProps } from "./BaseControl";
|
||||||
import { ControlType } from "../constants/PropertyControlConstants";
|
import { ControlType } from "../constants/PropertyControlConstants";
|
||||||
import { InputGroup } from "@blueprintjs/core";
|
|
||||||
import { InputType } from "../widgets/InputWidget";
|
import { InputType } from "../widgets/InputWidget";
|
||||||
|
import { ControlWrapper, StyledInputGroup } from "./StyledControls";
|
||||||
|
|
||||||
class InputTextControl extends BaseControl<InputControlProps> {
|
class InputTextControl extends BaseControl<InputControlProps> {
|
||||||
render() {
|
render() {
|
||||||
return <InputGroup onChange={this.onTextChange} />;
|
return (
|
||||||
|
<ControlWrapper>
|
||||||
|
<label>{this.props.label}</label>
|
||||||
|
<StyledInputGroup
|
||||||
|
onChange={this.onTextChange}
|
||||||
|
defaultValue={this.props.propertyValue}
|
||||||
|
/>
|
||||||
|
</ControlWrapper>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onTextChange(event: React.ChangeEvent<HTMLInputElement>) {
|
onTextChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
this.updateProperty(this.props.propertyName, event.target.value);
|
this.updateProperty(this.props.propertyName, event.target.value);
|
||||||
}
|
};
|
||||||
|
|
||||||
getControlType(): ControlType {
|
getControlType(): ControlType {
|
||||||
return "INPUT_TEXT";
|
return "INPUT_TEXT";
|
||||||
|
|
|
||||||
35
app/client/src/propertyControls/StyledControls.tsx
Normal file
35
app/client/src/propertyControls/StyledControls.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import styled from "styled-components";
|
||||||
|
import { Select } from "@blueprintjs/select";
|
||||||
|
import { Switch, InputGroup } from "@blueprintjs/core";
|
||||||
|
|
||||||
|
export const ControlWrapper = styled.div`
|
||||||
|
margin: ${props => props.theme.spaces[3]}px 0;
|
||||||
|
& > label {
|
||||||
|
display: block;
|
||||||
|
color: ${props => props.theme.colors.paneText};
|
||||||
|
margin-bottom: ${props => props.theme.spaces[1]}px;
|
||||||
|
font-size: ${props => props.theme.fontSizes[3]}px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DropDown = Select.ofType<{ label: string; value: string }>();
|
||||||
|
export const StyledDropDown = styled(DropDown)`
|
||||||
|
&&& button {
|
||||||
|
background: ${props => props.theme.colors.paneInputBG};
|
||||||
|
color: ${props => props.theme.colors.textOnDarkBG};
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const StyledSwitch = styled(Switch)`
|
||||||
|
&&&&& input:checked ~ span {
|
||||||
|
background: ${props => props.theme.colors.primary};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const StyledInputGroup = styled(InputGroup)`
|
||||||
|
& > input {
|
||||||
|
color: ${props => props.theme.colors.textOnDarkBG};
|
||||||
|
background: ${props => props.theme.colors.paneInputBG};
|
||||||
|
}
|
||||||
|
`;
|
||||||
31
app/client/src/propertyControls/SwitchControl.tsx
Normal file
31
app/client/src/propertyControls/SwitchControl.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React from "react";
|
||||||
|
import BaseControl, { ControlProps } from "./BaseControl";
|
||||||
|
import { ControlType } from "../constants/PropertyControlConstants";
|
||||||
|
import { ControlWrapper, StyledSwitch } from "./StyledControls";
|
||||||
|
|
||||||
|
class SwitchControl extends BaseControl<ControlProps> {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ControlWrapper>
|
||||||
|
<label>{this.props.label}</label>
|
||||||
|
<StyledSwitch
|
||||||
|
onChange={this.onToggle}
|
||||||
|
defaultChecked={this.props.propertyValue}
|
||||||
|
large
|
||||||
|
/>
|
||||||
|
</ControlWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onToggle = () => {
|
||||||
|
this.updateProperty(this.props.propertyName, !this.props.propertyValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
getControlType(): ControlType {
|
||||||
|
return "SWITCH";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SwitchControlProps = ControlProps;
|
||||||
|
|
||||||
|
export default SwitchControl;
|
||||||
|
|
@ -33,7 +33,7 @@ const canvasWidgetsReducer = createReducer(initialState, {
|
||||||
) => {
|
) => {
|
||||||
const widget = state[action.payload.widgetId];
|
const widget = state[action.payload.widgetId];
|
||||||
return {
|
return {
|
||||||
state,
|
...state,
|
||||||
[action.payload.widgetId]: {
|
[action.payload.widgetId]: {
|
||||||
...widget,
|
...widget,
|
||||||
[action.payload.propertyName]: action.payload.propertyValue,
|
[action.payload.propertyName]: action.payload.propertyValue,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ const initialState: PropertyPaneConfigState = PropertyPaneConfigResponse;
|
||||||
export type ControlConfig =
|
export type ControlConfig =
|
||||||
| InputControlProps
|
| InputControlProps
|
||||||
| DropDownControlProps
|
| DropDownControlProps
|
||||||
| InputControlProps
|
|
||||||
| ControlProps;
|
| ControlProps;
|
||||||
|
|
||||||
export type SectionOrientation = "HORIZONTAL" | "VERTICAL";
|
export type SectionOrientation = "HORIZONTAL" | "VERTICAL";
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ const propertyPaneReducer = createReducer(initialState, {
|
||||||
) => {
|
) => {
|
||||||
let isVisible = true;
|
let isVisible = true;
|
||||||
const { widgetId, node, toggle } = action.payload;
|
const { widgetId, node, toggle } = action.payload;
|
||||||
|
if (state.widgetId === action.payload.widgetId) {
|
||||||
|
isVisible = state.isVisible;
|
||||||
|
}
|
||||||
if (toggle) {
|
if (toggle) {
|
||||||
isVisible = !state.isVisible;
|
isVisible = !state.isVisible;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import {
|
||||||
} from "redux-saga/effects";
|
} from "redux-saga/effects";
|
||||||
|
|
||||||
import { extractCurrentDSL } from "../utils/WidgetPropsUtils";
|
import { extractCurrentDSL } from "../utils/WidgetPropsUtils";
|
||||||
import { getEditorConfigs } from "./selectors";
|
import { getEditorConfigs, getWidgets } from "./selectors";
|
||||||
import { validateResponse } from "./ErrorSagas";
|
import { validateResponse } from "./ErrorSagas";
|
||||||
|
|
||||||
export function* fetchPageSaga(
|
export function* fetchPageSaga(
|
||||||
|
|
@ -86,6 +86,22 @@ export function* savePageSaga(savePageAction: ReduxAction<SavePageRequest>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLayoutSavePayload(
|
||||||
|
widgets: {
|
||||||
|
[widgetId: string]: FlattenedWidgetProps;
|
||||||
|
},
|
||||||
|
editorConfigs: any,
|
||||||
|
) {
|
||||||
|
const denormalizedDSL = CanvasWidgetsNormalizer.denormalize(
|
||||||
|
Object.keys(widgets)[0],
|
||||||
|
{ canvasWidgets: widgets },
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...editorConfigs,
|
||||||
|
dsl: denormalizedDSL,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function* saveLayoutSaga(
|
export function* saveLayoutSaga(
|
||||||
updateLayoutAction: ReduxAction<{
|
updateLayoutAction: ReduxAction<{
|
||||||
widgets: { [widgetId: string]: FlattenedWidgetProps };
|
widgets: { [widgetId: string]: FlattenedWidgetProps };
|
||||||
|
|
@ -93,27 +109,54 @@ export function* saveLayoutSaga(
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const { widgets } = updateLayoutAction.payload;
|
const { widgets } = updateLayoutAction.payload;
|
||||||
const denormalizedDSL = CanvasWidgetsNormalizer.denormalize(
|
|
||||||
Object.keys(widgets)[0],
|
|
||||||
{ canvasWidgets: widgets },
|
|
||||||
);
|
|
||||||
const editorConfigs = yield select(getEditorConfigs) as any;
|
const editorConfigs = yield select(getEditorConfigs) as any;
|
||||||
|
|
||||||
yield put({
|
yield put({
|
||||||
type: ReduxActionTypes.SAVE_PAGE_INIT,
|
type: ReduxActionTypes.SAVE_PAGE_INIT,
|
||||||
payload: {
|
payload: getLayoutSavePayload(widgets, editorConfigs),
|
||||||
...editorConfigs,
|
|
||||||
dsl: denormalizedDSL,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(abhinav): This has redundant code. The only thing different here is the lack of state update.
|
||||||
|
// For now this is fire and forget.
|
||||||
|
export function* asyncSaveLayout() {
|
||||||
|
try {
|
||||||
|
const widgets = yield select(getWidgets);
|
||||||
|
const editorConfigs = yield select(getEditorConfigs) as any;
|
||||||
|
|
||||||
|
const request: SavePageRequest = getLayoutSavePayload(
|
||||||
|
widgets,
|
||||||
|
editorConfigs,
|
||||||
|
);
|
||||||
|
|
||||||
|
const savePageResponse: SavePageResponse = yield call(
|
||||||
|
PageApi.savePage,
|
||||||
|
request,
|
||||||
|
);
|
||||||
|
if (!validateResponse(savePageResponse)) {
|
||||||
|
throw Error("Error when saving layout");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
yield put({
|
||||||
|
type: ReduxActionErrorTypes.UPDATE_WIDGET_PROPERTY_ERROR,
|
||||||
|
payload: {
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function* pageSagas() {
|
export default function* pageSagas() {
|
||||||
yield all([
|
yield all([
|
||||||
takeLatest(ReduxActionTypes.FETCH_PAGE, fetchPageSaga),
|
takeLatest(ReduxActionTypes.FETCH_PAGE, fetchPageSaga),
|
||||||
takeLatest(ReduxActionTypes.SAVE_PAGE_INIT, savePageSaga),
|
takeLatest(ReduxActionTypes.SAVE_PAGE_INIT, savePageSaga),
|
||||||
takeEvery(ReduxActionTypes.UPDATE_LAYOUT, saveLayoutSaga),
|
takeEvery(ReduxActionTypes.UPDATE_LAYOUT, saveLayoutSaga),
|
||||||
|
// No need to save layout everytime a property is updated.
|
||||||
|
// We save the latest request to update layout.
|
||||||
|
takeLatest(ReduxActionTypes.UPDATE_WIDGET_PROPERTY, asyncSaveLayout),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,14 @@ export const getCurrentWidgetId = createSelector(
|
||||||
(propertyPane: PropertyPaneReduxState) => propertyPane.widgetId,
|
(propertyPane: PropertyPaneReduxState) => propertyPane.widgetId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getCurrentWidgetProperties = createSelector(
|
||||||
|
getCanvasWidgets,
|
||||||
|
getPropertyPaneState,
|
||||||
|
(widgets: CanvasWidgetsReduxState, pane: PropertyPaneReduxState) => {
|
||||||
|
return pane.widgetId && widgets ? widgets[pane.widgetId] : undefined;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const getCurrentReferenceNode = createSelector(
|
export const getCurrentReferenceNode = createSelector(
|
||||||
getPropertyPaneState,
|
getPropertyPaneState,
|
||||||
(pane: PropertyPaneReduxState) => {
|
(pane: PropertyPaneReduxState) => {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@ import InputTextControl, {
|
||||||
import DropDownControl, {
|
import DropDownControl, {
|
||||||
DropDownControlProps,
|
DropDownControlProps,
|
||||||
} from "../propertyControls/DropDownControl";
|
} from "../propertyControls/DropDownControl";
|
||||||
|
import SwitchControl, {
|
||||||
|
SwitchControlProps,
|
||||||
|
} from "../propertyControls/SwitchControl";
|
||||||
|
|
||||||
class PropertyControlRegistry {
|
class PropertyControlRegistry {
|
||||||
static registerPropertyControlBuilders() {
|
static registerPropertyControlBuilders() {
|
||||||
|
|
@ -19,6 +22,11 @@ class PropertyControlRegistry {
|
||||||
return <DropDownControl {...controlProps} />;
|
return <DropDownControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
PropertyControlFactory.registerControlBuilder("SWITCH", {
|
||||||
|
buildPropertyControl(controlProps: SwitchControlProps): JSX.Element {
|
||||||
|
return <SwitchControl {...controlProps} />;
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { WidgetConfigProps } from "../reducers/entityReducers/widgetConfigReduce
|
||||||
import { WidgetProps, WidgetOperations } from "../widgets/BaseWidget";
|
import { WidgetProps, WidgetOperations } from "../widgets/BaseWidget";
|
||||||
import { WidgetType, RenderModes } from "../constants/WidgetConstants";
|
import { WidgetType, RenderModes } from "../constants/WidgetConstants";
|
||||||
import { generateReactKey } from "../utils/generators";
|
import { generateReactKey } from "../utils/generators";
|
||||||
import { Colors } from "../constants/Colors";
|
|
||||||
import { GridDefaults, WidgetTypes } from "../constants/WidgetConstants";
|
import { GridDefaults, WidgetTypes } from "../constants/WidgetConstants";
|
||||||
import { snapToGrid } from "./helpers";
|
import { snapToGrid } from "./helpers";
|
||||||
import { OccupiedSpace } from "../widgets/ContainerWidget";
|
import { OccupiedSpace } from "../widgets/ContainerWidget";
|
||||||
|
|
@ -26,7 +25,7 @@ const defaultDSL = {
|
||||||
parentColumnSpace: 1,
|
parentColumnSpace: 1,
|
||||||
parentRowSpace: 1,
|
parentRowSpace: 1,
|
||||||
renderMode: "CANVAS",
|
renderMode: "CANVAS",
|
||||||
rightColumn: 1024,
|
rightColumn: 1300,
|
||||||
snapColumns: 16,
|
snapColumns: 16,
|
||||||
snapRows: 32,
|
snapRows: 32,
|
||||||
topRow: 0,
|
topRow: 0,
|
||||||
|
|
@ -37,7 +36,10 @@ const defaultDSL = {
|
||||||
export const extractCurrentDSL = (
|
export const extractCurrentDSL = (
|
||||||
fetchPageResponse: FetchPageResponse,
|
fetchPageResponse: FetchPageResponse,
|
||||||
): ContainerWidgetProps<WidgetProps> => {
|
): ContainerWidgetProps<WidgetProps> => {
|
||||||
return fetchPageResponse.data.layouts[0].dsl || defaultDSL;
|
const currentDSL = fetchPageResponse.data.layouts[0].dsl;
|
||||||
|
currentDSL.rightColumn = 1200;
|
||||||
|
currentDSL.snapColumns = 24;
|
||||||
|
return currentDSL || defaultDSL;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDropZoneOffsets = (
|
export const getDropZoneOffsets = (
|
||||||
|
|
@ -259,7 +261,6 @@ export const generateWidgetProps = (
|
||||||
renderMode: RenderModes.CANVAS,
|
renderMode: RenderModes.CANVAS,
|
||||||
...sizes,
|
...sizes,
|
||||||
...others,
|
...others,
|
||||||
backgroundColor: Colors.WHITE,
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (parent)
|
if (parent)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import * as React from "react";
|
import React from "react";
|
||||||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||||
import { WidgetType } from "../constants/WidgetConstants";
|
import { WidgetType } from "../constants/WidgetConstants";
|
||||||
import ButtonComponent from "../components/canvas/Button";
|
import ButtonComponent, { ButtonStyleName } from "../components/canvas/Button";
|
||||||
import { ActionPayload } from "../constants/ActionConstants";
|
import { ActionPayload } from "../constants/ActionConstants";
|
||||||
|
|
||||||
class ButtonWidget extends BaseWidget<ButtonWidgetProps, WidgetState> {
|
class ButtonWidget extends BaseWidget<ButtonWidgetProps, WidgetState> {
|
||||||
|
|
@ -10,13 +10,19 @@ class ButtonWidget extends BaseWidget<ButtonWidgetProps, WidgetState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getPageView() {
|
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 (
|
return (
|
||||||
<ButtonComponent
|
<ButtonComponent
|
||||||
style={this.getPositionStyle()}
|
style={this.getPositionStyle()}
|
||||||
|
styleName={translatedButtonStyleName}
|
||||||
widgetId={this.props.widgetId}
|
widgetId={this.props.widgetId}
|
||||||
widgetName={this.props.widgetName}
|
widgetName={this.props.widgetName}
|
||||||
key={this.props.widgetId}
|
key={this.props.widgetId}
|
||||||
text={this.props.text}
|
text={this.props.text}
|
||||||
|
disabled={this.props.isDisabled}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.onButtonClick();
|
this.onButtonClick();
|
||||||
}}
|
}}
|
||||||
|
|
@ -40,6 +46,7 @@ export interface ButtonWidgetProps extends WidgetProps {
|
||||||
buttonStyle?: ButtonStyle;
|
buttonStyle?: ButtonStyle;
|
||||||
onClick?: ActionPayload[];
|
onClick?: ActionPayload[];
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
|
isVisible?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ButtonWidget;
|
export default ButtonWidget;
|
||||||
|
|
|
||||||
1927
app/client/yarn.lock
1927
app/client/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user