diff --git a/app/client/README.md b/app/client/README.md index c533c7c2ef..d20687e8ed 100755 --- a/app/client/README.md +++ b/app/client/README.md @@ -1,10 +1,3 @@ -## GIT Commit Hooks - -This project has scripts to ESLint fix and Prettier write the code on the git commit hook. -It is recommended to install ESLint and Prettier global for successful git commits - -`yarn global add eslint` -`yarn global add prettier` This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). diff --git a/app/client/package.json b/app/client/package.json index eec0818de8..1ffbb1d56f 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -39,13 +39,13 @@ "node-sass": "^4.11.0", "normalizr": "^3.3.0", "prettier": "^1.18.2", - "re-resizable": "^6.0.0", "react": "^16.7.0", "react-dnd": "^9.3.4", "react-dnd-html5-backend": "^9.3.4", "react-dom": "^16.7.0", "react-netlify-identity": "^0.1.9", "react-redux": "^6.0.0", + "react-rnd": "^10.1.1", "react-router": "^5.0.1", "react-router-dom": "^5.0.1", "react-scripts": "^3.1.1", @@ -83,7 +83,6 @@ "eslint-config-react": "^1.1.7", "eslint-plugin-prettier": "^3.1.0", "eslint-plugin-react": "^7.14.3", - "icon-font-generator": "^2.1.10", "redux-devtools": "^3.5.0", "redux-devtools-extension": "^2.13.8" }, diff --git a/app/client/src/actions/pageActions.tsx b/app/client/src/actions/pageActions.tsx index 32004458ca..248edb4d5f 100644 --- a/app/client/src/actions/pageActions.tsx +++ b/app/client/src/actions/pageActions.tsx @@ -1,5 +1,4 @@ import { FetchPageRequest } from "../api/PageApi"; -import { ResponseMeta } from "../api/ApiResponses"; import { RenderMode } from "../constants/WidgetConstants"; import { WidgetProps, WidgetOperation } from "../widgets/BaseWidget"; import { WidgetType } from "../constants/WidgetConstants"; @@ -8,7 +7,6 @@ import { ReduxAction, UpdateCanvasPayload, SavePagePayload, - SavePageErrorPayload, SavePageSuccessPayload, } from "../constants/ReduxActionConstants"; import { ContainerWidgetProps } from "../widgets/ContainerWidget"; @@ -26,13 +24,6 @@ export const fetchPage = ( }; }; -export const fetchPageError = (payload: ResponseMeta) => { - return { - type: ReduxActionTypes.FETCH_PAGE_ERROR, - payload, - }; -}; - export const addWidget = ( pageId: string, widget: WidgetProps, @@ -86,13 +77,6 @@ export const savePageSuccess = (payload: SavePageSuccessPayload) => { }; }; -export const savePageError = (payload: SavePageErrorPayload) => { - return { - type: ReduxActionTypes.SAVE_PAGE_ERROR, - payload, - }; -}; - export type WidgetAddChild = { widgetId: string; type: WidgetType; @@ -108,12 +92,13 @@ export type WidgetMove = { widgetId: string; leftColumn: number; topRow: number; + parentId: string; /* - If parentWidgetId is different from what we have in redux store, + If newParentId is different from what we have in redux store, then we have to delete this, as it has been dropped in another container somewhere. */ - parentWidgetId: string; + newParentId: string; }; export type WidgetRemoveChild = { @@ -123,12 +108,15 @@ export type WidgetRemoveChild = { export type WidgetDelete = { widgetId: string; + parentId: string; }; export type WidgetResize = { widgetId: string; - width: number; // delta/diff - height: number; // delta/diff + leftColumn: number; + rightColumn: number; + topRow: number; + bottomRow: number; }; export const updateWidget = ( diff --git a/app/client/src/actions/widgetCardsPaneActions.tsx b/app/client/src/actions/widgetCardsPaneActions.tsx index ae393a5e0c..a3782ea18d 100644 --- a/app/client/src/actions/widgetCardsPaneActions.tsx +++ b/app/client/src/actions/widgetCardsPaneActions.tsx @@ -1,4 +1,7 @@ -import { ReduxActionTypes } from "../constants/ReduxActionConstants"; +import { + ReduxActionTypes, + ReduxActionErrorTypes, +} from "../constants/ReduxActionConstants"; import { WidgetCardProps } from "../widgets/BaseWidget"; export const fetchWidgetCards = () => { @@ -9,7 +12,7 @@ export const fetchWidgetCards = () => { export const errorFetchingWidgetCards = (error: any) => { return { - type: ReduxActionTypes.ERROR_FETCHING_WIDGET_CARDS, + type: ReduxActionErrorTypes.FETCH_WIDGET_CARDS_ERROR, error, }; }; @@ -18,7 +21,7 @@ export const successFetchingWidgetCards = (cards: { [id: string]: WidgetCardProps[]; }) => { return { - type: ReduxActionTypes.SUCCESS_FETCHING_WIDGET_CARDS, + type: ReduxActionTypes.FETCH_WIDGET_CARDS_SUCCESS, cards, }; }; diff --git a/app/client/src/api/PageApi.tsx b/app/client/src/api/PageApi.tsx index 9814ae976d..d501fd82de 100644 --- a/app/client/src/api/PageApi.tsx +++ b/app/client/src/api/PageApi.tsx @@ -31,7 +31,7 @@ export type FetchPageResponse = ApiResponse & { }; }; -export interface SavePageResponse { +export interface SavePageResponse extends ApiResponse { pageId: string; } diff --git a/app/client/src/assets/icons/control/address.svg b/app/client/src/assets/icons/control/address.svg new file mode 100644 index 0000000000..21acef28d8 --- /dev/null +++ b/app/client/src/assets/icons/control/address.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/bold.svg b/app/client/src/assets/icons/control/bold.svg new file mode 100644 index 0000000000..f3e136da0f --- /dev/null +++ b/app/client/src/assets/icons/control/bold.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/center-align.svg b/app/client/src/assets/icons/control/center-align.svg new file mode 100644 index 0000000000..34e6f1a462 --- /dev/null +++ b/app/client/src/assets/icons/control/center-align.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/chevron-down.svg b/app/client/src/assets/icons/control/chevron-down.svg new file mode 100644 index 0000000000..783082577d --- /dev/null +++ b/app/client/src/assets/icons/control/chevron-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/copy.svg b/app/client/src/assets/icons/control/copy.svg new file mode 100644 index 0000000000..3affe770d7 --- /dev/null +++ b/app/client/src/assets/icons/control/copy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/assets/icons/control/currency.svg b/app/client/src/assets/icons/control/currency.svg new file mode 100644 index 0000000000..2115953d27 --- /dev/null +++ b/app/client/src/assets/icons/control/currency.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/datepicker.svg b/app/client/src/assets/icons/control/datepicker.svg new file mode 100644 index 0000000000..959f2763c8 --- /dev/null +++ b/app/client/src/assets/icons/control/datepicker.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/decimal.svg b/app/client/src/assets/icons/control/decimal.svg new file mode 100644 index 0000000000..a6adb8fcb5 --- /dev/null +++ b/app/client/src/assets/icons/control/decimal.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/delete.svg b/app/client/src/assets/icons/control/delete.svg new file mode 100644 index 0000000000..09b3855266 --- /dev/null +++ b/app/client/src/assets/icons/control/delete.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/assets/icons/control/email.svg b/app/client/src/assets/icons/control/email.svg new file mode 100644 index 0000000000..16d7748e2e --- /dev/null +++ b/app/client/src/assets/icons/control/email.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/info.svg b/app/client/src/assets/icons/control/info.svg new file mode 100644 index 0000000000..300bcd9a06 --- /dev/null +++ b/app/client/src/assets/icons/control/info.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/input.svg b/app/client/src/assets/icons/control/input.svg new file mode 100644 index 0000000000..aeac1584b3 --- /dev/null +++ b/app/client/src/assets/icons/control/input.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/assets/icons/control/integer.svg b/app/client/src/assets/icons/control/integer.svg new file mode 100644 index 0000000000..cd3e6a56c7 --- /dev/null +++ b/app/client/src/assets/icons/control/integer.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/italics.svg b/app/client/src/assets/icons/control/italics.svg new file mode 100644 index 0000000000..7ae3bbba66 --- /dev/null +++ b/app/client/src/assets/icons/control/italics.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/left-align.svg b/app/client/src/assets/icons/control/left-align.svg new file mode 100644 index 0000000000..94eb13e628 --- /dev/null +++ b/app/client/src/assets/icons/control/left-align.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/move.svg b/app/client/src/assets/icons/control/move.svg new file mode 100644 index 0000000000..3cd36f1c26 --- /dev/null +++ b/app/client/src/assets/icons/control/move.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/assets/icons/control/multiline.svg b/app/client/src/assets/icons/control/multiline.svg new file mode 100644 index 0000000000..220bfad216 --- /dev/null +++ b/app/client/src/assets/icons/control/multiline.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/app/client/src/assets/icons/control/password.svg b/app/client/src/assets/icons/control/password.svg new file mode 100644 index 0000000000..ad542c6f95 --- /dev/null +++ b/app/client/src/assets/icons/control/password.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/app/client/src/assets/icons/control/phone.svg b/app/client/src/assets/icons/control/phone.svg new file mode 100644 index 0000000000..33fc877272 --- /dev/null +++ b/app/client/src/assets/icons/control/phone.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/redo.svg b/app/client/src/assets/icons/control/redo.svg new file mode 100644 index 0000000000..71bfe8be4e --- /dev/null +++ b/app/client/src/assets/icons/control/redo.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/right-align.svg b/app/client/src/assets/icons/control/right-align.svg new file mode 100644 index 0000000000..967eb8d1fb --- /dev/null +++ b/app/client/src/assets/icons/control/right-align.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/search.svg b/app/client/src/assets/icons/control/search.svg new file mode 100644 index 0000000000..3aa4f00565 --- /dev/null +++ b/app/client/src/assets/icons/control/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/underline.svg b/app/client/src/assets/icons/control/underline.svg new file mode 100644 index 0000000000..12a393304a --- /dev/null +++ b/app/client/src/assets/icons/control/underline.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/undo.svg b/app/client/src/assets/icons/control/undo.svg new file mode 100644 index 0000000000..2ec8e0fb7c --- /dev/null +++ b/app/client/src/assets/icons/control/undo.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/zoomin.svg b/app/client/src/assets/icons/control/zoomin.svg new file mode 100644 index 0000000000..baa27c369d --- /dev/null +++ b/app/client/src/assets/icons/control/zoomin.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/zoomout.svg b/app/client/src/assets/icons/control/zoomout.svg new file mode 100644 index 0000000000..aceffa4ff6 --- /dev/null +++ b/app/client/src/assets/icons/control/zoomout.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/constants/ApiConstants.tsx b/app/client/src/constants/ApiConstants.tsx index 88220ac5de..594ffea494 100644 --- a/app/client/src/constants/ApiConstants.tsx +++ b/app/client/src/constants/ApiConstants.tsx @@ -35,13 +35,13 @@ export const getEditorConfigs = () => { return { currentPageId: "5d807e7f795dc6000482bc78", currentLayoutId: "5d807e7f795dc6000482bc77", + currentPageName: "page1", }; } else { return { currentPageId: "5d807e76795dc6000482bc76", currentLayoutId: "5d807e76795dc6000482bc75", + currentPageName: "page1", }; } }; - -console.log("here", process.env.NODE_ENV); diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index 7a4e702fdd..60fd21b8c1 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -17,6 +17,7 @@ export const Colors: Record = { RED: "#CE4257", PURPLE: "#6871EF", OXFORD_BLUE: "#2E3D49", + FRENCH_PASS: "#BBE8FE", }; export type Color = (typeof Colors)[keyof typeof Colors]; diff --git a/app/client/src/constants/DefaultTheme.tsx b/app/client/src/constants/DefaultTheme.tsx index adf796fe59..34b6872f51 100644 --- a/app/client/src/constants/DefaultTheme.tsx +++ b/app/client/src/constants/DefaultTheme.tsx @@ -24,9 +24,9 @@ export type Theme = { }; export const theme: Theme = { - radii: [0, 4, 8, 10, 20], + radii: [0, 4, 8, 10, 20, 50], fontSizes: [0, 10, 12, 14, 16, 18, 24, 28, 32, 48, 64], - spaces: [0, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24], + spaces: [0, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24], fontWeights: [0, 400, 500, 700], colors: { primary: Colors.GREEN, @@ -38,15 +38,16 @@ export const theme: Theme = { border: Colors.GEYSER, paneCard: Colors.SHARK, paneBG: Colors.OUTER_SPACE, - grid: Colors.POLAR, + grid: Colors.GEYSER, + containerBorder: Colors.FRENCH_PASS, }, lineHeights: [0, 14, 18, 22, 24, 28, 36, 48, 64, 80], fonts: [FontFamilies.DMSans, FontFamilies.AppsmithWidget], borders: [ { - thickness: "2px", - style: "dashed", - color: Colors.POLAR, + thickness: "1px", + style: "solid", + color: Colors.FRENCH_PASS, }, ], }; diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index 712e8bd27d..a40adcf4e7 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -1,6 +1,8 @@ import { WidgetProps, WidgetCardProps } from "../widgets/BaseWidget"; export const ReduxActionTypes: { [key: string]: string } = { + REPORT_ERROR: "REPORT_ERROR", + FLUSH_ERRORS: "FLUSH_ERRORS", UPDATE_CANVAS: "UPDATE_CANVAS", FETCH_CANVAS: "FETCH_CANVAS", CLEAR_CANVAS: "CLEAR_CANVAS", @@ -16,8 +18,7 @@ export const ReduxActionTypes: { [key: string]: string } = { LOAD_PROPERTY_CONFIG: "LOAD_PROPERTY_CONFIG", PUBLISH: "PUBLISH", FETCH_WIDGET_CARDS: "FETCH_WIDGET_CARDS", - SUCCESS_FETCHING_WIDGET_CARDS: "SUCCESS_FETCHING_WIDGET_CARDS", - ERROR_FETCHING_WIDGET_CARDS: "ERROR_FETCHING_WIDGET_CARDS", + FETCH_WIDGET_CARDS_SUCCESS: "FETCH_WIDGET_CARDS_SUCCESS", ADD_PAGE_WIDGET: "ADD_PAGE_WIDGET", REMOVE_PAGE_WIDGET: "REMOVE_PAGE_WIDGET", LOAD_API_RESPONSE: "LOAD_API_RESPONSE", @@ -26,8 +27,6 @@ export const ReduxActionTypes: { [key: string]: string } = { LOAD_CANVAS_ACTIONS: "LOAD_CANVAS_ACTIONS", SAVE_PAGE_INIT: "SAVE_PAGE_INIT", SAVE_PAGE_SUCCESS: "SAVE_PAGE_SUCCESS", - SAVE_PAGE_ERROR: "SAVE_PAGE_ERROR", - FETCH_PAGE_ERROR: "FETCH_PAGE_ERROR", UPDATE_LAYOUT: "UPDATE_LAYOUT", WIDGET_ADD_CHILD: "WIDGET_ADD_CHILD", WIDGET_REMOVE_CHILD: "WIDGET_REMOVE_CHILD", @@ -37,18 +36,38 @@ export const ReduxActionTypes: { [key: string]: string } = { SHOW_PROPERTY_PANE: "SHOW_PROPERTY_PANE", UPDATE_WIDGET_PROPERTY: "UPDATE_WIDGET_PROPERTY", }; - export type ReduxActionType = (typeof ReduxActionTypes)[keyof typeof ReduxActionTypes]; +export const ReduxActionErrorTypes: { [key: string]: string } = { + API_ERROR: "API_ERROR", + WIDGET_DELETE_ERROR: "WIDGET_DELETE_ERROR", + WIDGET_MOVE_ERROR: "WIDGET_MOVE_ERROR", + WIDGET_RESIZE_ERROR: "WIDGET_RESIZE_ERROR", + WIDGET_REMOVE_CHILD_ERROR: "WIDGET_REMOVE_CHILD_ERROR", + WIDGET_ADD_CHILD_ERROR: "WIDGET_ADD_CHILD_ERROR", + FETCH_PAGE_ERROR: "FETCH_PAGE_ERROR", + SAVE_PAGE_ERROR: "SAVE_PAGE_ERROR", + FETCH_WIDGET_CARDS_ERROR: "FETCH_WIDGET_CARDS_ERROR", + WIDGET_OPERATION_ERROR: "WIDGET_OPERATION_ERROR", +}; +export type ReduxActionErrorType = (typeof ReduxActionErrorTypes)[keyof typeof ReduxActionErrorTypes]; + export interface ReduxAction { - type: ReduxActionType; + type: ReduxActionType | ReduxActionErrorType; payload: T; } +export interface ReduxActionErrorPayload { + message: string; + source?: string; +} + export interface UpdateCanvasPayload { pageWidgetId: string; widgets: { [widgetId: string]: WidgetProps }; - layoutId: string; + currentLayoutId: string; + currentPageId: string; + currentPageName: string; } export interface ShowPropertyPanePayload { diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index be21d05f84..05278c8c24 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -71,4 +71,5 @@ export const GridDefaults = { DEFAULT_WIDGET_HEIGHT: 100, DEFAULT_GRID_COLUMNS: 16, DEFAULT_GRID_ROWS: 32, + DEFAULT_GRID_ROW_HEIGHT: 40, }; diff --git a/app/client/src/editorComponents/BaseComponent.tsx b/app/client/src/editorComponents/BaseComponent.tsx index 3389208508..886a8be1fe 100644 --- a/app/client/src/editorComponents/BaseComponent.tsx +++ b/app/client/src/editorComponents/BaseComponent.tsx @@ -22,6 +22,7 @@ export interface BaseStyle { export interface ComponentProps { widgetId: string; + widgetName?: string; style: BaseStyle; } diff --git a/app/client/src/editorComponents/ButtonComponent.tsx b/app/client/src/editorComponents/ButtonComponent.tsx index 2fb5efa485..da9669b6c9 100644 --- a/app/client/src/editorComponents/ButtonComponent.tsx +++ b/app/client/src/editorComponents/ButtonComponent.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import { Button, MaybeElement } from "@blueprintjs/core"; import { TextComponentProps } from "./TextComponent"; import { Container } from "./ContainerComponent"; diff --git a/app/client/src/editorComponents/ContainerComponent.tsx b/app/client/src/editorComponents/ContainerComponent.tsx index 10826cc822..62fe2d4da9 100644 --- a/app/client/src/editorComponents/ContainerComponent.tsx +++ b/app/client/src/editorComponents/ContainerComponent.tsx @@ -1,7 +1,7 @@ import { ComponentProps } from "./BaseComponent"; import { ContainerOrientation } from "../constants/WidgetConstants"; import styled from "../constants/DefaultTheme"; -import React from "react"; +import React, { createContext, Context, useRef } from "react"; export const Container = styled("div")` display: flex; @@ -13,15 +13,44 @@ export const Container = styled("div")` position: ${props => { return props.style.positionType === "ABSOLUTE" ? "absolute" : "relative"; }}; + height: 100%; + left: 0; + top: 0; + width: 100%; + padding: ${props => props.theme.spaces[8]}px ${props => + props.theme.spaces[1]}px ${props => props.theme.spaces[1]}px; + &:after { + content: "${props => props.widgetName}"; + position: absolute; + left: ${props => props.theme.spaces[1]}px; + top: ${props => props.theme.spaces[1]}px; + font-size: ${props => props.theme.fontSizes[2]}px; + color: ${props => props.theme.colors.containerBorder}; + text-align: left; + width: 100%; + } `; +export const ParentBoundsContext: Context<{ + boundingParent?: React.RefObject; +}> = createContext({}); + const ContainerComponent = (props: ContainerProps) => { - return {props.children}; + const container = useRef(null); + + return ( + + + {props.children} + + + ); }; export interface ContainerProps extends ComponentProps { children?: JSX.Element[] | JSX.Element; orientation?: ContainerOrientation; + isRoot?: boolean; } export default ContainerComponent; diff --git a/app/client/src/editorComponents/DragLayerComponent.tsx b/app/client/src/editorComponents/DragLayerComponent.tsx index a84dd0e23a..454d68b40a 100644 --- a/app/client/src/editorComponents/DragLayerComponent.tsx +++ b/app/client/src/editorComponents/DragLayerComponent.tsx @@ -4,11 +4,12 @@ import { useDragLayer, XYCoord } from "react-dnd"; import DropZone from "./Dropzone"; import { noCollision } from "../utils/WidgetPropsUtils"; import { OccupiedSpace } from "../widgets/ContainerWidget"; +import DropTargetMask from "./DropTargetMask"; const WrappedDragLayer = styled.div` position: absolute; pointer-events: none; - z-index: 100; + z-index: 10; left: 0; top: 0; width: 100%; @@ -23,6 +24,8 @@ type DragLayerProps = { visible: boolean; dropTargetOffset: XYCoord; occupiedSpaces: OccupiedSpace[] | null; + onBoundsUpdate: Function; + isOver: boolean; }; const DragLayerComponent = (props: DragLayerProps) => { @@ -54,8 +57,15 @@ const DragLayerComponent = (props: DragLayerProps) => { } return ( + ` + & > div.control { + display: ${props => (props.show ? "block" : "none")}; + } + display: block; +`; const DragHandle = styled.div` position: absolute; - left: ${props => props.theme.spaces[2]}px; - top: -${props => props.theme.spaces[8]}px; + left: -${props => props.theme.fontSizes[CONTROL_THEME_FONTSIZE_INDEX] / 2}px; + top: -${props => props.theme.fontSizes[CONTROL_THEME_FONTSIZE_INDEX] / 2}px; cursor: move; + display: none; + cursor: grab; + z-index: 11; `; const DeleteControl = styled.div` position: absolute; - right: ${props => props.theme.spaces[2]}px; - top: -${props => props.theme.spaces[8]}px; + right: -${props => props.theme.fontSizes[CONTROL_THEME_FONTSIZE_INDEX] / 2}px; + top: -${props => props.theme.fontSizes[CONTROL_THEME_FONTSIZE_INDEX] / 2}px; + display: none; + cursor: pointer; + z-index: 11; `; +const moveControlIcon = ControlIcons.MOVE_CONTROL({ + width: theme.fontSizes[CONTROL_THEME_FONTSIZE_INDEX], + height: theme.fontSizes[CONTROL_THEME_FONTSIZE_INDEX], +}); + +const deleteControlIcon = ControlIcons.DELETE_CONTROL({ + width: theme.fontSizes[CONTROL_THEME_FONTSIZE_INDEX], + height: theme.fontSizes[CONTROL_THEME_FONTSIZE_INDEX], +}); + type DraggableComponentProps = WidgetProps & ContainerProps; +export const ResizingContext: Context<{ + setIsResizing?: Function; +}> = createContext({}); + const DraggableComponent = (props: DraggableComponentProps) => { + const { isFocused, setFocus } = useContext(FocusContext); + const { updateWidget } = useContext(WidgetFunctionsContext); + const [isResizing, setIsResizing] = useState(false); const deleteWidget = () => { - props.updateWidget && - props.updateWidget(WidgetOperations.DELETE, props.widgetId); + updateWidget && + updateWidget(WidgetOperations.DELETE, props.widgetId, { + parentId: props.parentId, + }); }; const [{ isDragging }, drag, preview] = useDrag({ item: props, @@ -32,11 +71,19 @@ const DraggableComponent = (props: DraggableComponentProps) => { isDragging: monitor.isDragging(), }), }); + return ( - + -
{ + if (setFocus) { + setFocus(props.widgetId); + e.stopPropagation(); + } + }} + show={props.widgetId === isFocused && !isResizing} style={{ display: isDragging ? "none" : "flex", flexDirection: "column", @@ -47,17 +94,21 @@ const DraggableComponent = (props: DraggableComponentProps) => { top: props.style ? props.style.yPosition + props.style.yPositionUnit : 0, + minWidth: + props.style.componentWidth + (props.style.widthUnit || "px"), + minHeight: + props.style.componentHeight + (props.style.heightUnit || "px"), }} > - - + + {moveControlIcon} - - + + {deleteControlIcon} {props.children} -
-
+ + ); }; diff --git a/app/client/src/editorComponents/DropTargetComponent.tsx b/app/client/src/editorComponents/DropTargetComponent.tsx index b6285d1714..c3d96b7881 100644 --- a/app/client/src/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/editorComponents/DropTargetComponent.tsx @@ -1,14 +1,13 @@ -import React, { useState } from "react"; +import React, { useState, useContext } from "react"; import { WidgetProps } from "../widgets/BaseWidget"; import { OccupiedSpace } from "../widgets/ContainerWidget"; import { WidgetConfigProps } from "../reducers/entityReducers/widgetConfigReducer"; import { useDrop, XYCoord } from "react-dnd"; import { ContainerProps } from "./ContainerComponent"; import WidgetFactory from "../utils/WidgetFactory"; - import { widgetOperationParams, noCollision } from "../utils/WidgetPropsUtils"; import DragLayerComponent from "./DragLayerComponent"; -import DropTargetMask from "./DropTargetMask"; +import { WidgetFunctionsContext } from "../pages/Editor"; type DropTargetComponentProps = ContainerProps & { updateWidget?: Function; @@ -29,15 +28,15 @@ type DropTargetBounds = { export const DropTargetComponent = (props: DropTargetComponentProps) => { // Hook to keep the offset of the drop target container in state const [dropTargetOffset, setDropTargetOffset] = useState({ x: 0, y: 0 }); - + const { updateWidget } = useContext(WidgetFunctionsContext); // Make this component a drop target - const [{ isOver }, drop] = useDrop({ + const [{ isOver, isExactlyOver }, drop] = useDrop({ accept: Object.values(WidgetFactory.getWidgetTypes()), drop(widget: WidgetProps & Partial, monitor) { // Make sure we're dropping in this container. - if (isOver && monitor.canDrop()) { - props.updateWidget && - props.updateWidget( + if (isOver) { + updateWidget && + updateWidget( ...widgetOperationParams( widget, monitor.getClientOffset() as XYCoord, @@ -52,13 +51,18 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => { }, // Collect isOver for ui transforms when hovering over this component collect: monitor => ({ - isOver: !!monitor.isOver({ shallow: true }), + isOver: + (monitor.isOver({ shallow: true }) && + props.widgetId !== monitor.getItem().widgetId) || + (monitor.isOver() && props.widgetId !== monitor.getItem().widgetId), + isExactlyOver: monitor.isOver({ shallow: true }), }), // Only allow drop if the drag object is directly over this component // As opposed to the drag object being over a child component, or outside the component bounds // Also only if the dropzone does not overlap any existing children canDrop: (widget, monitor) => { - if (monitor.isOver({ shallow: true })) { + // Check if the draggable is the same as the dropTarget + if (isOver) { return noCollision( monitor.getClientOffset() as XYCoord, props.snapColumnSpace, @@ -84,27 +88,31 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => { return (
- + {props.children}
); diff --git a/app/client/src/editorComponents/DropTargetMask.tsx b/app/client/src/editorComponents/DropTargetMask.tsx index dba29bf90e..fe0b07c986 100644 --- a/app/client/src/editorComponents/DropTargetMask.tsx +++ b/app/client/src/editorComponents/DropTargetMask.tsx @@ -1,10 +1,11 @@ import React, { useLayoutEffect, MutableRefObject } from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; type DropTargetMaskProps = { rowHeight: number; columnWidth: number; setBounds: Function; + showGrid: boolean; }; export const DropTargetMaskWrapper = styled.div` @@ -16,13 +17,19 @@ export const DropTargetMaskWrapper = styled.div` width: 100%; height: 100%; background: white; - background-image: radial-gradient( - circle, - ${props => props.theme.colors.grid} 2px, - transparent 0 - ); - background-size: ${props => props.columnWidth}px ${props => props.rowHeight}px; - background-position: -50% -50%; + ${props => + props.showGrid && + css` + background-image: radial-gradient( + circle, + ${props => props.theme.colors.grid} 2px, + transparent 0 + ); + background-size: ${props => props.columnWidth}px + ${props => props.rowHeight}px; + background-position: -${props => props.columnWidth / 2}px -${props => + props.rowHeight / 2}px; + `} `; /* eslint-disable react/display-name */ export const DropTargetMask = (props: DropTargetMaskProps) => { @@ -38,7 +45,6 @@ export const DropTargetMask = (props: DropTargetMaskProps) => { props.setBounds(rect); } }); - return ; }; diff --git a/app/client/src/editorComponents/Dropzone.tsx b/app/client/src/editorComponents/Dropzone.tsx index c52da957ac..8a49b268b8 100644 --- a/app/client/src/editorComponents/Dropzone.tsx +++ b/app/client/src/editorComponents/Dropzone.tsx @@ -2,10 +2,11 @@ import React from "react"; import { XYCoord } from "react-dnd"; import styled from "styled-components"; import { snapToGrid } from "../utils/helpers"; +import { theme } from "../constants/DefaultTheme"; const DropZoneWrapper = styled.div` position: absolute; - z-index: 100; + z-index: 10; background: ${props => props.theme.colors.hover}; border: 1px dashed ${props => props.theme.colors.textAnchor}; opacity: 0.7; @@ -57,7 +58,7 @@ export const DropZone = (props: DropZoneProps) => { width: wrapperProps.width + "px", top: wrapperProps.top + "px", height: wrapperProps.height + "px", - background: props.canDrop ? "blue" : "red", + background: props.canDrop ? theme.colors.hover : theme.colors.error, }} /> ); diff --git a/app/client/src/editorComponents/ResizableComponent.tsx b/app/client/src/editorComponents/ResizableComponent.tsx index 5d3bdd5d5b..1ceaab6679 100644 --- a/app/client/src/editorComponents/ResizableComponent.tsx +++ b/app/client/src/editorComponents/ResizableComponent.tsx @@ -1,46 +1,94 @@ -import React from "react"; +import React, { useContext } from "react"; import styled from "styled-components"; -import { Icon } from "@blueprintjs/core"; -import { Resizable, ResizeDirection } from "re-resizable"; +import { Rnd } from "react-rnd"; +import { XYCoord } from "react-dnd"; import { WidgetProps, WidgetOperations } from "../widgets/BaseWidget"; -import { ContainerProps } from "./ContainerComponent"; +import { ContainerProps, ParentBoundsContext } from "./ContainerComponent"; +import { ResizingContext } from "./DraggableComponent"; +import { WidgetFunctionsContext } from "../pages/Editor"; export type ResizableComponentProps = WidgetProps & ContainerProps; -const ResizableContainer = styled(Resizable)` +const ResizableContainer = styled(Rnd)` + position: relative; + z-index: 10; border: ${props => { return Object.values(props.theme.borders[0]).join(" "); }}; + &:after, + &:before { + content: ""; + position: absolute; + width: ${props => props.theme.spaces[2]}px; + height: ${props => props.theme.spaces[2]}px; + border-radius: ${props => props.theme.radii[5]}%; + z-index: 9; + background: ${props => props.theme.colors.containerBorder}; + } + &:after { + right: -${props => props.theme.spaces[1]}px; + top: calc(50% - ${props => props.theme.spaces[1]}px); + } + + &:before { + left: calc(50% - ${props => props.theme.spaces[1]}px); + bottom: -${props => props.theme.spaces[1]}px; + } `; -const CustomHandle = (props: any) =>
; -const BottomRightHandle = () => ( - - - -); - export const ResizableComponent = (props: ResizableComponentProps) => { + const { setIsResizing } = useContext(ResizingContext); + const { boundingParent } = useContext(ParentBoundsContext); + const { updateWidget } = useContext(WidgetFunctionsContext); + let bounds = "body"; + if (boundingParent && boundingParent.current) { + bounds = "." + boundingParent.current.className.split(" ")[1]; + } const updateSize = ( e: Event, - dir: ResizeDirection, + dir: any, ref: any, delta: { width: number; height: number }, + position: XYCoord, ) => { - props.updateWidget && - props.updateWidget(WidgetOperations.RESIZE, props.widgetId, delta); + setIsResizing && setIsResizing(false); + const leftColumn = props.leftColumn + position.x / props.parentColumnSpace; + const topRow = props.topRow + position.y / props.parentRowSpace; + + const rightColumn = + props.rightColumn + (delta.width + position.x) / props.parentColumnSpace; + const bottomRow = + props.bottomRow + (delta.height + position.y) / props.parentRowSpace; + + updateWidget && + updateWidget(WidgetOperations.RESIZE, props.widgetId, { + leftColumn, + rightColumn, + topRow, + bottomRow, + }); }; return ( }} onResizeStop={updateSize} - grid={[props.parentColumnSpace, props.parentRowSpace]} - enable={{ + onResizeStart={() => { + setIsResizing && setIsResizing(true); + }} + resizeGrid={[props.parentColumnSpace, props.parentRowSpace]} + bounds={bounds} + enableResizing={{ top: true, right: true, bottom: true, @@ -48,7 +96,7 @@ export const ResizableComponent = (props: ResizableComponentProps) => { topRight: false, topLeft: false, bottomRight: true, - bottomLeft: true, + bottomLeft: false, }} > {props.children} diff --git a/app/client/src/icons/ControlIcons.tsx b/app/client/src/icons/ControlIcons.tsx new file mode 100644 index 0000000000..b69acedcd5 --- /dev/null +++ b/app/client/src/icons/ControlIcons.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import { IconProps, IconWrapper } from "../constants/IconConstants"; +import { ReactComponent as DeleteIcon } from "../assets/icons/control/delete.svg"; +import { ReactComponent as MoveIcon } from "../assets/icons/control/move.svg"; + +/* eslint-disable react/display-name */ + +export const ControlIcons: { + [id: string]: Function; +} = { + DELETE_CONTROL: (props: IconProps) => ( + + + + ), + MOVE_CONTROL: (props: IconProps) => ( + + + + ), +}; diff --git a/app/client/src/mockResponses/WidgetCardsPaneResponse.tsx b/app/client/src/mockResponses/WidgetCardsPaneResponse.tsx index bfd64b112d..eb7239fdad 100644 --- a/app/client/src/mockResponses/WidgetCardsPaneResponse.tsx +++ b/app/client/src/mockResponses/WidgetCardsPaneResponse.tsx @@ -1,99 +1,99 @@ -import { WidgetCardsPaneReduxState } from "../reducers/uiReducers/widgetCardsPaneReducer"; +import { WidgetCardProps } from "../widgets/BaseWidget"; import { generateReactKey } from "../utils/generators"; -const WidgetCardsPaneResponse: WidgetCardsPaneReduxState = { - cards: { - common: [ - { - type: "TEXT_WIDGET", - icon: "icon-text", - widgetCardName: "Text", - key: generateReactKey(), - }, - { - type: "BUTTON_WIDGET", - icon: "icon-button", - widgetCardName: "Button", - key: generateReactKey(), - }, - { - type: "SPINNER_WIDGET", - icon: "icon-switch", - widgetCardName: "Spinner", - key: generateReactKey(), - }, - { - type: "CONTAINER_WIDGET", - icon: "icon-container", - widgetCardName: "Container", - key: generateReactKey(), - }, - ], - form: [ - { - type: "BUTTON_WIDGET", - icon: "icon-button", - widgetCardName: "Button", - key: generateReactKey(), - }, - { - type: "BUTTON_WIDGET", - icon: "icon-button", - widgetCardName: "Button", - key: generateReactKey(), - }, - { - type: "DROP_DOWN_WIDGET", - icon: "icon-dropdown", - widgetCardName: "Dropdown", - key: generateReactKey(), - }, - { - type: "DATE_PICKER_WIDGET", - icon: "icon-datepicker", - widgetCardName: "DatePicker", - key: generateReactKey(), - }, - { - type: "RADIO_GROUP_WIDGET", - icon: "icon-radio", - widgetCardName: "Radio Button", - key: generateReactKey(), - }, - { - type: "SWITCH_WIDGET", - icon: "icon-switch", - widgetCardName: "Toggle", - key: generateReactKey(), - }, - ], - view: [ - { - type: "TEXT_WIDGET", - icon: "icon-text", - widgetCardName: "Text", - key: generateReactKey(), - }, - { - type: "CONTAINER_WIDGET", - icon: "icon-container", - widgetCardName: "Container", - key: generateReactKey(), - }, - { - type: "SPINNER_WIDGET", - icon: "icon-spinner", - widgetCardName: "Spinner", - key: generateReactKey(), - }, - { - type: "TABLE_WIDGET", - icon: "icon-table", - widgetCardName: "Table", - key: generateReactKey(), - }, - ], - }, +const WidgetCardsPaneResponse: { + [id: string]: WidgetCardProps[]; +} = { + common: [ + { + type: "TEXT_WIDGET", + icon: "icon-text", + widgetCardName: "Text", + key: generateReactKey(), + }, + { + type: "BUTTON_WIDGET", + icon: "icon-button", + widgetCardName: "Button", + key: generateReactKey(), + }, + { + type: "SPINNER_WIDGET", + icon: "icon-switch", + widgetCardName: "Spinner", + key: generateReactKey(), + }, + { + type: "CONTAINER_WIDGET", + icon: "icon-container", + widgetCardName: "Container", + key: generateReactKey(), + }, + ], + form: [ + { + type: "BUTTON_WIDGET", + icon: "icon-button", + widgetCardName: "Button", + key: generateReactKey(), + }, + { + type: "BUTTON_WIDGET", + icon: "icon-button", + widgetCardName: "Button", + key: generateReactKey(), + }, + { + type: "DROP_DOWN_WIDGET", + icon: "icon-dropdown", + widgetCardName: "Dropdown", + key: generateReactKey(), + }, + { + type: "DATE_PICKER_WIDGET", + icon: "icon-datepicker", + widgetCardName: "DatePicker", + key: generateReactKey(), + }, + { + type: "RADIO_GROUP_WIDGET", + icon: "icon-radio", + widgetCardName: "Radio Button", + key: generateReactKey(), + }, + { + type: "SWITCH_WIDGET", + icon: "icon-switch", + widgetCardName: "Toggle", + key: generateReactKey(), + }, + ], + view: [ + { + type: "TEXT_WIDGET", + icon: "icon-text", + widgetCardName: "Text", + key: generateReactKey(), + }, + { + type: "CONTAINER_WIDGET", + icon: "icon-container", + widgetCardName: "Container", + key: generateReactKey(), + }, + { + type: "SPINNER_WIDGET", + icon: "icon-spinner", + widgetCardName: "Spinner", + key: generateReactKey(), + }, + { + type: "TABLE_WIDGET", + icon: "icon-table", + widgetCardName: "Table", + key: generateReactKey(), + }, + ], }; export default WidgetCardsPaneResponse; diff --git a/app/client/src/mockResponses/WidgetConfigResponse.tsx b/app/client/src/mockResponses/WidgetConfigResponse.tsx index efff9f0c9f..f63cf16dd6 100644 --- a/app/client/src/mockResponses/WidgetConfigResponse.tsx +++ b/app/client/src/mockResponses/WidgetConfigResponse.tsx @@ -7,12 +7,14 @@ const WidgetConfigResponse: WidgetConfigReducerState = { buttonStyle: "PRIMARY_BUTTON", rows: 1, columns: 2, + widgetName: "Button", }, TEXT_WIDGET: { text: "Not all labels are bad!", textStyle: "LABEL", rows: 1, columns: 3, + widgetName: "Text", }, IMAGE_WIDGET: { defaultImage: "", @@ -20,27 +22,32 @@ const WidgetConfigResponse: WidgetConfigReducerState = { image: "", rows: 3, columns: 3, + widgetName: "Image", }, INPUT_WIDGET: { inputType: "TEXT", label: "Label me", rows: 1, columns: 3, + widgetName: "Input", }, SWITCH_WIDGET: { isOn: false, label: "Turn me on", rows: 1, columns: 4, + widgetName: "Switch", }, CONTAINER_WIDGET: { backgroundColor: "#FFFFFF", rows: 1, columns: 4, + widgetName: "Container", }, SPINNER_WIDGET: { rows: 1, columns: 1, + widgetName: "Spinner", }, DATE_PICKER_WIDGET: { enableTime: false, @@ -48,23 +55,27 @@ const WidgetConfigResponse: WidgetConfigReducerState = { rows: 1, columns: 3, label: "Date", + widgetName: "Datepicker", }, TABLE_WIDGET: { rows: 5, columns: 7, label: "Don't table me!", + widgetName: "Table", }, DROP_DOWN_WIDGET: { rows: 1, columns: 3, selectionType: "SINGLE_SELECT", label: "Pick me!", + widgetName: "Dropdown", }, CHECKBOX_WIDGET: { rows: 1, columns: 3, label: "Label - CHECK!", defaultCheckedState: true, + widgetName: "Checkbox", }, RADIO_GROUP_WIDGET: { rows: 3, @@ -76,6 +87,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { { label: "Charlie", value: "3" }, ], defaultOptionValue: "1", + widgetName: "RadioGroup", }, ALERT_WIDGET: { alertType: "NOTIFICATION", @@ -84,6 +96,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { columns: 3, header: "", message: "", + widgetName: "Alert", }, }, configVersion: 1, diff --git a/app/client/src/pages/Editor/Canvas.tsx b/app/client/src/pages/Editor/Canvas.tsx index 0b7f414dee..a5e9350d9a 100644 --- a/app/client/src/pages/Editor/Canvas.tsx +++ b/app/client/src/pages/Editor/Canvas.tsx @@ -1,8 +1,13 @@ -import React from "react"; +import React, { + createContext, + useState, + Context, + Dispatch, + SetStateAction, +} from "react"; import styled from "styled-components"; import WidgetFactory from "../../utils/WidgetFactory"; import { RenderModes } from "../../constants/WidgetConstants"; -import { WidgetFunctions } from "../../widgets/BaseWidget"; import { ContainerWidgetProps } from "../../widgets/ContainerWidget"; import { WidgetProps } from "../../widgets/BaseWidget"; @@ -15,21 +20,22 @@ const ArtBoard = styled.div` interface CanvasProps { dsl: ContainerWidgetProps; - widgetFunctions: WidgetFunctions; } +export const FocusContext: Context<{ + isFocused?: string; + setFocus?: Dispatch>; +}> = createContext({}); + const Canvas = (props: CanvasProps) => { + const [isFocused, setFocus] = useState(""); return ( - + {props.dsl.widgetId && - WidgetFactory.createWidget( - props.dsl, - props.widgetFunctions, - RenderModes.CANVAS, - )} + WidgetFactory.createWidget(props.dsl, RenderModes.CANVAS)} - + ); }; diff --git a/app/client/src/pages/Editor/EditorHeader.tsx b/app/client/src/pages/Editor/EditorHeader.tsx index ed47517185..bb3a96a1a5 100644 --- a/app/client/src/pages/Editor/EditorHeader.tsx +++ b/app/client/src/pages/Editor/EditorHeader.tsx @@ -1,35 +1,57 @@ -import React, { Component } from "react"; +import React from "react"; import styled from "styled-components"; -// import { connect } from "react-redux"; -// import { AppState } from "../../reducers"; -// import { EditorHeaderReduxState } from "../../reducers/uiReducers/editorHeaderReducer"; +import { Breadcrumbs, IBreadcrumbProps, Spinner } from "@blueprintjs/core"; const Header = styled.header` + display: flex; + justify-content: space-around; + align-items: center; height: 50px; + padding: 0px 30px; box-shadow: 0px 0px 3px #ccc; background: #fff; + font-size: ${props => props.theme.fontSizes[1]}px; `; -class EditorHeader extends Component { - render() { - return
; +const NotificationText = styled.div` + display: flex; + justify-content: space-evenly; + align-items: center; + flex-grow: 1; +`; + +const StretchedBreadCrumb = styled(Breadcrumbs)` + flex-grow: 10; + * { + font-family: ${props => props.theme.fonts[0]}; + font-size: ${props => props.theme.fontSizes[2]}px; } -} + li:after { + background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.71 7.29l-4-4a1.003 1.003 0 0 0-1.42 1.42L8.59 8 5.3 11.29c-.19.18-.3.43-.3.71a1.003 1.003 0 0 0 1.71.71l4-4c.18-.18.29-.43.29-.71 0-.28-.11-.53-.29-.71z' fill='rgba(92,112,128,1)'/%3E%3C/svg%3E"); + } +`; + +type EditorHeaderProps = { + notificationText?: string; + pageName: string; +}; + +export const EditorHeader = (props: EditorHeaderProps) => { + const navigation: IBreadcrumbProps[] = [ + { href: "#", icon: "folder-close", text: "appsmith-dev" }, + { href: "#", icon: "folder-close", text: "application" }, + { icon: "page-layout", text: props.pageName, current: true }, + ]; + + return ( +
+ + + {props.notificationText && } + {props.notificationText} + +
+ ); +}; export default EditorHeader; - -// const mapStateToProps = ( -// state: AppState, -// props: any, -// ): EditorHeaderReduxState => { -// return state; -// }; - -// const mapDispatchToProps = (dispatch: any) => { -// return {}; -// }; - -// export default connect( -// mapStateToProps, -// mapDispatchToProps, -// )(EditorHeader); diff --git a/app/client/src/pages/Editor/WidgetCard.tsx b/app/client/src/pages/Editor/WidgetCard.tsx index 4ef4f75858..ee1b8a6dec 100644 --- a/app/client/src/pages/Editor/WidgetCard.tsx +++ b/app/client/src/pages/Editor/WidgetCard.tsx @@ -29,6 +29,9 @@ export const Wrapper = styled.div` path { fill: ${props => props.theme.colors.textDefault}; } + rect { + stroke: ${props => props.theme.colors.textDefault}; + } } } & i { diff --git a/app/client/src/pages/Editor/WidgetCardsPane.tsx b/app/client/src/pages/Editor/WidgetCardsPane.tsx index 6e7652af77..5c4a048979 100644 --- a/app/client/src/pages/Editor/WidgetCardsPane.tsx +++ b/app/client/src/pages/Editor/WidgetCardsPane.tsx @@ -19,7 +19,7 @@ const CardsPaneWrapper = styled.div` const CardsWrapper = styled.div` display: grid; grid-template-columns: 1fr 1fr 1fr; - grid-gap: ${props => props.theme.spaces[2]}px; + grid-gap: ${props => props.theme.spaces[1]}px; justify-items: stretch; align-items: stretch; `; diff --git a/app/client/src/pages/Editor/index.tsx b/app/client/src/pages/Editor/index.tsx index 68390dbf71..086ad989b5 100644 --- a/app/client/src/pages/Editor/index.tsx +++ b/app/client/src/pages/Editor/index.tsx @@ -1,5 +1,4 @@ -import React, { Component } from "react"; -import { Position, Toaster } from "@blueprintjs/core"; +import React, { Component, Context, createContext } from "react"; import { connect } from "react-redux"; import styled from "styled-components"; import Canvas from "./Canvas"; @@ -7,6 +6,7 @@ import { WidgetCardProps, WidgetProps, WidgetOperation, + WidgetFunctions, } from "../../widgets/BaseWidget"; import { AppState } from "../../reducers"; import { EditorReduxState } from "../../reducers/uiReducers/editorReducer"; @@ -20,10 +20,6 @@ import { executeAction } from "../../actions/widgetActions"; import { ActionPayload } from "../../constants/ActionConstants"; import PropertyPane from "./PropertyPane"; -const SaveToast = Toaster.create({ - position: Position.TOP, -}); - const CanvasContainer = styled.section` height: 100%; width: 100%; @@ -37,7 +33,7 @@ const CanvasContainer = styled.section` right: 0; bottom: 0; left: 0; - z-index: 1000; + z-index: 11; pointer-events: none; } `; @@ -60,50 +56,41 @@ type EditorProps = { updateWidget: Function; cards: { [id: string]: WidgetCardProps[] } | any; savePageLayout: Function; - page: string; + currentPageName: string; currentPageId: string; currentLayoutId: string; isSaving: boolean; }; +export const WidgetFunctionsContext: Context = createContext( + {}, +); + class Editor extends Component { componentDidMount() { this.props.fetchCanvasWidgets(this.props.currentPageId); } - componentDidUpdate(prevProps: EditorProps) { - if (this.props.isSaving && prevProps.isSaving !== this.props.isSaving) { - SaveToast.clear(); - SaveToast.show({ message: "Saving Page..." }); - } else if ( - !this.props.isSaving && - prevProps.isSaving !== this.props.isSaving - ) { - SaveToast.clear(); - SaveToast.show({ message: "Page Saved" }); - } - } - public render() { return ( - - + + - {this.props.dsl && ( - - )} + {this.props.dsl && } - + ); } } @@ -120,14 +107,14 @@ const mapStateToProps = (state: AppState): EditorReduxState => { state.ui.editor.pageWidgetId, state.entities, ); - const configs = state.entities.widgetConfig.config; - const cards = state.ui.widgetCardsPane.cards; - Object.keys(cards).forEach((group: string) => { - cards[group] = cards[group].map((widget: WidgetCardProps) => ({ - ...widget, - ...configs[widget.type], - })); + const cards = state.ui.editor.cards; + const groups: string[] = Object.keys(cards); + groups.forEach((group: string) => { + cards[group] = cards[group].map((widget: WidgetCardProps) => { + const { rows, columns } = state.entities.widgetConfig.config[widget.type]; + return { ...widget, rows, columns }; + }); }); return { @@ -136,6 +123,7 @@ const mapStateToProps = (state: AppState): EditorReduxState => { pageWidgetId: state.ui.editor.pageWidgetId, currentPageId: state.ui.editor.currentPageId, currentLayoutId: state.ui.editor.currentLayoutId, + currentPageName: state.ui.editor.currentPageName, isSaving: state.ui.editor.isSaving, }; }; diff --git a/app/client/src/reducers/index.tsx b/app/client/src/reducers/index.tsx index 4483e76eca..0b703388e1 100644 --- a/app/client/src/reducers/index.tsx +++ b/app/client/src/reducers/index.tsx @@ -2,8 +2,8 @@ import { combineReducers } from "redux"; import entityReducer from "./entityReducers"; import uiReducer from "./uiReducers"; import { CanvasWidgetsReduxState } from "./entityReducers/canvasWidgetsReducer"; -import { WidgetCardsPaneReduxState } from "./uiReducers/widgetCardsPaneReducer"; import { EditorReduxState } from "./uiReducers/editorReducer"; +import { ErrorReduxState } from "./uiReducers/errorReducer"; import { APIDataState } from "./entityReducers/apiDataReducer"; import { QueryDataState } from "./entityReducers/queryDataReducer"; import { ActionDataState } from "./entityReducers/actionsReducer"; @@ -20,9 +20,9 @@ export default appReducer; export interface AppState { ui: { - widgetCardsPane: WidgetCardsPaneReduxState; editor: EditorReduxState; propertyPane: PropertyPaneReduxState; + errors: ErrorReduxState; }; entities: { canvasWidgets: CanvasWidgetsReduxState; diff --git a/app/client/src/reducers/uiReducers/editorHeaderReducer.tsx b/app/client/src/reducers/uiReducers/editorHeaderReducer.tsx deleted file mode 100644 index 76896fb8cf..0000000000 --- a/app/client/src/reducers/uiReducers/editorHeaderReducer.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { createReducer } from "../../utils/AppsmithUtils"; - -const initialState = {}; - -const editorHeaderReducer = createReducer(initialState, {}); - -// export interface EditorHeaderReduxState {} - -export default editorHeaderReducer; diff --git a/app/client/src/reducers/uiReducers/editorReducer.tsx b/app/client/src/reducers/uiReducers/editorReducer.tsx index 693a4d24c7..952d322032 100644 --- a/app/client/src/reducers/uiReducers/editorReducer.tsx +++ b/app/client/src/reducers/uiReducers/editorReducer.tsx @@ -7,15 +7,18 @@ import { } from "../../constants/ReduxActionConstants"; import { WidgetCardProps, WidgetProps } from "../../widgets/BaseWidget"; import { ContainerWidgetProps } from "../../widgets/ContainerWidget"; +import WidgetCardsPaneResponse from "../../mockResponses/WidgetCardsPaneResponse"; + const editorConfigs = getEditorConfigs(); const initialState: EditorReduxState = { pageWidgetId: "0", ...editorConfigs, isSaving: false, + cards: WidgetCardsPaneResponse, }; const editorReducer = createReducer(initialState, { - [ReduxActionTypes.SUCCESS_FETCHING_WIDGET_CARDS]: ( + [ReduxActionTypes.FETCH_WIDGET_CARDS_SUCCESS]: ( state: EditorReduxState, action: ReduxAction, ) => { @@ -27,19 +30,17 @@ const editorReducer = createReducer(initialState, { [ReduxActionTypes.SAVE_PAGE_SUCCESS]: (state: EditorReduxState) => { return { ...state, isSaving: false }; }, - [ReduxActionTypes.SAVE_PAGE_ERROR]: (state: EditorReduxState) => { - return { ...state, isSaving: false }; - }, }); export interface EditorReduxState { dsl?: ContainerWidgetProps; - cards?: { + cards: { [id: string]: WidgetCardProps[]; }; pageWidgetId: string; currentPageId: string; currentLayoutId: string; + currentPageName: string; isSaving: boolean; } diff --git a/app/client/src/reducers/uiReducers/errorReducer.tsx b/app/client/src/reducers/uiReducers/errorReducer.tsx new file mode 100644 index 0000000000..3cb81b3473 --- /dev/null +++ b/app/client/src/reducers/uiReducers/errorReducer.tsx @@ -0,0 +1,31 @@ +import { createReducer } from "../../utils/AppsmithUtils"; +import { + ReduxAction, + ReduxActionTypes, + ReduxActionErrorPayload, +} from "../../constants/ReduxActionConstants"; + +const initialState: ErrorReduxState = { sourceAction: "", message: "" }; + +const errorReducer = createReducer(initialState, { + [ReduxActionTypes.REPORT_ERROR]: ( + state: ErrorReduxState, + action: ReduxAction, + ) => { + return { + sourceAction: action.payload.source, + message: action.payload.message, + }; + }, + [ReduxActionTypes.FLUSH_ERRORS]: () => { + return {}; + }, +}); + +export interface ErrorReduxState { + // Expiration? + sourceAction?: string; + message?: string; +} + +export default errorReducer; diff --git a/app/client/src/reducers/uiReducers/index.tsx b/app/client/src/reducers/uiReducers/index.tsx index d8312d0961..73b606e0f0 100644 --- a/app/client/src/reducers/uiReducers/index.tsx +++ b/app/client/src/reducers/uiReducers/index.tsx @@ -1,13 +1,11 @@ import { combineReducers } from "redux"; -import widgetCardsPaneReducer from "./widgetCardsPaneReducer"; -import editorHeaderReducer from "./editorHeaderReducer"; import editorReducer from "./editorReducer"; +import errorReducer from "./errorReducer"; import propertyPaneReducer from "./propertyPaneReducer"; const uiReducer = combineReducers({ - widgetCardsPane: widgetCardsPaneReducer, - editorHeader: editorHeaderReducer, editor: editorReducer, + errors: errorReducer, propertyPane: propertyPaneReducer, }); export default uiReducer; diff --git a/app/client/src/reducers/uiReducers/widgetCardsPaneReducer.tsx b/app/client/src/reducers/uiReducers/widgetCardsPaneReducer.tsx deleted file mode 100644 index 42dd6c5b9e..0000000000 --- a/app/client/src/reducers/uiReducers/widgetCardsPaneReducer.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { createReducer } from "../../utils/AppsmithUtils"; -import { - ReduxActionTypes, - ReduxAction, - LoadWidgetCardsPanePayload, -} from "../../constants/ReduxActionConstants"; -import { WidgetCardProps } from "../../widgets/BaseWidget"; -import WidgetCardsPaneResponse from "../../mockResponses/WidgetCardsPaneResponse"; - -const initialState: WidgetCardsPaneReduxState = WidgetCardsPaneResponse; - -const widgetCardsPaneReducer = createReducer(initialState, { - [ReduxActionTypes.ERROR_FETCHING_WIDGET_CARDS]: ( - state: WidgetCardsPaneReduxState, - action: ReduxAction, - ) => { - return { cards: action.payload.cards }; - }, -}); - -export interface WidgetCardsPaneReduxState { - cards: { - [id: string]: WidgetCardProps[]; - }; -} - -export default widgetCardsPaneReducer; diff --git a/app/client/src/sagas/ErrorSagas.tsx b/app/client/src/sagas/ErrorSagas.tsx new file mode 100644 index 0000000000..754999101f --- /dev/null +++ b/app/client/src/sagas/ErrorSagas.tsx @@ -0,0 +1,39 @@ +import { + ReduxActionTypes, + ReduxActionErrorTypes, + ReduxAction, +} from "../constants/ReduxActionConstants"; + +import { ApiResponse } from "../api/ApiResponses"; +import { put, takeLatest } from "redux-saga/effects"; + +export function* validateResponse(response: ApiResponse) { + if (response.responseMeta.success) { + return true; + } else { + yield put({ + type: ReduxActionErrorTypes.API_ERROR, + payload: { + error: response.responseMeta.error, + }, + }); + return false; + } +} + +export function* errorSaga(errorAction: ReduxAction<{ error: any }>) { + // Just a pass through for now. + // Add procedures to customize errors here + console.log(errorAction.payload.error); + yield put({ + type: ReduxActionTypes.REPORT_ERROR, + payload: { + message: errorAction.payload.error, + source: errorAction.type, + }, + }); +} + +export default function* errorSagas() { + yield takeLatest(Object.values(ReduxActionErrorTypes), errorSaga); +} diff --git a/app/client/src/sagas/PageSagas.tsx b/app/client/src/sagas/PageSagas.tsx index 8c1a440852..e63ef3918f 100644 --- a/app/client/src/sagas/PageSagas.tsx +++ b/app/client/src/sagas/PageSagas.tsx @@ -1,15 +1,11 @@ import CanvasWidgetsNormalizer from "../normalizers/CanvasWidgetsNormalizer"; import { ReduxActionTypes, + ReduxActionErrorTypes, ReduxAction, UpdateCanvasPayload, } from "../constants/ReduxActionConstants"; -import { - updateCanvas, - savePageError, - savePageSuccess, - fetchPageError, -} from "../actions/pageActions"; +import { updateCanvas, savePageSuccess } from "../actions/pageActions"; import PageApi, { FetchPageResponse, SavePageResponse, @@ -25,27 +21,31 @@ import { takeEvery, all, } from "redux-saga/effects"; + import { extractCurrentDSL } from "../utils/WidgetPropsUtils"; import { getEditorConfigs } from "./selectors"; +import { validateResponse } from "./ErrorSagas"; export function* fetchPageSaga( pageRequestAction: ReduxAction, ) { - const pageRequest = pageRequestAction.payload; try { + const pageRequest = pageRequestAction.payload; const fetchPageResponse: FetchPageResponse = yield call( PageApi.fetchPage, pageRequest, ); - - if (fetchPageResponse.responseMeta.success) { + const isValidResponse = yield validateResponse(fetchPageResponse); + if (isValidResponse) { const normalizedResponse = CanvasWidgetsNormalizer.normalize( extractCurrentDSL(fetchPageResponse), ); const canvasWidgetsPayload: UpdateCanvasPayload = { pageWidgetId: normalizedResponse.result, + currentPageName: fetchPageResponse.data.name, + currentPageId: fetchPageResponse.data.id, widgets: normalizedResponse.entities.canvasWidgets, - layoutId: fetchPageResponse.data.layouts[0].id, // TODO(abhinav): Handle for multiple layouts + currentLayoutId: fetchPageResponse.data.layouts[0].id, // TODO(abhinav): Handle for multiple layouts }; yield all([ put(updateCanvas(canvasWidgetsPayload)), @@ -56,8 +56,12 @@ export function* fetchPageSaga( ]); } } catch (error) { - console.log(error); - yield put(fetchPageError(error)); + yield put({ + type: ReduxActionErrorTypes.FETCH_PAGE_ERROR, + payload: { + error, + }, + }); } } @@ -68,10 +72,17 @@ export function* savePageSaga(savePageAction: ReduxAction) { PageApi.savePage, savePageRequest, ); - yield put(savePageSuccess(savePageResponse)); - } catch (err) { - console.log(err); - yield put(savePageError(err)); + const isValidResponse = validateResponse(savePageResponse); + if (isValidResponse) { + yield put(savePageSuccess(savePageResponse)); + } + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.SAVE_PAGE_ERROR, + payload: { + error, + }, + }); } } @@ -87,7 +98,6 @@ export function* saveLayoutSaga( { canvasWidgets: widgets }, ); const editorConfigs = yield select(getEditorConfigs) as any; - console.log(editorConfigs); yield put({ type: ReduxActionTypes.SAVE_PAGE_INIT, payload: { diff --git a/app/client/src/sagas/WidgetCardsPaneSagas.tsx b/app/client/src/sagas/WidgetCardsPaneSagas.tsx index d6cf112b1a..4d4a1b7f42 100644 --- a/app/client/src/sagas/WidgetCardsPaneSagas.tsx +++ b/app/client/src/sagas/WidgetCardsPaneSagas.tsx @@ -1,5 +1,7 @@ -// import CanvasWidgetsNormalizer from "../normalizers/CanvasWidgetsNormalizer" -import { ReduxActionTypes } from "../constants/ReduxActionConstants"; +import { + ReduxActionTypes, + ReduxActionErrorTypes, +} from "../constants/ReduxActionConstants"; import WidgetCardsPaneApi, { WidgetCardsPaneResponse, } from "../api/WidgetCardsPaneApi"; @@ -13,7 +15,7 @@ export function* fetchWidgetCards() { ]); yield put(successFetchingWidgetCards(widgetCards.cards)); } catch (err) { - yield put({ type: ReduxActionTypes.ERROR_FETCHING_WIDGET_CARDS, err }); + yield put({ type: ReduxActionErrorTypes.FETCH_WIDGET_CARDS_ERROR, err }); } } diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 88504f0705..d7c42a9c2d 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -1,5 +1,6 @@ import { ReduxActionTypes, + ReduxActionErrorTypes, ReduxAction, } from "../constants/ReduxActionConstants"; import { @@ -9,13 +10,13 @@ import { WidgetDelete, } from "../actions/pageActions"; import { FlattenedWidgetProps } from "../reducers/entityReducers/canvasWidgetsReducer"; -import { getWidgets, getWidget, getWidgetParent } from "./selectors"; +import { getWidgets, getWidget, getDefaultWidgetConfig } from "./selectors"; import { generateWidgetProps, - updateWidgetSize, updateWidgetPosition, } from "../utils/WidgetPropsUtils"; import { put, select, takeEvery, takeLatest, all } from "redux-saga/effects"; +import { getNextWidgetName } from "../utils/AppsmithUtils"; export function* addChildSaga(addChildAction: ReduxAction) { try { @@ -31,7 +32,7 @@ export function* addChildSaga(addChildAction: ReduxAction) { } = addChildAction.payload; const widget: FlattenedWidgetProps = yield select(getWidget, widgetId); const widgets = yield select(getWidgets); - + const defaultWidgetConfig = yield select(getDefaultWidgetConfig, type); const childWidget = generateWidgetProps( widget, type, @@ -41,6 +42,8 @@ export function* addChildSaga(addChildAction: ReduxAction) { rows, parentRowSpace, parentColumnSpace, + getNextWidgetName(defaultWidgetConfig.widgetName, widgets), + defaultWidgetConfig, ); widgets[childWidget.widgetId] = childWidget; if (widget && widget.children) { @@ -51,93 +54,112 @@ export function* addChildSaga(addChildAction: ReduxAction) { type: ReduxActionTypes.UPDATE_LAYOUT, payload: { widgets }, }); - } catch (err) { + } catch (error) { yield put({ - type: ReduxActionTypes.WIDGET_OPERATION_ERROR, - action: ReduxActionTypes.WIDGET_ADD_CHILD, - ...err, + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: ReduxActionTypes.WIDGET_ADD_CHILD, + error, + }, }); } } export function* deleteSaga(deleteAction: ReduxAction) { try { - const { widgetId } = deleteAction.payload; + const { widgetId, parentId } = deleteAction.payload; const widgets = yield select(getWidgets); - delete widgets[widgetId]; - const parent = yield select(getWidgetParent, widgetId); + const parent = yield select(getWidget, parentId); parent.children = parent.children.filter( (child: string) => child !== widgetId, ); - widgets[parent.widgetId] = parent; + delete widgets[widgetId]; + widgets[parentId] = parent; yield put({ type: ReduxActionTypes.UPDATE_LAYOUT, payload: { widgets }, }); - } catch (err) { - console.log(err); + } catch (error) { yield put({ - type: ReduxActionTypes.WIDGET_OPERATION_ERROR, - action: ReduxActionTypes.WIDGET_DELETE, - ...err, + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: ReduxActionTypes.WIDGET_DELETE, + error, + }, }); } } export function* moveSaga(moveAction: ReduxAction) { try { - const { widgetId, leftColumn, topRow, parentWidgetId } = moveAction.payload; + const { + widgetId, + leftColumn, + topRow, + parentId, + newParentId, + } = moveAction.payload; let widget: FlattenedWidgetProps = yield select(getWidget, widgetId); // Get all widgets from DSL/Redux Store const widgets = yield select(getWidgets) as any; // Get parent from DSL/Redux Store - const parent = yield select(getWidgetParent, widgetId); + const parent = yield select(getWidget, parentId); // Update position of widget widget = updateWidgetPosition(widget, leftColumn, topRow, parent); // Replace widget with update widget props widgets[widgetId] = widget; // If the parent has changed i.e parentWidgetId is not parent.widgetId - if (parent.widgetId !== parentWidgetId) { + if (parent.widgetId !== newParentId && widgetId !== newParentId) { // Remove from the previous parent parent.children = parent.children.filter( (child: string) => child !== widgetId, ); widgets[parent.widgetId] = parent; // Add to new parent - widgets[parentWidgetId].children.push(widgetId); + widgets[newParentId].children.push(widgetId); } yield put({ type: ReduxActionTypes.UPDATE_LAYOUT, payload: { widgets }, }); - } catch (err) { + } catch (error) { yield put({ - type: ReduxActionTypes.WIDGET_OPERATION_ERROR, - action: ReduxActionTypes.WIDGET_MOVE, - ...err, + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: ReduxActionTypes.WIDGET_MOVE, + error, + }, }); } } export function* resizeSaga(resizeAction: ReduxAction) { try { - const { widgetId, height, width } = resizeAction.payload; + const { + widgetId, + leftColumn, + rightColumn, + topRow, + bottomRow, + } = resizeAction.payload; let widget: FlattenedWidgetProps = yield select(getWidget, widgetId); const widgets = yield select(getWidgets); - widget = updateWidgetSize(widget, height, width); + widget = { ...widget, leftColumn, rightColumn, topRow, bottomRow }; widgets[widgetId] = widget; yield put({ type: ReduxActionTypes.UPDATE_LAYOUT, payload: { widgets }, }); - } catch (err) { + } catch (error) { yield put({ - type: ReduxActionTypes.WIDGET_OPERATION_ERROR, - action: ReduxActionTypes.WIDGET_RESIZE, - ...err, + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: ReduxActionTypes.WIDGET_RESIZE, + error, + }, }); } } diff --git a/app/client/src/sagas/index.tsx b/app/client/src/sagas/index.tsx index 5ab51c8cd7..52593510e2 100644 --- a/app/client/src/sagas/index.tsx +++ b/app/client/src/sagas/index.tsx @@ -3,6 +3,7 @@ import pageSagas from "../sagas/PageSagas"; import { fetchWidgetCardsSaga } from "./WidgetCardsPaneSagas"; import { watchExecuteActionSaga } from "./ActionSagas"; import widgetOperationSagas from "./WidgetOperationSagas"; +import errorSagas from "./ErrorSagas"; export function* rootSaga() { yield all([ @@ -10,5 +11,6 @@ export function* rootSaga() { spawn(fetchWidgetCardsSaga), spawn(watchExecuteActionSaga), spawn(widgetOperationSagas), + spawn(errorSagas), ]); } diff --git a/app/client/src/sagas/selectors.tsx b/app/client/src/sagas/selectors.tsx index a32b31a914..86fa24db4c 100644 --- a/app/client/src/sagas/selectors.tsx +++ b/app/client/src/sagas/selectors.tsx @@ -1,7 +1,7 @@ import { AppState } from "../reducers"; import { FlattenedWidgetProps } from "../reducers/entityReducers/canvasWidgetsReducer"; import { WidgetProps } from "../widgets/BaseWidget"; - +import { WidgetType } from "../constants/WidgetConstants"; export const getWidgets = ( state: AppState, ): { [widgetId: string]: FlattenedWidgetProps } => { @@ -22,16 +22,13 @@ export const getEditorConfigs = ( }; }; -export const getWidgetParent = ( +export const getDefaultWidgetConfig = ( state: AppState, - widgetId: string, -): FlattenedWidgetProps | undefined => { - const widgets = state.entities.canvasWidgets; - return Object.values(widgets).find( - (widget: FlattenedWidgetProps) => - widget && - widget.children && - widget.children.length > 0 && - widget.children.indexOf(widgetId) > -1, - ); + type: WidgetType, +): Partial => { + const configs = state.entities.widgetConfig.config; + const widgetConfig = { ...configs[type] }; + delete widgetConfig.rows; + delete widgetConfig.columns; + return widgetConfig; }; diff --git a/app/client/src/utils/AppsmithUtils.tsx b/app/client/src/utils/AppsmithUtils.tsx index af48741e0f..dbd83854b3 100644 --- a/app/client/src/utils/AppsmithUtils.tsx +++ b/app/client/src/utils/AppsmithUtils.tsx @@ -12,6 +12,7 @@ import FontFaceObserver from "fontfaceobserver"; import PropertyControlRegistry from "./PropertyControlRegistry"; import WidgetBuilderRegistry from "./WidgetRegistry"; import { Property } from "../api/ActionAPI"; +import { FlattenedWidgetProps } from "../reducers/entityReducers/canvasWidgetsReducer"; import _ from "lodash"; export const createReducer = ( @@ -60,3 +61,26 @@ export const mapToPropList = (map: Record): Property[] => { return { key: key, value: value }; }); }; + +export const getNextWidgetName = ( + prefix: string, + widgets: { + [id: string]: FlattenedWidgetProps; + }, +) => { + const regex = new RegExp(`^${prefix}(\\d+)$`); + const usedIndices: number[] = Object.values(widgets).map(widget => { + if (widget && widget.widgetName && regex.test(widget.widgetName)) { + const name = widget.widgetName || ""; + const matches = name.match(regex); + const ind = + matches && Array.isArray(matches) ? parseInt(matches[1], 10) : 0; + return Number.isNaN(ind) ? 0 : ind; + } + return 0; + }) as number[]; + + const lastIndex = Math.max(...usedIndices); + + return prefix + (lastIndex + 1); +}; diff --git a/app/client/src/utils/WidgetFactory.tsx b/app/client/src/utils/WidgetFactory.tsx index 9ca0c0adcd..bbac31556b 100644 --- a/app/client/src/utils/WidgetFactory.tsx +++ b/app/client/src/utils/WidgetFactory.tsx @@ -2,7 +2,6 @@ import { WidgetType, RenderMode } from "../constants/WidgetConstants"; import { WidgetBuilder, WidgetProps, - WidgetFunctions, WidgetDataProps, } from "../widgets/BaseWidget"; @@ -18,13 +17,11 @@ class WidgetFactory { static createWidget( widgetData: WidgetDataProps, - widgetFunctions: WidgetFunctions, renderMode: RenderMode, ): JSX.Element { const widgetProps: WidgetProps = { key: widgetData.widgetId, renderMode: renderMode, - ...widgetFunctions, ...widgetData, }; const widgetBuilder = this.widgetMap.get(widgetData.type); diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index 13966d9f3f..3e974980f8 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -57,10 +57,10 @@ export const getDropZoneOffsets = ( const areIntersecting = (r1: Rect, r2: Rect) => { return !( - r2.left > r1.right || - r2.right < r1.left || - r2.top > r1.bottom || - r2.bottom < r1.top + r2.left >= r1.right || + r2.right <= r1.left || + r2.top >= r1.bottom || + r2.bottom <= r1.top ); }; @@ -71,7 +71,10 @@ export const isDropZoneOccupied = ( ) => { if (occupied) { occupied = occupied.filter(widgetDetails => { - return widgetDetails.id !== widget.widgetId; + return ( + widgetDetails.id !== widget.widgetId && + widgetDetails.parentId !== widget.widgetId + ); }); for (let i = 0; i < occupied.length; i++) { if (areIntersecting(occupied[i], offset)) { @@ -139,7 +142,8 @@ export const widgetOperationParams = ( { leftColumn, topRow, - parentWidgetId: widgetId, + parentId: widget.parentId, + newParentId: widgetId, }, ]; // If this is not an existing widget, we'll not have the widgetId @@ -211,6 +215,8 @@ export const generateWidgetProps = ( rows: number, parentRowSpace: number, parentColumnSpace: number, + widgetName: string, + widgetConfig: Partial, ): ContainerWidgetProps => { if (parent && parent.snapColumns && parent.snapRows) { const sizes = { @@ -230,10 +236,10 @@ export const generateWidgetProps = ( }; } return { + ...widgetConfig, type, - executeAction: () => {}, widgetId: generateReactKey(), - widgetName: generateReactKey(), //TODO: figure out what this is to populate appropriately + widgetName: widgetName || generateReactKey(), //TODO: figure out what this is to populate appropriately isVisible: true, parentColumnSpace, parentRowSpace, diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index 9b0937a97b..885e5dcf00 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -8,15 +8,3 @@ export const snapToGrid = ( const snappedY = Math.floor(y / rowHeight); return [snappedX, snappedY]; }; - -export const getRowColSizes = ( - rowCount: number, - columnCount: number, - width: number, - height: number, -): { rowHeight: number; columnWidth: number } => { - return { - columnWidth: width / columnCount, - rowHeight: height / rowCount, - }; -}; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index c0f3c33476..f67fbf8bcf 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -9,16 +9,16 @@ import { RenderModes, CSSUnits, } from "../constants/WidgetConstants"; -import { Component } from "react"; +import React, { Component } from "react"; import { BaseStyle } from "../editorComponents/BaseComponent"; import _ from "lodash"; -import React from "react"; import DraggableComponent from "../editorComponents/DraggableComponent"; import ResizableComponent from "../editorComponents/ResizableComponent"; import { ActionPayload } from "../constants/ActionConstants"; +import { WidgetFunctionsContext } from "../pages/Editor"; abstract class BaseWidget< - T extends WidgetProps & WidgetFunctions, + T extends WidgetProps, K extends WidgetState > extends Component { constructor(props: T) { @@ -32,6 +32,13 @@ abstract class BaseWidget< this.state = initialState as K; } + static contextType = WidgetFunctionsContext; + + executeAction(actionPayloads?: ActionPayload[]): void { + const { executeAction } = this.context; + executeAction && executeAction(actionPayloads); + } + componentDidMount(): void { this.calculateWidgetBounds( this.props.rightColumn, @@ -94,24 +101,28 @@ abstract class BaseWidget< getCanvasView(): JSX.Element { const style = this.getPositionStyle(); - return ( - - - {this.getPageView()} - - - ); + if (!this.props.parentId) { + return this.getPageView(); + } else { + return ( + + + {this.getPageView()} + + + ); + } } abstract getWidgetType(): WidgetType; getPositionStyle(): BaseStyle { return { - positionType: "CONTAINER_DIRECTION", + positionType: "ABSOLUTE", componentHeight: this.state.componentHeight, componentWidth: this.state.componentWidth, yPosition: this.props.topRow * this.props.parentRowSpace, @@ -134,17 +145,11 @@ export interface WidgetState { componentWidth: number; } -export interface DraggableWidget { - type: string; - widget: WidgetProps; - key: string; -} - export interface WidgetBuilder { buildWidget(widgetProps: T): JSX.Element; } -export interface WidgetProps extends WidgetFunctions, WidgetDataProps { +export interface WidgetProps extends WidgetDataProps { key?: string; renderMode: RenderMode; } @@ -164,7 +169,7 @@ export interface WidgetDataProps { } export interface WidgetFunctions { - executeAction: (actionPayloads?: ActionPayload[]) => void; + executeAction?: (actionPayloads?: ActionPayload[]) => void; updateWidget?: Function; } diff --git a/app/client/src/widgets/ButtonWidget.tsx b/app/client/src/widgets/ButtonWidget.tsx index 63e4a04c92..8967c0df80 100644 --- a/app/client/src/widgets/ButtonWidget.tsx +++ b/app/client/src/widgets/ButtonWidget.tsx @@ -6,7 +6,7 @@ import { ActionPayload } from "../constants/ActionConstants"; class ButtonWidget extends BaseWidget { onButtonClick() { - this.props.executeAction(this.props.onClick); + super.executeAction(this.props.onClick); } getPageView() { @@ -14,6 +14,7 @@ class ButtonWidget extends BaseWidget { { diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index f19db96f12..ec1ded6bb3 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -1,9 +1,5 @@ import React from "react"; -import BaseWidget, { - WidgetProps, - WidgetState, - WidgetFunctions, -} from "./BaseWidget"; +import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget"; import ContainerComponent from "../editorComponents/ContainerComponent"; import { ContainerOrientation, WidgetType } from "../constants/WidgetConstants"; import WidgetFactory from "../utils/WidgetFactory"; @@ -14,7 +10,7 @@ import { GridDefaults } from "../constants/WidgetConstants"; import DraggableComponent from "../editorComponents/DraggableComponent"; import ResizableComponent from "../editorComponents/ResizableComponent"; -const { DEFAULT_GRID_COLUMNS, DEFAULT_GRID_ROWS } = GridDefaults; +const { DEFAULT_GRID_COLUMNS, DEFAULT_GRID_ROW_HEIGHT } = GridDefaults; class ContainerWidget extends BaseWidget< ContainerWidgetProps, @@ -34,21 +30,15 @@ class ContainerWidget extends BaseWidget< componentDidUpdate(previousProps: ContainerWidgetProps) { super.componentDidUpdate(previousProps); let snapColumnSpace = this.state.snapColumnSpace; - let snapRowSpace = this.state.snapRowSpace; if (this.state.componentWidth) - snapColumnSpace = + snapColumnSpace = Math.floor( this.state.componentWidth / - (this.props.snapColumns || DEFAULT_GRID_COLUMNS); - if (this.state.componentHeight) - snapRowSpace = - this.state.componentHeight / (this.props.snapRows || DEFAULT_GRID_ROWS); - if ( - this.state.snapColumnSpace !== snapColumnSpace || - this.state.snapRowSpace !== snapRowSpace - ) { + (this.props.snapColumns || DEFAULT_GRID_COLUMNS), + ); + if (this.state.snapColumnSpace !== snapColumnSpace) { this.setState({ snapColumnSpace, - snapRowSpace, + snapRowSpace: DEFAULT_GRID_ROW_HEIGHT, }); } } @@ -57,12 +47,7 @@ class ContainerWidget extends BaseWidget< childWidgetData.parentColumnSpace = this.state.snapColumnSpace; childWidgetData.parentRowSpace = this.state.snapRowSpace; childWidgetData.parentId = this.props.widgetId; - const widgetFunctions: WidgetFunctions = this.props as WidgetFunctions; - return WidgetFactory.createWidget( - childWidgetData, - widgetFunctions, - this.props.renderMode, - ); + return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } getPageView() { @@ -72,25 +57,31 @@ class ContainerWidget extends BaseWidget< style={{ ...this.getPositionStyle(), }} + isRoot={!this.props.parentId} orientation={this.props.orientation || "VERTICAL"} + widgetName={this.props.widgetName} > {_.map(this.props.children, this.renderChildWidget)} ); } - getCanvasView() { - const style = this.getPositionStyle(); - const occupiedSpaces: OccupiedSpace[] | null = this.props.children + getOccupiedSpaces(): OccupiedSpace[] | null { + return this.props.children ? this.props.children.map(child => ({ id: child.widgetId, + parentId: this.props.widgetId, left: child.leftColumn, top: child.topRow, bottom: child.bottomRow, right: child.rightColumn, })) : null; - return ( + } + getCanvasView() { + const style = this.getPositionStyle(); + const occupiedSpaces = this.getOccupiedSpaces(); + const renderComponent = ( - - - {this.getPageView()} - - + {this.getPageView()} ); + const renderDraggableComponent = ( + + + {renderComponent} + + + ); + + return this.props.parentId ? renderDraggableComponent : renderComponent; } getWidgetType(): WidgetType { @@ -137,6 +134,7 @@ export type OccupiedSpace = { top: number; bottom: number; id: string; + parentId?: string; }; export default ContainerWidget; diff --git a/app/client/src/widgets/SpinnerWidget.tsx b/app/client/src/widgets/SpinnerWidget.tsx index 70429a8d50..3291c20e6f 100644 --- a/app/client/src/widgets/SpinnerWidget.tsx +++ b/app/client/src/widgets/SpinnerWidget.tsx @@ -10,6 +10,7 @@ class SpinnerWidget extends BaseWidget { =3.5 <5", lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@^4.2.0, lodash@~4.17.10: +"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@^4.2.0, lodash@~4.17.10: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -7448,11 +7409,6 @@ micro-api-client@^3.2.1: resolved "https://registry.yarnpkg.com/micro-api-client/-/micro-api-client-3.3.0.tgz#52dd567d322f10faffe63d19d4feeac4e4ffd215" integrity sha512-y0y6CUB9RLVsy3kfgayU28746QrNMpSm9O/AYGNsBgOkJr/X/Jk0VLGoO8Ude7Bpa8adywzF+MzXNZRFRsNPhg== -microbuffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/microbuffer/-/microbuffer-1.0.0.tgz#8b3832ed40c87d51f47bb234913a698a756d19d2" - integrity sha1-izgy7UDIfVH0e7I0kTppinVtGdI= - microevent.ts@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" @@ -7689,7 +7645,7 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -nan@^2.1.0, nan@^2.12.1, nan@^2.13.2: +nan@^2.12.1, nan@^2.13.2: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -7721,13 +7677,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -neatequal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/neatequal/-/neatequal-1.0.0.tgz#2ee1211bc9fa6e4c55715fd210bb05602eb1ae3b" - integrity sha1-LuEhG8n6bkxVcV/SELsFYC6xrjs= - dependencies: - varstream "^0.3.2" - needle@^2.2.1: version "2.4.0" resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" @@ -7774,7 +7723,7 @@ node-forge@0.8.2: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.8.2.tgz#b4bcc59fb12ce77a8825fc6a783dfe3182499c5a" integrity sha512-mXQ9GBq1N3uDCyV1pdSzgIguwgtVpM7f5/5J4ipz12PKWElmPpVWLDuWl8iXmhysr21+WmX/OJ5UKx82wjomgg== -node-gyp@^3.0.3, node-gyp@^3.8.0: +node-gyp@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== @@ -8347,7 +8296,7 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pako@^1.0.0, pako@~1.0.5: +pako@~1.0.5: version "1.0.10" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== @@ -9365,11 +9314,6 @@ private@^0.1.6: resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -9576,10 +9520,10 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -re-resizable@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.0.0.tgz#84258f098b0dde214a39ca6d9ca9959aeefbc26d" - integrity sha512-RTrnhbGgYyZ4hTc6db4JeMnRfmloEPWtuYaXZEa2PRaEC4mreWNFnZtMVsHil3z3iX+WchD+da8BLlTJBcstMA== +re-resizable@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.1.0.tgz#ba4ece505b48f05691446d57837151349d7575e8" + integrity sha512-Jj9zdYW6SnUto8pmH4b/3Kms/PKPv9CuWE70W1IuUIR1HlrEibgsqhbUe8BYDRBTuagH1gav09806k7TieUeSA== dependencies: fast-memoize "^2.5.1" @@ -9661,6 +9605,14 @@ react-dom@^16.7.0: prop-types "^15.6.2" scheduler "^0.15.0" +react-draggable@4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.0.3.tgz#6b9f76f66431c47b9070e9b805bbc520df8ca481" + integrity sha512-4vD6zms+9QGeZ2RQXzlUBw8PBYUXy+dzYX5r22idjp9YwQKIIvD/EojL0rbjS1GK4C3P0rAJnmKa8gDQYWUDyA== + dependencies: + classnames "^2.2.5" + prop-types "^15.6.0" + react-error-overlay@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.1.tgz#b8d3cf9bb991c02883225c48044cb3ee20413e0f" @@ -9708,6 +9660,15 @@ react-redux@^6.0.0: prop-types "^15.7.2" react-is "^16.8.2" +react-rnd@^10.1.1: + version "10.1.1" + resolved "https://registry.yarnpkg.com/react-rnd/-/react-rnd-10.1.1.tgz#4f4d9d28c46a6060acb64600df7a88490862a324" + integrity sha512-KwNUbNd4Kg2DTLdw/Eb8dSC3T5nMRQIRfyyVjdoLT85hKXpeCkMRb2zo4BHCzkgdgCbFGgYLDmj1qRH5DUkTGA== + dependencies: + re-resizable "6.1.0" + react-draggable "4.0.3" + tslib "1.10.0" + react-router-dom@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be" @@ -9878,7 +9839,7 @@ read-pkg@^5.1.1: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -9891,16 +9852,6 @@ read-pkg@^5.1.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^1.0.33: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - readable-stream@^3.0.6, readable-stream@^3.1.1: version "3.4.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" @@ -9910,18 +9861,6 @@ readable-stream@^3.0.6, readable-stream@^3.1.1: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@~2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" - integrity sha1-j5A0HmilPMySh4jaz80Rs265t44= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" @@ -10523,7 +10462,7 @@ sass-loader@7.2.0: pify "^4.0.1" semver "^5.5.0" -sax@^1.1.5, sax@^1.2.4, sax@~1.2.4: +sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -11075,16 +11014,6 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string.fromcodepoint@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string.fromcodepoint/-/string.fromcodepoint-0.2.1.tgz#8d978333c0bc92538f50f383e4888f3e5619d653" - integrity sha1-jZeDM8C8klOPUPOD5IiPPlYZ1lM= - -string.prototype.codepointat@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc" - integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg== - string.prototype.trimleft@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" @@ -11108,11 +11037,6 @@ string_decoder@^1.0.0, string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -11267,38 +11191,6 @@ svg-parser@^2.0.0: resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.2.tgz#d134cc396fa2681dc64f518330784e98bd801ec8" integrity sha512-1gtApepKFweigFZj3sGO8KT8LvVZK8io146EzXrpVuWCDAbISz/yMucco3hWTkpZNoPabM+dnMOpy6Swue68Zg== -svg-pathdata@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/svg-pathdata/-/svg-pathdata-1.0.4.tgz#7a681342aac7effd8d52afba7999910c9da3b959" - integrity sha1-emgTQqrH7/2NUq+6eZmRDJ2juVk= - dependencies: - readable-stream "~2.0.4" - -svg2ttf@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/svg2ttf/-/svg2ttf-4.3.0.tgz#433440c7e9062f8fdcec3cad721cd08a2c7e51e3" - integrity sha512-LZ0B7zzHWLWbzLzwaKGHQvPOuxCXLReIb3LSxFSGUy1gMw2Utk6KGNbTmbmRL6Rk1qDSmTixnDrQgnXaL9n0CA== - dependencies: - argparse "^1.0.6" - cubic2quad "^1.0.0" - lodash "^4.17.10" - microbuffer "^1.0.0" - svgpath "^2.1.5" - xmldom "~0.1.22" - -svgicons2svgfont@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/svgicons2svgfont/-/svgicons2svgfont-5.0.2.tgz#0511823c6491be1a7d543292e29a8ae627ad0406" - integrity sha1-BRGCPGSRvhp9VDKS4pqK5ietBAY= - dependencies: - commander "^2.9.0" - neatequal "^1.0.0" - readable-stream "^2.0.4" - sax "^1.1.5" - string.fromcodepoint "^0.2.1" - string.prototype.codepointat "^0.2.0" - svg-pathdata "^1.0.4" - svgo@^1.0.0, svgo@^1.2.2: version "1.3.0" resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.0.tgz#bae51ba95ded9a33a36b7c46ce9c359ae9154313" @@ -11318,11 +11210,6 @@ svgo@^1.0.0, svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" -svgpath@^2.1.5: - version "2.2.2" - resolved "https://registry.yarnpkg.com/svgpath/-/svgpath-2.2.2.tgz#1c70d44e27f7b6bd42a74ed3c960be93e411def3" - integrity sha512-7cXFbkZvPkZpKLC+3QIfyUd3/Un/CvJONjTD3Gz5qLuEa73StPOt8kZjTi9apxO6zwCaza0bPNnmzTyrQ4qQlw== - symbol-observable@^1.0.2, symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -11650,16 +11537,16 @@ tsdx@^0.6.0: tslib "^1.9.3" typescript "^3.4.5" +tslib@1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + tslib@1.9.3, tslib@~1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - tsutils@^3.17.1, tsutils@^3.7.0: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" @@ -11667,33 +11554,6 @@ tsutils@^3.17.1, tsutils@^3.7.0: dependencies: tslib "^1.8.1" -ttf2eot@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ttf2eot/-/ttf2eot-2.0.0.tgz#8e6337a585abd1608a0c84958ab483ce69f6654b" - integrity sha1-jmM3pYWr0WCKDISVirSDzmn2ZUs= - dependencies: - argparse "^1.0.6" - microbuffer "^1.0.0" - -ttf2woff2@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/ttf2woff2/-/ttf2woff2-2.0.3.tgz#5e020afe6e643287f3ad7687abed20fe654eb329" - integrity sha1-XgIK/m5kMofzrXaHq+0g/mVOsyk= - dependencies: - bindings "^1.2.1" - bufferstreams "^1.1.0" - nan "^2.1.0" - node-gyp "^3.0.3" - -ttf2woff@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ttf2woff/-/ttf2woff-2.0.1.tgz#871832240024b09db9570904c7c1928b8057c969" - integrity sha1-hxgyJAAksJ25VwkEx8GSi4BXyWk= - dependencies: - argparse "^1.0.6" - microbuffer "^1.0.0" - pako "^1.0.0" - tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -11786,11 +11646,6 @@ uglify-js@^3.1.4: commander "~2.20.0" source-map "~0.6.1" -underscore@^1.7.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" - integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== - unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -11893,11 +11748,6 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-join@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78" - integrity sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg= - url-loader@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-2.1.0.tgz#bcc1ecabbd197e913eca23f5e0378e24b4412961" @@ -11988,13 +11838,6 @@ value-equal@^1.0.1: resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== -varstream@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/varstream/-/varstream-0.3.2.tgz#18ac6494765f3ff1a35ad9a4be053bec188a5de1" - integrity sha1-GKxklHZfP/GjWtmkvgU77BiKXeE= - dependencies: - readable-stream "^1.0.33" - vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -12072,22 +11915,6 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -webfonts-generator@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/webfonts-generator/-/webfonts-generator-0.4.0.tgz#5f89fc81c7160e6e0cbbc9b7387e42a5851fda46" - integrity sha1-X4n8gccWDm4Mu8m3OH5CpYUf2kY= - dependencies: - handlebars "^4.0.5" - mkdirp "^0.5.0" - q "^1.1.2" - svg2ttf "^4.0.0" - svgicons2svgfont "^5.0.0" - ttf2eot "^2.0.0" - ttf2woff "^2.0.1" - ttf2woff2 "^2.0.3" - underscore "^1.7.0" - url-join "^1.1.0" - webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -12525,11 +12352,6 @@ xmlchars@^2.1.1: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xmldom@~0.1.22: - version "0.1.27" - resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" - integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk= - xregexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"