diff --git a/app/client/.editorconfig b/app/client/.editorconfig index a402d3daf0..1014ba7888 100644 --- a/app/client/.editorconfig +++ b/app/client/.editorconfig @@ -9,4 +9,4 @@ indent_size = 2 trim_trailing_whitespace = true [*.md] -trim_trailing_whitespace = false \ No newline at end of file +trim_trailing_whitespace = false diff --git a/app/client/.eslintrc.js b/app/client/.eslintrc.js new file mode 100644 index 0000000000..7cbc2a03e4 --- /dev/null +++ b/app/client/.eslintrc.js @@ -0,0 +1,23 @@ +module.exports = { + parser: '@typescript-eslint/parser', + // plugins: ['@typescript-eslint', 'react'], + extends: [ + "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react + 'plugin:@typescript-eslint/recommended' + ], + parserOptions: { + ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features + sourceType: "module", // Allows for the use of imports + ecmaFeatures: { + jsx: true // Allows for the parsing of JSX + } + }, + rules: { + "@typescript-eslint/explicit-function-return-type": "off", + }, + settings: { + react: { + version: "detect" // Tells eslint-plugin-react to automatically detect the version of React to use + } + } +}; \ No newline at end of file diff --git a/app/client/.nvmrc b/app/client/.nvmrc index c68ea6ff65..03128968bf 100644 --- a/app/client/.nvmrc +++ b/app/client/.nvmrc @@ -1 +1 @@ -lts/dubnium \ No newline at end of file +lts/dubnium diff --git a/app/client/.prettierignore b/app/client/.prettierignore index e6a5d5aac2..a8cc396af2 100644 --- a/app/client/.prettierignore +++ b/app/client/.prettierignore @@ -2,4 +2,5 @@ build/ node_modules/ package-lock.json yarn.lock -package.json \ No newline at end of file +package.json + diff --git a/app/client/.prettierrc b/app/client/.prettierrc index 17740f0c8b..fe33162eb0 100644 --- a/app/client/.prettierrc +++ b/app/client/.prettierrc @@ -1,9 +1,9 @@ { - "printWidth": 80, - "tabWidth": 2, - "useTabs": false, - "semi": true, - "singleQuote": false, - "trailingComma": "all", - "parser": "typescript" -} \ No newline at end of file + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "parser": "typescript" +} diff --git a/app/client/Concepts.md b/app/client/Concepts.md new file mode 100644 index 0000000000..90336cfeef --- /dev/null +++ b/app/client/Concepts.md @@ -0,0 +1,16 @@ +Concepts +======== +Widgets + - WidgetProperties + - WIDGET_PROPERTY_LABEL (STRING) + - WIDGET_PROPERTY_TYPE (STRING) + - WIDGET_PROPERTY_DEFAULT (STRING) + - WIDGET_PROPERTY_ENABLED (BOOLEAN) + - WIDGET_PROPERTY_META + - ALLOWED_VALUES (LIST) + - Components + - WidgetCards + - WIDGET_TYPE (STRING) + - WIDGET_CARD_LABEL (STRING) + - WIDGET_CARD_ENABLED (BOOLEAN) + - WIDGET_CARD_ICON (UTF-16) \ No newline at end of file diff --git a/app/client/package.json b/app/client/package.json index c88739c1fb..1dda87bd0f 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -16,6 +16,7 @@ "@types/jest": "^24.0.18", "@types/lodash": "^4.14.120", "@types/moment-timezone": "^0.5.10", + "@types/nanoid": "^2.0.0", "@types/netlify-identity-widget": "^1.4.1", "@types/node": "^10.12.18", "@types/react": "^16.8.2", @@ -28,6 +29,7 @@ "husky": "^1.3.1", "lint-staged": "^8.1.0", "lodash": "^4.17.11", + "nanoid": "^2.0.4", "netlify-identity-widget": "^1.5.5", "node-sass": "^4.11.0", "normalizr": "^3.3.0", @@ -40,10 +42,11 @@ "react-redux": "^6.0.0", "react-router": "^4.3.1", "react-router-dom": "^4.3.1", - "react-scripts": "3.1.1", + "react-scripts": "^3.1.1", "redux": "^4.0.1", "redux-saga": "^1.0.0", "styled-components": "^4.1.3", + "ts-loader": "^6.0.4", "typescript": "^3.2.4" }, "scripts": { @@ -66,6 +69,10 @@ "not op_mini all" ], "devDependencies": { + "@typescript-eslint/eslint-plugin": "^2.0.0", + "@typescript-eslint/parser": "^2.0.0", + "eslint-config-prettier": "^6.1.0", + "eslint-plugin-prettier": "^3.1.0", "dotenv": "^8.1.0", "redux-devtools": "^3.5.0" } diff --git a/app/client/src/App.tsx b/app/client/src/App.tsx index 6bc48f5081..bf256f0bb1 100755 --- a/app/client/src/App.tsx +++ b/app/client/src/App.tsx @@ -1,29 +1,29 @@ -import React, { Component } from "react"; -import logo from "./assets/images/logo.svg"; -import "./App.css"; -import "../node_modules/@blueprintjs/core/src/blueprint.scss"; +import React, { Component } from "react" +import logo from "./assets/images/logo.svg" +import "./App.css" +import "../node_modules/@blueprintjs/core/src/blueprint.scss" class App extends Component { render() { return ( -
-
- logo -

- Edit {"src/App.tsx"} and save to reload. -

- - Learn React - -
-
- ); +
+
+ logo +

+ Edit {"src/App.tsx"} and save to reload. +

+ + Learn React + +
+
+ ) } } -export default App; +export default App diff --git a/app/client/src/actions/pageActions.tsx b/app/client/src/actions/pageActions.tsx index 2fe48582fa..f54af9f794 100644 --- a/app/client/src/actions/pageActions.tsx +++ b/app/client/src/actions/pageActions.tsx @@ -4,6 +4,7 @@ import { } from "../constants/ActionConstants" import { PageRequest } from "../api/PageApi" import { RenderMode } from "../constants/WidgetConstants"; +import { IWidgetProps } from "../widgets/BaseWidget"; export const fetchPage = (pageId: string, renderMode: RenderMode): ReduxAction => { return { @@ -14,3 +15,23 @@ export const fetchPage = (pageId: string, renderMode: RenderMode): ReduxAction

=> { + return { + type: ActionTypes.ADD_PAGE_WIDGET, + payload: { + pageId, + widget, + } + } +} + +export const removeWidget = (pageId: string, widgetId: string): ReduxAction<{ pageId: string, widgetId: string}> => { + return { + type: ActionTypes.REMOVE_PAGE_WIDGET, + payload: { + pageId, + widgetId, + } + } +} \ No newline at end of file diff --git a/app/client/src/actions/widgetCardsPaneActions.tsx b/app/client/src/actions/widgetCardsPaneActions.tsx new file mode 100644 index 0000000000..b24e42cedb --- /dev/null +++ b/app/client/src/actions/widgetCardsPaneActions.tsx @@ -0,0 +1,30 @@ +import { + ActionTypes +} from "../constants/ActionConstants" +import { IWidgetCardProps } from '../widgets/BaseWidget' + +export const fetchWidgetCards = () => { + return { + type: ActionTypes.FETCH_WIDGET_CARDS + } +} + +export const errorFetchingWidgetCards = (error: any) => { + return { + type: ActionTypes.ERROR_FETCHING_WIDGET_CARDS, + error + } +} + +export const successFetchingWidgetCards = (cards: { [id: string]: IWidgetCardProps[] }) => { + return { + type: ActionTypes.SUCCESS_FETCHING_WIDGET_CARDS, + cards + } +} + +export default { + fetchWidgetCards, + errorFetchingWidgetCards, + successFetchingWidgetCards +} \ No newline at end of file diff --git a/app/client/src/api/ApiRequests.tsx b/app/client/src/api/ApiRequests.tsx index 43e52b6e57..0e890a592f 100644 --- a/app/client/src/api/ApiRequests.tsx +++ b/app/client/src/api/ApiRequests.tsx @@ -4,7 +4,6 @@ export interface ApiHeaders { Accept: ContentType "Content-Type": ContentType dataType: DataType - "Accept-Encoding": EncodingType } export interface ApiRequest { diff --git a/app/client/src/api/PageApi.tsx b/app/client/src/api/PageApi.tsx index b228590964..4aebdfd5ea 100644 --- a/app/client/src/api/PageApi.tsx +++ b/app/client/src/api/PageApi.tsx @@ -1,27 +1,27 @@ import Api from "./Api" -import { IContainerWidgetProps } from "../widgets/ContainerWidget" +import { ContainerWidgetProps } from "../widgets/ContainerWidget" import { ApiResponse } from "./ApiResponses" import { RenderMode } from "../constants/WidgetConstants"; export interface PageRequest { - pageId: string, - renderMode: RenderMode + pageId: string; + renderMode: RenderMode; } export interface SavePageRequest { - pageWidget: IContainerWidgetProps + pageWidget: ContainerWidgetProps; } export interface PageResponse extends ApiResponse { - pageWidget: IContainerWidgetProps + pageWidget: ContainerWidgetProps; } export interface SavePageResponse { - pageId: string + pageId: string; } class PageApi extends Api { - static url: string = "/page" + static url = "/page" static fetchPage(pageRequest: PageRequest): Promise { return Api.get(PageApi.url + "/" + pageRequest.pageId, pageRequest) @@ -30,7 +30,8 @@ class PageApi extends Api { static savePage(savePageRequest: SavePageRequest): Promise { return Api.post(PageApi.url, undefined, savePageRequest) } - } + + export default PageApi diff --git a/app/client/src/api/WidgetCardsPaneApi.tsx b/app/client/src/api/WidgetCardsPaneApi.tsx new file mode 100644 index 0000000000..45e7b56be5 --- /dev/null +++ b/app/client/src/api/WidgetCardsPaneApi.tsx @@ -0,0 +1,17 @@ +import Api from "./Api" +import { IWidgetCardProps } from "../widgets/BaseWidget" + +export interface WidgetCardsPaneResponse { + cards : { [id: string]: IWidgetCardProps[]} +} +export interface WidgetCardsPaneRequest {} + +class WidgetCardsPaneApi extends Api { + static url: string = "/widgetCards" + static fetchWidgetCards(): Promise { + return Api.get(WidgetCardsPaneApi.url, {}) + } +} + + +export default WidgetCardsPaneApi diff --git a/app/client/src/assets/images/blank.png b/app/client/src/assets/images/blank.png new file mode 100644 index 0000000000..cbdf4ae908 Binary files /dev/null and b/app/client/src/assets/images/blank.png differ diff --git a/app/client/src/constants/ActionConstants.tsx b/app/client/src/constants/ActionConstants.tsx index 4fb455989b..8d8f43caeb 100644 --- a/app/client/src/constants/ActionConstants.tsx +++ b/app/client/src/constants/ActionConstants.tsx @@ -1,5 +1,5 @@ -import ContainerWidget from "../widgets/ContainerWidget" -import { IWidgetProps } from "../widgets/BaseWidget" +// import ContainerWidget from "../widgets/ContainerWidget" +import { IWidgetProps, IWidgetCardProps } from "../widgets/BaseWidget" export type ActionType = | "UPDATE_CANVAS" @@ -9,6 +9,16 @@ export type ActionType = | "REMOVE_WIDGET_CANVAS" | "LOAD_WIDGET_PANE" | "FETCH_PAGE" + | "ZOOM_IN_CANVAS" + | "ZOOM_OUT_CANVAS" + | "PUBLISH" + | "UNDO_CANVAS_ACTION" + | "REDO_CANVAS_ACTION" + | "FETCH_WIDGET_CARDS" + | "SUCCESS_FETCHING_WIDGET_CARDS" + | "ERROR_FETCHING_WIDGET_CARDS" + | "ADD_PAGE_WIDGET" + | "REMOVE_PAGE_WIDGET" export const ActionTypes: { [id: string]: ActionType } = { UPDATE_CANVAS: "UPDATE_CANVAS", @@ -17,7 +27,17 @@ export const ActionTypes: { [id: string]: ActionType } = { FETCH_PAGE: "FETCH_PAGE", DROP_WIDGET_CANVAS: "DROP_WIDGET_CANVAS", REMOVE_WIDGET_CANVAS: "REMOVE_WIDGET_CANVAS", - LOAD_WIDGET_PANE: "LOAD_WIDGET_PANE" + LOAD_WIDGET_PANE: "LOAD_WIDGET_PANE", + ZOOM_IN_CANVAS: "ZOOM_IN_CANVAS", + ZOOM_OUT_CANVAS: "ZOOM_OUT_CANVAS", + UNDO_CANVAS_ACTION: "UNDO_CANVAS_ACTION", + REDO_CANVAS_ACTION: "REDO_CANVAS_ACTION", + PUBLISH: "PUBLISH", + FETCH_WIDGET_CARDS: "FETCH_WIDGET_CARDS", + SUCCESS_FETCHING_WIDGET_CARDS: "SUCCESS_FETCHING_WIDGET_CARDS", + ERROR_FETCHING_WIDGET_CARDS: "ERROR_FETCHING_WIDGET_CARDS", + ADD_PAGE_WIDGET: "ADD_PAGE_WIDGET", + REMOVE_PAGE_WIDGET: "REMOVE_PAGE_WIDGET" } export interface ReduxAction { @@ -33,3 +53,7 @@ export interface LoadCanvasPayload { export interface LoadWidgetPanePayload { widgets: IWidgetProps[] } + +export interface LoadWidgetCardsPanePayload { + cards: { [id: string] : IWidgetCardProps[] } +} diff --git a/app/client/src/constants/ApiConstants.tsx b/app/client/src/constants/ApiConstants.tsx index a98ea407d7..ebac51b8b3 100644 --- a/app/client/src/constants/ApiConstants.tsx +++ b/app/client/src/constants/ApiConstants.tsx @@ -5,12 +5,17 @@ export type ContentType = "application/json" | "application/x-www-form-urlencode export type EncodingType = "gzip" export const PROD_BASE_URL = "https://mobtools.com/api/" +export const MOCK_BASE_URL = "https://f78ff9dd-2c08-45f1-9bf9-8c670a1bb696.mock.pstmn.io" export const STAGE_BASE_URL = "https://14157cb0-190f-4082-a791-886a8df05930.mock.pstmn.io" -export const BASE_URL = STAGE_BASE_URL +export const BASE_URL = MOCK_BASE_URL export const REQUEST_TIMEOUT_MS = 2000 export const REQUEST_HEADERS: ApiHeaders = { Accept: "application/json", "Content-Type": "application/json", dataType: "json", - "Accept-Encoding": "gzip" +} + +export interface APIException { + error: number; + message: string; } diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index a858273719..e362d67dd6 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -13,6 +13,16 @@ export type WidgetType = | "NUMERIC_INPUT_WIDGET" | "CHECKBOX_WIDGET" | "RADIO_GROUP_WIDGET" + | "INPUT_WIDGET" + | "TOGGLE_WIDGET" + +export const WidgetTypes: {[id: string]: WidgetType } = { + BUTTON_WIDGET: "BUTTON_WIDGET", + TEXT_WIDGET: "TEXT_WIDGET", + INPUT_WIDGET: "INPUT_WIDGET", + TOGGLE_WIDGET: "TOGGLE_WIDGET", +} + export type ContainerOrientation = "HORIZONTAL" | "VERTICAL" export type PositionType = "ABSOLUTE" | "CONTAINER_DIRECTION" export type CSSUnit = @@ -47,6 +57,6 @@ export const RenderModes: { [id: string]: RenderMode } = { export const CSSUnits: { [id: string]: CSSUnit } = { PIXEL: "px", - RELATIVE_FONTSIZE: "em", + RELATIVE_FONTSIZE: "rem", RELATIVE_PARENT: "%" } diff --git a/app/client/src/editorComponents/BaseComponent.tsx b/app/client/src/editorComponents/BaseComponent.tsx index 98bcf07f10..0b4b876c0a 100644 --- a/app/client/src/editorComponents/BaseComponent.tsx +++ b/app/client/src/editorComponents/BaseComponent.tsx @@ -1,32 +1,28 @@ import { Component } from "react" -import React from "react" import { PositionType, CSSUnit } from "../constants/WidgetConstants" +import { Color } from "../constants/StyleConstants" /*** * Components are responsible for binding render inputs to corresponding UI SDKs */ -abstract class BaseComponent extends Component { - - constructor(componentProps: T) { - super(componentProps) - } - -} +abstract class BaseComponent extends Component {} export interface BaseStyle { - height?: number - width?: number - positionType: PositionType - xPosition: number - yPosition: number - xPositionUnit: CSSUnit - yPositionUnit: CSSUnit - heightUnit?: CSSUnit - widthUnit?: CSSUnit + height?: number; + width?: number; + positionType: PositionType; + xPosition: number; + yPosition: number; + xPositionUnit: CSSUnit; + yPositionUnit: CSSUnit; + heightUnit?: CSSUnit; + widthUnit?: CSSUnit; + backgroundColor?: Color; + } export interface IComponentProps { - widgetId: string - style: BaseStyle + widgetId: string; + style: BaseStyle; } export default BaseComponent \ No newline at end of file diff --git a/app/client/src/editorComponents/BreadcrumbsComponent.tsx b/app/client/src/editorComponents/BreadcrumbsComponent.tsx index 43402e5153..495de950c7 100644 --- a/app/client/src/editorComponents/BreadcrumbsComponent.tsx +++ b/app/client/src/editorComponents/BreadcrumbsComponent.tsx @@ -3,13 +3,11 @@ import { IComponentProps } from "./BaseComponent" import { Boundary, Breadcrumbs, - Breadcrumb, - Card, IBreadcrumbProps } from "@blueprintjs/core" import { Container } from "./ContainerComponent" -class BreadcrumbsComponent extends React.Component { +class BreadcrumbsComponent extends React.Component { render() { return ( @@ -24,12 +22,12 @@ class BreadcrumbsComponent extends React.Component { } } -export interface IBreadcrumbsComponentProps extends IComponentProps { - width?: number - collapseFrom?: Boundary - className?: string - minVisibleItems?: number - items?: IBreadcrumbProps[] +export interface BreadcrumbsComponentProps extends IComponentProps { + width?: number; + collapseFrom?: Boundary; + className?: string; + minVisibleItems?: number; + items?: IBreadcrumbProps[]; } export default BreadcrumbsComponent diff --git a/app/client/src/editorComponents/ButtonComponent.tsx b/app/client/src/editorComponents/ButtonComponent.tsx index e1449a7a92..b39cd0d34a 100644 --- a/app/client/src/editorComponents/ButtonComponent.tsx +++ b/app/client/src/editorComponents/ButtonComponent.tsx @@ -7,7 +7,7 @@ class ButtonComponent extends React.Component { render() { return ( - ) } diff --git a/app/client/src/editorComponents/ContainerComponent.tsx b/app/client/src/editorComponents/ContainerComponent.tsx index 65e7c37f3c..46abbe45e9 100644 --- a/app/client/src/editorComponents/ContainerComponent.tsx +++ b/app/client/src/editorComponents/ContainerComponent.tsx @@ -1,9 +1,12 @@ -import BaseComponent, { IComponentProps } from "./BaseComponent" -import { ContainerOrientation } from "../constants/WidgetConstants" -import styled from "../constants/DefaultTheme" -import React from "react" +import { IComponentProps } from "./BaseComponent"; +import { ContainerOrientation } from "../constants/WidgetConstants"; +import styled from "../constants/DefaultTheme"; +import { useDrop } from "react-dnd" +import { WidgetTypes } from "../constants/WidgetConstants" +import { DraggableWidget } from "../widgets/BaseWidget" +import React from "react"; -export const Container = styled("div")` +export const Container = styled("div")` display: flex; flex-direction: ${props => { return props.orientation === "HORIZONTAL" ? "row" : "column" @@ -14,22 +17,30 @@ export const Container = styled("div")` return props.style.positionType === "ABSOLUTE" ? "absolute" : "relative" }}; left: ${props => { - return props.style.xPosition + props.style.xPositionUnit + return props.style.positionType !== "ABSOLUTE" ? undefined : props.style.xPosition + props.style.xPositionUnit }}; top: ${props => { - return props.style.yPosition + props.style.yPositionUnit + return props.style.positionType !== "ABSOLUTE" ? undefined : props.style.yPosition + props.style.yPositionUnit }}; -` - -class ContainerComponent extends BaseComponent { - render() { - return {this.props.children} - } +`; +const ContainerComponent = (props: ContainerProps) => { + const addWidgetFn = props.addWidget; + const [, drop] = useDrop({ + accept: Object.values(WidgetTypes), + drop(item: DraggableWidget, monitor) { + if (addWidgetFn && monitor.isOver({shallow: true})){ + addWidgetFn(item.type); + } + return undefined + }, + }) + return {props.children} } -export interface IContainerProps extends IComponentProps { - children?: JSX.Element[] | JSX.Element - orientation?: ContainerOrientation +export interface ContainerProps extends IComponentProps { + children?: JSX.Element[] | JSX.Element; + orientation?: ContainerOrientation; + addWidget?: Function; } export default ContainerComponent diff --git a/app/client/src/editorComponents/DraggableComponent.tsx b/app/client/src/editorComponents/DraggableComponent.tsx new file mode 100644 index 0000000000..f5651292ce --- /dev/null +++ b/app/client/src/editorComponents/DraggableComponent.tsx @@ -0,0 +1,52 @@ +import * as React from "react" +import { IWidgetProps, IWidgetState } from "../widgets/BaseWidget" +import { DragSource, DragSourceConnector, DragSourceMonitor } from "react-dnd" +import { ContainerProps } from "./ContainerComponent" + +export interface DraggableProps extends ContainerProps { + connectDragSource: Function; + isDragging?: boolean; +} + +class DraggableComponent extends React.Component { + render() { + return this.props.connectDragSource( +

+ {this.props.children} +
+ ) + } +} + +const widgetSource = { + beginDrag(props: IWidgetProps) { + return { + widgetId: props.widgetId, + widgetType: props.widgetType + } + } +} + +const widgetType = (props: IWidgetProps) => { + return props.widgetType +} + +function collect(connect: DragSourceConnector, monitor: DragSourceMonitor) { + return { + connectDragSource: connect.dragSource(), + isDragging: monitor.isDragging() + } +} + +export default DragSource(widgetType, widgetSource, collect)(DraggableComponent) diff --git a/app/client/src/editorComponents/DroppableComponent.tsx b/app/client/src/editorComponents/DroppableComponent.tsx new file mode 100644 index 0000000000..faeff03221 --- /dev/null +++ b/app/client/src/editorComponents/DroppableComponent.tsx @@ -0,0 +1,58 @@ +import * as React from "react" +import { IWidgetProps, IWidgetState } from "../widgets/BaseWidget" +import { DropTargetConnector, DropTargetMonitor, DropTarget, XYCoord } from "react-dnd" +import { ContainerProps } from "./ContainerComponent" +import WidgetFactory from "../utils/WidgetFactory"; + +export interface DroppableProps extends ContainerProps { + connectDropTarget: Function; + isOver?: boolean; +} + +class DroppableComponent extends React.Component< + DroppableProps, + IWidgetState +> { + render() { + return this.props.connectDropTarget( +
+ { this.props.isOver ? undefined : this.props.children} +
+ ) + } +} + +const dropTarget = { + canDrop(props: IWidgetProps) { + return true + }, + drop(props: IWidgetProps, monitor: DropTargetMonitor) { + const item = monitor.getItem() + const delta = monitor.getDifferenceFromInitialOffset() as XYCoord + const left = Math.round(item.left + delta.x) + const top = Math.round(item.top + delta.y) + return { left: left, top: top } + } +} + +const getDropTypes = (props: IWidgetProps) => { + return WidgetFactory.getWidgetTypes() +} + +function collect(connect: DropTargetConnector, monitor: DropTargetMonitor) { + return { + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver() + } +} + +export default DropTarget(getDropTypes, dropTarget, collect)(DroppableComponent) diff --git a/app/client/src/editorComponents/IconComponent.tsx b/app/client/src/editorComponents/IconComponent.tsx index 88bff73750..2e20f44d4e 100644 --- a/app/client/src/editorComponents/IconComponent.tsx +++ b/app/client/src/editorComponents/IconComponent.tsx @@ -3,7 +3,7 @@ import { IComponentProps } from "./BaseComponent" import { Icon, Intent } from "@blueprintjs/core" import { IconName } from "@blueprintjs/icons" import { Container } from "./ContainerComponent" -class IconComponent extends React.Component { +class IconComponent extends React.Component { render() { return ( @@ -17,11 +17,11 @@ class IconComponent extends React.Component { } } -export interface IIconComponentProps extends IComponentProps { - iconSize?: number - icon?: IconName - intent?: Intent - ellipsize?: boolean +export interface IconComponentProps extends IComponentProps { + iconSize?: number; + icon?: IconName; + intent?: Intent; + ellipsize?: boolean; } export default IconComponent diff --git a/app/client/src/editorComponents/RadioGroupComponent.tsx b/app/client/src/editorComponents/RadioGroupComponent.tsx index 06c7f1ddc3..c1be6b0f93 100644 --- a/app/client/src/editorComponents/RadioGroupComponent.tsx +++ b/app/client/src/editorComponents/RadioGroupComponent.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { IComponentProps } from "./BaseComponent" import { Radio, RadioGroup, IOptionProps } from "@blueprintjs/core" import { Container } from "./ContainerComponent" -class RadioGroupComponent extends React.Component { +class RadioGroupComponent extends React.Component { render() { return ( @@ -25,19 +25,19 @@ class RadioGroupComponent extends React.Component { } } -export interface IRadioGroupComponentProps extends IComponentProps { - label: string - inline: boolean - selectedValue: string | number - handleRadioChange: (event: React.FormEvent) => void - disabled: boolean - className: string - name: string - options: IOptionProps[] +export interface RadioGroupComponentProps extends IComponentProps { + label: string; + inline: boolean; + selectedValue: string | number; + handleRadioChange: (event: React.FormEvent) => void; + disabled: boolean; + className: string; + name: string; + options: IOptionProps[]; items: Array<{ - label: string - value: number | string - }> + label: string; + value: number | string; + }>; } export default RadioGroupComponent diff --git a/app/client/src/editorComponents/SpinnerComponent.tsx b/app/client/src/editorComponents/SpinnerComponent.tsx index 7ff503055a..129fc3f370 100644 --- a/app/client/src/editorComponents/SpinnerComponent.tsx +++ b/app/client/src/editorComponents/SpinnerComponent.tsx @@ -3,7 +3,7 @@ import { IComponentProps } from "./BaseComponent" import { Spinner, Intent } from "@blueprintjs/core" import { Container } from "./ContainerComponent" -class SpinnerComponent extends React.Component { +class SpinnerComponent extends React.Component { render() { return ( @@ -17,7 +17,7 @@ class SpinnerComponent extends React.Component { } } -export interface ISpinnerComponentProps extends IComponentProps { +export interface SpinnerComponentProps extends IComponentProps { size?: number value?: number intent?: Intent diff --git a/app/client/src/editorComponents/TagInputComponent.tsx b/app/client/src/editorComponents/TagInputComponent.tsx index 0153e3b810..ce3e39af8b 100644 --- a/app/client/src/editorComponents/TagInputComponent.tsx +++ b/app/client/src/editorComponents/TagInputComponent.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { IComponentProps } from "./BaseComponent" import { Intent, ITagProps, TagInput, HTMLInputProps } from "@blueprintjs/core" import { Container } from "./ContainerComponent" -class TagInputComponent extends React.Component { +class TagInputComponent extends React.Component { render() { return ( @@ -15,7 +15,7 @@ class TagInputComponent extends React.Component { } } -export interface ITagInputComponentProps extends IComponentProps { +export interface TagInputComponentProps extends IComponentProps { addOnPaste?: boolean className?: string disabled?: boolean diff --git a/app/client/src/index.css b/app/client/src/index.css index cee5f348fb..0773dc6c88 100755 --- a/app/client/src/index.css +++ b/app/client/src/index.css @@ -6,6 +6,7 @@ body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + background: #efefef; } code { diff --git a/app/client/src/index.tsx b/app/client/src/index.tsx index 89b4e5f52a..d118033555 100755 --- a/app/client/src/index.tsx +++ b/app/client/src/index.tsx @@ -14,6 +14,10 @@ import WidgetBuilderRegistry from "./utils/WidgetRegistry"; import { ThemeProvider, theme } from "./constants/DefaultTheme"; import createSagaMiddleware from 'redux-saga' import { rootSaga } from "./sagas" +// import { ActionType, ReduxAction } from "./constants/ActionConstants"; + +import { DndProvider } from "react-dnd" +import HTML5Backend from "react-dnd-html5-backend" import { appInitializer } from "./utils/AppsmithUtils"; import ProtectedRoute from "./pages/common/ProtectedRoute"; appInitializer(); @@ -22,18 +26,21 @@ const sagaMiddleware = createSagaMiddleware() const store = createStore(appReducer, applyMiddleware(sagaMiddleware)); sagaMiddleware.run(rootSaga) ReactDOM.render( - - - - - - - - - - - - , + + + + + + + + + + + + + + + , document.getElementById("root") ); diff --git a/app/client/src/mockResponses/CanvasResponse.tsx b/app/client/src/mockResponses/CanvasResponse.tsx deleted file mode 100644 index d8f7b20a5a..0000000000 --- a/app/client/src/mockResponses/CanvasResponse.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import { IContainerWidgetProps } from "../widgets/ContainerWidget" -import { RenderModes } from "../constants/WidgetConstants" - -const CanvasResponse: IContainerWidgetProps = { - widgetId: "0", - widgetType: "CONTAINER_WIDGET", - snapColumns: 10, - snapRows: 10, - topRow: 100, - bottomRow: 700, - leftColumn: 100, - rightColumn: 1000, - parentColumnSpace: 1, - parentRowSpace: 1, - renderMode: RenderModes.CANVAS, - children: [ - { - widgetId: "1", - widgetType: "TEXT_WIDGET", - topRow: 2, - leftColumn: 1, - bottomRow: 5, - rightColumn: 3, - text: "Lorem Ipsum asda asd kjhsadjhas kdh kashkjdas kdhas d as", - renderMode: RenderModes.CANVAS - }, - { - widgetId: "2", - widgetType: "BUTTON_WIDGET", - topRow: 2, - leftColumn: 4, - bottomRow: 5, - rightColumn: 5, - text: "Lorem Ipsum", - renderMode: RenderModes.CANVAS - }, - { - widgetId: "3", - widgetType: "INPUT_GROUP_WIDGET", - topRow: 1, - leftColumn: 1, - bottomRow: 1, - rightColumn: 5, - placeholder: "Lorem Ipsum", - renderMode: RenderModes.CANVAS - }, - { - widgetId: "4", - widgetType: "CALLOUT_WIDGET", - topRow: 2, - leftColumn: 8, - bottomRow: 4, - rightColumn: 14, - id: "sample_id", - title: "Visually important content", - description: - "The component is a simple wrapper around the CSS API that provides props for modifiers and optional title element. Any additional HTML props will be spread to the rendered
element.", - icon: "", - intent: "success", - renderMode: RenderModes.CANVAS - }, - { - widgetId: "5", - widgetType: "ICON_WIDGET", - topRow: 4, - leftColumn: 2, - bottomRow: 5, - rightColumn: 3, - icon: "globe", - iconSize: "20", - intent: "warning", - renderMode: RenderModes.CANVAS - }, - { - widgetId: "6", - widgetType: "SPINNER_WIDGET", - topRow: 5, - leftColumn: 6, - bottomRow: 5, - rightColumn: 5, - size: 20 - }, - { - widgetId: "7", - widgetType: "BREADCRUMBS_WIDGET", - topRow: 6, - leftColumn: 2, - bottomRow: 5, - rightColumn: 5, - width: "100%", - collapseFrom: "start", - className: "breadcrumbs", - size: 20, - renderMode: RenderModes.CANVAS, - items: [ - { icon: "folder-close", text: "All files" }, - { icon: "folder-close", text: "Users" }, - { icon: "folder-close", text: "Janet" }, - { href: "#", icon: "folder-close", text: "Photos" }, - { href: "#", icon: "folder-close", text: "Wednesday" }, - { icon: "document", text: "image.jpg" } - ] - }, - { - widgetId: "8", - widgetType: "TAG_INPUT_WIDGET", - topRow: 7, - leftColumn: 1, - bottomRow: 5, - rightColumn: 5, - palceholder: "Lorem, Ipsum", - values: ["abx", "somf", "soke"], - renderMode: RenderModes.CANVAS - }, - { - widgetId: "9", - widgetType: "NUMERIC_INPUT_WIDGET", - topRow: 4, - leftColumn: 1, - bottomRow: 5, - rightColumn: 5, - palceholder: "Numeric input", - allowNumericCharactersOnly: true, - renderMode: RenderModes.CANVAS - }, - { - widgetId: "10", - widgetType: "CHECKBOX_WIDGET", - topRow: 6, - leftColumn: 1, - bottomRow: 5, - rightColumn: 5, - items: [ - { - label: "a", - value: 1 - }, - { - label: "b", - value: 2 - }, - { - label: "c", - value: 3 - } - ] - }, - { - widgetId: "11", - widgetType: "RADIO_GROUP_WIDGET", - topRow: 6, - leftColumn: 1, - bottomRow: 5, - rightColumn: 5, - items: [ - { - label: "a", - value: 1 - }, - { - label: "b", - value: 2 - }, - { - label: "c", - value: 3 - } - ] - } - ] -} - -export default CanvasResponse diff --git a/app/client/src/mockResponses/WidgetCardsPaneResponse.tsx b/app/client/src/mockResponses/WidgetCardsPaneResponse.tsx new file mode 100644 index 0000000000..93bea8c9fc --- /dev/null +++ b/app/client/src/mockResponses/WidgetCardsPaneResponse.tsx @@ -0,0 +1,78 @@ +import { WidgetCardsPaneReduxState } from "../reducers/uiReducers/widgetCardsPaneReducer"; + +const WidgetCardsPaneResponse: WidgetCardsPaneReduxState = { + cards: { + common: [ + { + widgetType: "BUTTON_WIDGET", + icon: "\f243", + label: "Button", + groups: ["common", "form"] + }, + { + widgetType: "INPUT_WIDGET", + icon: "\f243", + label: "Input", + groups: ["common", "form"] + }, + { + widgetType: "TOGGLE_WIDGET", + icon: "\f205", + label: "Toggle", + groups: ["common", "view"] + } + ], + form: [ + { + widgetType: "BUTTON_WIDGET", + icon: "\f243", + label: "Button", + groups: ["common", "form"] + }, + { + widgetType: "INPUT_WIDGET", + icon: "\f243", + label: "Input", + groups: ["common", "form"] + } + ], + view: [ + { + widgetType: "TOGGLE_WIDGET", + icon: "\f205", + label: "Toggle", + groups: ["common", "view"] + } + ] + } +}; +// const WidgetPaneResponse: WidgetPaneReduxState = { +// widgets: [ +// { +// widgetType: "BUTTON_WIDGET", +// text: "Lorem Ipsum", +// renderMode: RenderModes.COMPONENT_PANE, +// bottomRow: 50, +// widgetId: "wp1", +// rightColumn: 200 +// }, +// { +// widgetType: "TEXT_WIDGET", +// text: "Lorem Ipsum", +// renderMode: RenderModes.COMPONENT_PANE, +// bottomRow: 50, +// widgetId: "wp2", +// rightColumn: 200 +// }, +// { +// widgetType: "SPINNER_WIDGET", +// renderMode: RenderModes.COMPONENT_PANE, +// backgroundColor: "#434343", +// bottomRow: 50, +// widgetId: "wp3", +// rightColumn: 200 +// } +// ] +// } + +export default WidgetCardsPaneResponse; diff --git a/app/client/src/mockResponses/WidgetPaneResponse.tsx b/app/client/src/mockResponses/WidgetPaneResponse.tsx deleted file mode 100644 index dd2f662284..0000000000 --- a/app/client/src/mockResponses/WidgetPaneResponse.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { WidgetPaneReduxState } from "../reducers/uiReducers/widgetPaneReducer" -import { RenderModes } from "../constants/WidgetConstants" - -const WidgetPaneResponse: WidgetPaneReduxState = { - widgets: [ - { - widgetType: "BUTTON_WIDGET", - text: "Lorem Ipsum", - renderMode: RenderModes.COMPONENT_PANE, - bottomRow: 50, - widgetId: "1", - rightColumn: 200 - }, - { - widgetType: "TEXT_WIDGET", - text: "Lorem Ipsum", - renderMode: RenderModes.COMPONENT_PANE, - bottomRow: 50, - widgetId: "2", - rightColumn: 200 - }, - { - widgetType: "SPINNER_WIDGET", - renderMode: RenderModes.COMPONENT_PANE, - backgroundColor: "#434343", - bottomRow: 50, - widgetId: "3", - rightColumn: 200 - } - ] -} - -export default WidgetPaneResponse diff --git a/app/client/src/normalizers/CanvasWidgetsNormalizer.tsx b/app/client/src/normalizers/CanvasWidgetsNormalizer.tsx index 25220b4d49..50c694f71d 100644 --- a/app/client/src/normalizers/CanvasWidgetsNormalizer.tsx +++ b/app/client/src/normalizers/CanvasWidgetsNormalizer.tsx @@ -1,10 +1,10 @@ import { normalize, schema, denormalize } from 'normalizr'; import { PageResponse } from '../api/PageApi'; -import { IContainerWidgetProps } from '../widgets/ContainerWidget'; +import { ContainerWidgetProps } from '../widgets/ContainerWidget'; -export const widgetSchema = new schema.Entity('canvasWidgets', { }, { idAttribute: "widgetId" }, ); -const widgets = new schema.Array(widgetSchema); -widgetSchema.define({ children: widgets }); +export const widgetSchema = new schema.Entity('canvasWidgets', { }, { idAttribute: "widgetId" }); +// const widgets = new schema.Array(widgetSchema); +widgetSchema.define({ children: [widgetSchema] }); class CanvasWidgetsNormalizer { @@ -12,7 +12,7 @@ class CanvasWidgetsNormalizer { return normalize(pageResponse.pageWidget, widgetSchema) } - static denormalize(pageWidgetId: string, entities: any): IContainerWidgetProps { + static denormalize(pageWidgetId: string, entities: any): ContainerWidgetProps { return denormalize(pageWidgetId, widgetSchema, entities) } diff --git a/app/client/src/pages/Editor/Canvas.tsx b/app/client/src/pages/Editor/Canvas.tsx index 52bd51da74..3a23ee369a 100644 --- a/app/client/src/pages/Editor/Canvas.tsx +++ b/app/client/src/pages/Editor/Canvas.tsx @@ -1,46 +1,82 @@ -import React, { Component } from "react" -import { connect } from "react-redux" -import { AppState } from "../../reducers" +import React, { MutableRefObject, useLayoutEffect } from "react" +import styled from "styled-components" import WidgetFactory from "../../utils/WidgetFactory" -import CanvasWidgetsNormalizer, { widgetSchema } from "../../normalizers/CanvasWidgetsNormalizer"; -import { IContainerWidgetProps } from "../../widgets/ContainerWidget"; -import { fetchPage } from "../../actions/pageActions"; -import { RenderModes } from "../../constants/WidgetConstants"; +import { WidgetTypes } from "../../constants/WidgetConstants" +import { DraggableWidget } from "../../widgets/BaseWidget" +import { useDrop } from "react-dnd" +import { ContainerWidgetProps } from "../../widgets/ContainerWidget" +import EditorDragLayer from "./EditorDragLayer" -class Canvas extends Component<{ pageWidget: IContainerWidgetProps, fetchPage: Function }> { +const ArtBoardBackgroundMask = styled.div` + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: -10; +`; - componentDidMount() { - this.props.fetchPage("123") - } +const ArtBoard = styled.div` + width: 100%; + height: 100%; + position:relative; + overflow: auto; + background: linear-gradient( + 90deg, + transparent, + transparent 1px, + #ffffff 1px, + #ffffff 63px, + transparent 63px, + transparent 100%), + linear-gradient( + transparent, + transparent 1px, + #ffffff 1px, + #ffffff 63px, + transparent 63px, + transparent 100%), black; +background-size: 64px 64px; +background-position:0 0; +`; - render() { - const pageWidget = this.props.pageWidget - return ( -
- {pageWidget - ? WidgetFactory.createWidget(pageWidget) - : undefined} -
- ) - } +interface CanvasProps { + pageWidget: ContainerWidgetProps; + addWidget: Function; } -const mapStateToProps = (state: AppState, props: any) => { - const pageWidget = CanvasWidgetsNormalizer.denormalize(state.ui.canvas.pageWidgetId, state.entities) - return { - pageWidget: pageWidget - } +interface ArtBoardProps { + cellSize: string; } -const mapDispatchToProps = (dispatch: any) => { - return { - fetchPage: (pageId: string) => { - return dispatch(fetchPage(pageId, RenderModes.CANVAS)) +const Canvas = (props: CanvasProps) => { + const [width, setWidth] = React.useState(1) + const artBoardMask: MutableRefObject = React.useRef(null) + const [, drop] = useDrop({ + accept: Object.values(WidgetTypes), + drop(item: DraggableWidget) { + props.addWidget(item.type, item.key); + return undefined + }, + }) + + useLayoutEffect(()=> { + const el = artBoardMask.current + if (el) { + const rect = el.getBoundingClientRect() + setWidth(rect.width) } - } + }, [setWidth]) + + return ( + + + + + {props.pageWidget && WidgetFactory.createWidget(props.pageWidget)} + + + ) } -export default connect( - mapStateToProps, - mapDispatchToProps -)(Canvas) +export default Canvas \ No newline at end of file diff --git a/app/client/src/pages/Editor/EditorDragLayer.tsx b/app/client/src/pages/Editor/EditorDragLayer.tsx new file mode 100644 index 0000000000..b77237bcaa --- /dev/null +++ b/app/client/src/pages/Editor/EditorDragLayer.tsx @@ -0,0 +1,88 @@ +import React from 'react' +import styled from 'styled-components' +import { XYCoord, useDragLayer } from 'react-dnd' +import snapToGrid from './snapToGrid' +import WidgetFactory from '../../utils/WidgetFactory'; +import { RenderModes, WidgetType } from '../../constants/WidgetConstants'; + +const WrappedDragLayer = styled.div` + position: absolute; + pointer-events: none; + z-index: 100; + left: 0; + top: 0; + width: 100%; + height: 100%; + border: 10px solid #000; +`; + + +function getItemStyles( + initialOffset: XYCoord | null, + currentOffset: XYCoord | null +) { + if (!initialOffset || !currentOffset) { + return { + display: 'none', + } + } + + let { x, y } = currentOffset + + x -= initialOffset.x + y -= initialOffset.y + ;[x, y] = snapToGrid(64, x, y) + x += initialOffset.x + y += initialOffset.y + + const transform = `translate(${x}px, ${y}px)` + return { + transform, + WebkitTransform: transform, + } +} + + +const EditorDragLayer: React.FC = () => { + const { + itemType, + isDragging, + item, + initialOffset, + currentOffset, + } = useDragLayer(monitor => ({ + item: monitor.getItem(), + itemType: monitor.getItemType(), + initialOffset: monitor.getInitialSourceClientOffset(), + currentOffset: monitor.getSourceClientOffset(), + isDragging: monitor.isDragging(), + })) + + function renderItem() { + return WidgetFactory.createWidget({ + widgetType: itemType as WidgetType, + widgetId: item.key, + topRow: 10, + leftColumn: 10, + bottomRow: 14, + rightColumn: 20, + parentColumnSpace: 1, + parentRowSpace: 1, + renderMode: RenderModes.CANVAS + }) + } + + if (!isDragging) { + return null + } + return ( + +
+ {renderItem()} +
+
+ ) +} +export default EditorDragLayer diff --git a/app/client/src/pages/Editor/EditorHeader.tsx b/app/client/src/pages/Editor/EditorHeader.tsx new file mode 100644 index 0000000000..b01e8d5d0c --- /dev/null +++ b/app/client/src/pages/Editor/EditorHeader.tsx @@ -0,0 +1,34 @@ +import React, { Component } from "react" +import styled from "styled-components" +import { connect } from "react-redux" +import { AppState } from "../../reducers" +import { EditorHeaderReduxState } from "../../reducers/uiReducers/editorHeaderReducer"; + +const Header = styled.header` + height: 50px; + box-shadow: 0px 0px 3px #ccc; + background: #fff; +`; + + +class EditorHeader extends Component { + render() { + return ( +
+
+ ) + } +} + +const mapStateToProps = (state: AppState, props: any): EditorHeaderReduxState => { + return {} +} + +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 new file mode 100644 index 0000000000..63f273f7af --- /dev/null +++ b/app/client/src/pages/Editor/WidgetCard.tsx @@ -0,0 +1,76 @@ +import React, { useState, useLayoutEffect, MutableRefObject } from 'react'; +import { useDrag, DragSourceMonitor, DragPreviewImage } from 'react-dnd' +import blankImage from "../../assets/images/blank.png" +import { IWidgetCardProps } from '../../widgets/BaseWidget' +import styled from 'styled-components' +import { Icon } from '@blueprintjs/core' +import { IconNames } from '@blueprintjs/icons' +import { generateReactKey } from "../../utils/generators" + + +type WidgetCardProps = { + details: IWidgetCardProps; +} + +export const Wrapper = styled.div` + display:flex; + flex-direction: row; + justify-content: center; + align-items: center; + flex: 1; + padding: 10px 5px 10px 5px; + margin: 0px 10px 0 0; + border-radius: 5px; + background: #eee + border: 1px solid #eee; + &:hover{ + background: #fff; + cursor: grab; + } +`; +export const IconLabel = styled.h5` + text-align: center; + padding: 10px 0; + margin: 0; + text-transform: uppercase; + font-weight: normal; + flex-shrink: 1; + font-size: 0.5rem; +`; + +/* eslint-disable @typescript-eslint/no-unused-vars */ +const WidgetCard = (props: WidgetCardProps) => { + const [initialOffset, setInitialOffset] = useState({ x: 0, y: 0}) + + const [{ isDragging }, drag, preview] = useDrag({ + item: { type: props.details.widgetType, widget: props.details, key: generateReactKey(), initialOffset}, + collect: (monitor: DragSourceMonitor) => ({ + isDragging: monitor.isDragging(), + }), + }) + const card: MutableRefObject = React.useRef(null) + useLayoutEffect(()=> { + const el = card.current + if (el) { + const rect = el.getBoundingClientRect() + setInitialOffset({ + x: Math.ceil(rect.left), + y: Math.ceil(rect.top) + }) + } + }, [setInitialOffset]) + + return ( + + + +
+ + {props.details.label} +
+
+
+ ) +} + +export default WidgetCard \ No newline at end of file diff --git a/app/client/src/pages/Editor/WidgetCardsPane.tsx b/app/client/src/pages/Editor/WidgetCardsPane.tsx new file mode 100644 index 0000000000..b2bba361cc --- /dev/null +++ b/app/client/src/pages/Editor/WidgetCardsPane.tsx @@ -0,0 +1,42 @@ +import React from "react" +import WidgetCard from "./WidgetCard" +import styled from "styled-components" +import { IWidgetCardProps } from "../../widgets/BaseWidget" + +type WidgetCardPaneProps = { + cards: { [id: string]: IWidgetCardProps[]}; +} + +const CardsPaneWrapper = styled.div` + width: 300px; + background-color: #fff; + border-radius: 5px; + box-shadow: 0px 0px 3px #ccc; + padding: 5px 10px; +`; + +const CardsWrapper = styled.div` + display: flex; + flex-flow: row wrap; + justify-content: flex-start; +`; + +const WidgetCardsPane: React.SFC = (props: WidgetCardPaneProps) => { + const groups = Object.keys(props.cards) + return ( + + { + groups.map((group: string) => + +
{group}
+ + { props.cards[group].map((card: IWidgetCardProps) => ) } + +
+ ) + } +
+ ) +} + +export default WidgetCardsPane \ No newline at end of file diff --git a/app/client/src/pages/Editor/WidgetPane.tsx b/app/client/src/pages/Editor/WidgetPane.tsx deleted file mode 100644 index c2b2e5697d..0000000000 --- a/app/client/src/pages/Editor/WidgetPane.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React, { Component } from "react" -import { connect } from "react-redux" -import { AppState } from "../../reducers" -import WidgetFactory from "../../utils/WidgetFactory" -import { WidgetPaneReduxState } from "../../reducers/uiReducers/widgetPaneReducer"; -import { IWidgetProps } from "../../widgets/BaseWidget"; - -class WidgetPane extends Component { - render() { - return (
- {this.props.widgets.map((widget: IWidgetProps) => { - return WidgetFactory.createWidget(widget) - })} -
) - } -} - -const mapStateToProps = (state: AppState, props: any): WidgetPaneReduxState => { - return { - widgets: state.ui.widgetPane.widgets - } -} - -const mapDispatchToProps = (dispatch: any) => { - return {} -} - -export default connect( - mapStateToProps, - mapDispatchToProps -)(WidgetPane) diff --git a/app/client/src/pages/Editor/index.tsx b/app/client/src/pages/Editor/index.tsx index 7d5ae1c9d1..4a57fe8573 100644 --- a/app/client/src/pages/Editor/index.tsx +++ b/app/client/src/pages/Editor/index.tsx @@ -1,16 +1,110 @@ import React, { Component } from "react" +import { connect } from "react-redux" +import styled from "styled-components" import Canvas from "./Canvas" -import WidgetPane from "./WidgetPane" +import { IWidgetCardProps, IWidgetProps } from '../../widgets/BaseWidget' +import { AppState } from "../../reducers" +import { EditorReduxState } from "../../reducers/uiReducers/editorReducer" +import WidgetCardsPane from "./WidgetCardsPane" +import EditorHeader from "./EditorHeader" +import { WidgetType } from "../../constants/WidgetConstants" +import CanvasWidgetsNormalizer from "../../normalizers/CanvasWidgetsNormalizer" +import { ContainerWidgetProps } from "../../widgets/ContainerWidget" +import { fetchPage, addWidget } from "../../actions/pageActions" +import { RenderModes } from "../../constants/WidgetConstants" +// import EditorDragLayer from "./EditorDragLayer" -class Editor extends Component { - render() { +const CanvasContainer = styled.section` + height: 100%; + width: 100%; + position: relative; + overflow-x: hidden; + overflow-y: auto; + margin: 0px 10px; + &:before { + position: absolute; + top: 0; right: 0; bottom: 0; left: 0; + z-index: 1000; + pointer-events: none; + } +`; + +const EditorWrapper = styled.div` + display: flex; + flex-direction: row; + align-items: stretch; + justify-content: flex-start; + width: 100vw; + overflow: hidden; + padding: 10px; + height: calc(100vh - 60px); +`; + +type EditorProps = { + pageWidget: ContainerWidgetProps | any; + fetchCanvasWidgets: Function; + cards: { [id: string]: IWidgetCardProps[] } | any; + addPageWidget: Function; + page: string; +} + +class Editor extends Component { + componentDidMount() { + this.props.fetchCanvasWidgets("1") + } + + addWidgetToCanvas = (widgetType: WidgetType, key: string): void => { + this.props.addPageWidget("1", { + key: key, + bottomRow: 9, + leftColumn: 1, + parentColumnSpace: 90, + parentRowSpace: 50, + renderMode: RenderModes.CANVAS, + rightColumn: 3, + snapColumns: 20, + snapRows: 20, + children: [], + topRow: 1, + widgetId: key, + widgetType: widgetType + }) + } + + public render() { return ( -
- - -
+ + + + + + + + + ) } } -export default Editor +const mapStateToProps = (state: AppState, props: EditorProps): EditorReduxState => { + const pageWidget = CanvasWidgetsNormalizer.denormalize( + state.ui.canvas.pageWidgetId, + state.entities + ) + return { + cards: state.ui.widgetCardsPane.cards, + pageWidget, + } +} + +const mapDispatchToProps = (dispatch: any) => { + return { + fetchCanvasWidgets: (pageId: string) => dispatch(fetchPage(pageId, RenderModes.CANVAS)), + addPageWidget: (pageId: string, widgetProps: IWidgetProps) => dispatch(addWidget(pageId, widgetProps)) + } +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Editor) diff --git a/app/client/src/pages/Editor/snapToGrid.ts b/app/client/src/pages/Editor/snapToGrid.ts new file mode 100644 index 0000000000..1c0a3ffa3d --- /dev/null +++ b/app/client/src/pages/Editor/snapToGrid.ts @@ -0,0 +1,5 @@ +export default function snapToGrid(cellSize: number, x: number, y: number) { + const snappedX = Math.round(x / cellSize) * cellSize + const snappedY = Math.round(y / cellSize) * cellSize + return [snappedX, snappedY] +} diff --git a/app/client/src/reducers/entityReducers/canvasWidgetsReducers.tsx b/app/client/src/reducers/entityReducers/canvasWidgetsReducers.tsx index 1c5086f71c..b8cc800362 100644 --- a/app/client/src/reducers/entityReducers/canvasWidgetsReducers.tsx +++ b/app/client/src/reducers/entityReducers/canvasWidgetsReducers.tsx @@ -5,20 +5,13 @@ import { ReduxAction } from "../../constants/ActionConstants" import { IWidgetProps } from "../../widgets/BaseWidget" -import { RenderModes } from "../../constants/WidgetConstants" +import CanvasWidgetsNormalizer from "../../normalizers/CanvasWidgetsNormalizer"; -const initialState: CanvasWidgetsReduxState = { - "0": { - widgetId: "0", - widgetType: "CONTAINER_WIDGET", - topRow: 100, - bottomRow: 700, - leftColumn: 100, - rightColumn: 800, - parentColumnSpace: 1, - parentRowSpace: 1, - renderMode: RenderModes.CANVAS - } +const initialState: CanvasWidgetsReduxState = {} + + +export interface IFlattenedWidgetProps extends IWidgetProps { + children?: string[]; } const canvasWidgetsReducer = createReducer(initialState, { @@ -27,11 +20,26 @@ const canvasWidgetsReducer = createReducer(initialState, { action: ReduxAction ) => { return { ...action.payload.widgets } + }, + [ActionTypes.ADD_PAGE_WIDGET]: ( + state: CanvasWidgetsReduxState, + action: ReduxAction<{pageId: string, widget: IWidgetProps}> + ) => { + const widget = action.payload.widget + const widgetTree = CanvasWidgetsNormalizer.denormalize("0", { canvasWidgets: state }) + const children = widgetTree.children || [] + children.push(widget) + widgetTree.children = children + const newState = CanvasWidgetsNormalizer.normalize({ + responseMeta: {}, + pageWidget: widgetTree + }).entities + return newState.canvasWidgets } }) export interface CanvasWidgetsReduxState { - [widgetId: string]: IWidgetProps + [widgetId: string]: IFlattenedWidgetProps; } export default canvasWidgetsReducer diff --git a/app/client/src/reducers/index.tsx b/app/client/src/reducers/index.tsx index 2386a3311a..5b42b897f5 100644 --- a/app/client/src/reducers/index.tsx +++ b/app/client/src/reducers/index.tsx @@ -3,7 +3,9 @@ import entityReducer from "./entityReducers" import uiReducer from "./uiReducers" import { CanvasReduxState } from "./uiReducers/canvasReducer" import { CanvasWidgetsReduxState } from "./entityReducers/canvasWidgetsReducers" -import { WidgetPaneReduxState } from "./uiReducers/widgetPaneReducer" +import { WidgetCardsPaneReduxState } from "./uiReducers/widgetCardsPaneReducer" +import { EditorHeaderReduxState } from "./uiReducers/editorHeaderReducer" +import { EditorReduxState } from "./uiReducers/editorReducer" const appReducer = combineReducers({ entities: entityReducer, @@ -15,7 +17,9 @@ export default appReducer export interface AppState { ui: { canvas: CanvasReduxState - widgetPane: WidgetPaneReduxState + widgetCardsPane: WidgetCardsPaneReduxState + editorHeader: EditorHeaderReduxState + editor: EditorReduxState } entities: { canvasWidgets: CanvasWidgetsReduxState diff --git a/app/client/src/reducers/uiReducers/editorHeaderReducer.tsx b/app/client/src/reducers/uiReducers/editorHeaderReducer.tsx new file mode 100644 index 0000000000..f8b029484f --- /dev/null +++ b/app/client/src/reducers/uiReducers/editorHeaderReducer.tsx @@ -0,0 +1,9 @@ +import { createReducer } from "../../utils/AppsmithUtils" + +const initialState: EditorHeaderReduxState = {} + +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 new file mode 100644 index 0000000000..366b2a9972 --- /dev/null +++ b/app/client/src/reducers/uiReducers/editorReducer.tsx @@ -0,0 +1,41 @@ +import { createReducer } from "../../utils/AppsmithUtils" +import { + ActionTypes, + ReduxAction, + LoadCanvasPayload, + LoadWidgetCardsPanePayload +} from "../../constants/ActionConstants" +import { IWidgetCardProps, IWidgetProps } from "../../widgets/BaseWidget" +import { ContainerWidgetProps } from "../../widgets/ContainerWidget" + +const initialState: EditorReduxState = {} + +const editorReducer = createReducer(initialState, { + [ActionTypes.SUCCESS_FETCHING_WIDGET_CARDS]: ( + state: EditorReduxState, + action: ReduxAction + ) => { + return { ...state.pageWidget, ...action.payload } + }, + [ActionTypes.ADD_PAGE_WIDGET]: ( + state: EditorReduxState, + action: ReduxAction<{pageId: string, widget: IWidgetProps}> + ) => { + return state + }, + [ActionTypes.UPDATE_CANVAS]: ( + state: EditorReduxState, + action: ReduxAction + ) => { + return { pageWidgetId: action.payload.pageWidgetId } + } +}) + +export interface EditorReduxState { + pageWidget?: ContainerWidgetProps; + cards?: { + [id: string]: IWidgetCardProps[]; + }; +} + +export default editorReducer diff --git a/app/client/src/reducers/uiReducers/index.tsx b/app/client/src/reducers/uiReducers/index.tsx index e2c410602d..723df6ce0f 100644 --- a/app/client/src/reducers/uiReducers/index.tsx +++ b/app/client/src/reducers/uiReducers/index.tsx @@ -1,6 +1,8 @@ import { combineReducers } from "redux" import canvasReducer from "./canvasReducer" -import widgetPaneReducer from "./widgetPaneReducer"; +import widgetCardsPaneReducer from "./widgetCardsPaneReducer" +import editorHeaderReducer from "./editorHeaderReducer" +import editorReducer from "./editorReducer" -const uiReducer = combineReducers({ canvas: canvasReducer, widgetPane: widgetPaneReducer }) +const uiReducer = combineReducers({ canvas: canvasReducer, widgetCardsPane: widgetCardsPaneReducer, editorHeader: editorHeaderReducer, editor: editorReducer }) export default uiReducer diff --git a/app/client/src/reducers/uiReducers/widgetCardsPaneReducer.tsx b/app/client/src/reducers/uiReducers/widgetCardsPaneReducer.tsx new file mode 100644 index 0000000000..5ada95d5cb --- /dev/null +++ b/app/client/src/reducers/uiReducers/widgetCardsPaneReducer.tsx @@ -0,0 +1,27 @@ +import { createReducer } from "../../utils/AppsmithUtils" +import { + ActionTypes, + ReduxAction, + LoadWidgetCardsPanePayload +} from "../../constants/ActionConstants" +import { IWidgetCardProps } from "../../widgets/BaseWidget"; +import WidgetCardsPaneResponse from "../../mockResponses/WidgetCardsPaneResponse" + +const initialState: WidgetCardsPaneReduxState = WidgetCardsPaneResponse + +const widgetCardsPaneReducer = createReducer(initialState, { + [ActionTypes.ERROR_FETCHING_WIDGET_CARDS]: ( + state: WidgetCardsPaneReduxState, + action: ReduxAction + ) => { + return { cards: action.payload.cards } + } +}) + +export interface WidgetCardsPaneReduxState { + cards: { + [id: string]: IWidgetCardProps[] + } +} + +export default widgetCardsPaneReducer diff --git a/app/client/src/reducers/uiReducers/widgetPaneReducer.tsx b/app/client/src/reducers/uiReducers/widgetPaneReducer.tsx deleted file mode 100644 index 6f0235587b..0000000000 --- a/app/client/src/reducers/uiReducers/widgetPaneReducer.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { createReducer } from "../../utils/AppsmithUtils" -import { - ActionTypes, - ReduxAction, - LoadWidgetPanePayload -} from "../../constants/ActionConstants" -import { IWidgetProps } from "../../widgets/BaseWidget"; -import WidgetPaneResponse from "../../mockResponses/WidgetPaneResponse" - -const initialState: WidgetPaneReduxState = WidgetPaneResponse - -const widgetPaneReducer = createReducer(initialState, { - // [ActionTypes.LOAD_CANVAS]: ( - // state: WidgetPaneReduxState, - // action: ReduxAction - // ) => { - // return { widgets: action.payload.widgets } - // } -}) - -export interface WidgetPaneReduxState { - widgets: (IWidgetProps | any)[] -} - -export default widgetPaneReducer diff --git a/app/client/src/sagas/PageSagas.tsx b/app/client/src/sagas/PageSagas.tsx index 8a8bb28a6e..1f9fbbf612 100644 --- a/app/client/src/sagas/PageSagas.tsx +++ b/app/client/src/sagas/PageSagas.tsx @@ -1,19 +1,23 @@ import CanvasWidgetsNormalizer from "../normalizers/CanvasWidgetsNormalizer" import { ActionTypes, ReduxAction } from "../constants/ActionConstants" import PageApi, { PageResponse, PageRequest } from "../api/PageApi" -import { call, put, takeLeading, all, takeEvery } from "redux-saga/effects" +import { call, put, takeEvery } from "redux-saga/effects" import { RenderModes } from "../constants/WidgetConstants" export function* fetchPageSaga(pageRequestAction: ReduxAction) { const pageRequest = pageRequestAction.payload - const pageResponse: PageResponse = yield call(PageApi.fetchPage, pageRequest) - if (pageRequest.renderMode === RenderModes.CANVAS) { - const normalizedResponse = CanvasWidgetsNormalizer.normalize(pageResponse) - const payload = { - pageWidgetId: normalizedResponse.result, - widgets: normalizedResponse.entities.canvasWidgets + try { + const pageResponse: PageResponse = yield call(PageApi.fetchPage, pageRequest) + if (pageRequest.renderMode === RenderModes.CANVAS) { + const normalizedResponse = CanvasWidgetsNormalizer.normalize(pageResponse) + const payload = { + pageWidgetId: normalizedResponse.result, + widgets: normalizedResponse.entities.canvasWidgets + } + yield put({ type: ActionTypes.UPDATE_CANVAS, payload }) } - yield put({ type: ActionTypes.UPDATE_CANVAS, payload }) + } catch(err){ + //TODO(abhinav): REFACTOR THIS } } diff --git a/app/client/src/sagas/WidgetCardsPaneSagas.tsx b/app/client/src/sagas/WidgetCardsPaneSagas.tsx new file mode 100644 index 0000000000..54c64940f7 --- /dev/null +++ b/app/client/src/sagas/WidgetCardsPaneSagas.tsx @@ -0,0 +1,19 @@ +// import CanvasWidgetsNormalizer from "../normalizers/CanvasWidgetsNormalizer" +import { ActionTypes, ReduxAction } from "../constants/ActionConstants" +import WidgetCardsPaneApi, { WidgetCardsPaneResponse, WidgetCardsPaneRequest } from "../api/WidgetCardsPaneApi" +import { successFetchingWidgetCards } from "../actions/widgetCardsPaneActions" +import { call, put, takeLatest } from "redux-saga/effects" + + +export function* fetchWidgetCards(widgetCardsRequestAction: ReduxAction) { + try { + const widgetCards: WidgetCardsPaneResponse = yield call(WidgetCardsPaneApi.fetchWidgetCards) + yield put(successFetchingWidgetCards(widgetCards.cards)) + } catch(err) { + yield put({ type: ActionTypes.ERROR_FETCHING_WIDGET_CARDS, err}) + } +} + +export function* fetchWidgetCardsSaga() { + yield takeLatest(ActionTypes.FETCH_WIDGET_CARDS, fetchWidgetCards) +} \ No newline at end of file diff --git a/app/client/src/sagas/index.tsx b/app/client/src/sagas/index.tsx index 76ee3d0f52..c86314dc04 100644 --- a/app/client/src/sagas/index.tsx +++ b/app/client/src/sagas/index.tsx @@ -1,6 +1,7 @@ import { all } from "redux-saga/effects" import { watchFetchPage } from "../sagas/PageSagas" +import { fetchWidgetCardsSaga } from './WidgetCardsPaneSagas' export function* rootSaga() { - yield all([watchFetchPage()]) + yield all([watchFetchPage(), fetchWidgetCardsSaga()]) } diff --git a/app/client/src/utils/WidgetFactory.tsx b/app/client/src/utils/WidgetFactory.tsx index d0a4d57c6b..2412741a0d 100644 --- a/app/client/src/utils/WidgetFactory.tsx +++ b/app/client/src/utils/WidgetFactory.tsx @@ -15,17 +15,21 @@ class WidgetFactory { if (widgetBuilder) return widgetBuilder.buildWidget(widgetData) else { - const ex: IWidgetCreationException = { + const ex: WidgetCreationException = { message: "Widget Builder not registered for widget type" + widgetData.widgetType } throw ex } } + static getWidgetTypes(): WidgetType[] { + return Array.from(this.widgetMap.keys()); + } + } -export interface IWidgetCreationException { - message: string +export interface WidgetCreationException { + message: string; } export default WidgetFactory \ No newline at end of file diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx index 06049d5fc2..1971eec473 100644 --- a/app/client/src/utils/WidgetRegistry.tsx +++ b/app/client/src/utils/WidgetRegistry.tsx @@ -1,104 +1,105 @@ -import BaseWidget, { IWidgetProps } from "../widgets/BaseWidget" +import { IWidgetProps } from "../widgets/BaseWidget" import ContainerWidget, { - IContainerWidgetProps + ContainerWidgetProps } from "../widgets/ContainerWidget" -import TextWidget, { ITextWidgetProps } from "../widgets/TextWidget" +import TextWidget, { TextWidgetProps } from "../widgets/TextWidget" import InputGroupWidget, { - IInputGroupWidgetProps + InputGroupWidgetProps } from "../widgets/InputGroupWidget" -import CalloutWidget, { ICalloutWidgetProps } from "../widgets/CalloutWidget" -import IconWidget, { IIconWidgetProps } from "../widgets/IconWidget" -import SpinnerWidget, { ISpinnerWidgetProps } from "../widgets/SpinnerWidget" +import CalloutWidget, { CalloutWidgetProps } from "../widgets/CalloutWidget" +import IconWidget, { IconWidgetProps } from "../widgets/IconWidget" +import SpinnerWidget, { SpinnerWidgetProps } from "../widgets/SpinnerWidget" import BreadcrumbsWidget, { - IBreadcrumbsWidgetProps + BreadcrumbsWidgetProps } from "../widgets/BreadcrumbsWidget" -import TagInputWidget, { ITagInputWidgetProps } from "../widgets/TagInputWidget" +import TagInputWidget, { TagInputWidgetProps } from "../widgets/TagInputWidget" import NumericInputWidget, { - INumericInputWidgetProps + NumericInputWidgetProps } from "../widgets/NumericInputWidget" -import CheckboxWidget, { ICheckboxWidgetProps } from "../widgets/CheckboxWidget" +import CheckboxWidget, { CheckboxWidgetProps } from "../widgets/CheckboxWidget" import RadioGroupWidget, { - IRadioGroupWidgetProps + RadioGroupWidgetProps } from "../widgets/RadioGroupWidget" import WidgetFactory from "./WidgetFactory" import React from "react" -import ButtonWidget, { IButtonWidgetProps } from "../widgets/ButtonWidget" +import ButtonWidget, { ButtonWidgetProps } from "../widgets/ButtonWidget" class WidgetBuilderRegistry { static registerWidgetBuilders() { WidgetFactory.registerWidgetBuilder("CONTAINER_WIDGET", { buildWidget( - widgetData: IContainerWidgetProps + widgetData: ContainerWidgetProps ): JSX.Element { return } }) WidgetFactory.registerWidgetBuilder("TEXT_WIDGET", { - buildWidget(widgetData: ITextWidgetProps): JSX.Element { + buildWidget(widgetData: TextWidgetProps): JSX.Element { return } }) WidgetFactory.registerWidgetBuilder("BUTTON_WIDGET", { - buildWidget(widgetData: IButtonWidgetProps): JSX.Element { + buildWidget(widgetData: ButtonWidgetProps): JSX.Element { return } }) WidgetFactory.registerWidgetBuilder("CALLOUT_WIDGET", { - buildWidget(widgetData: ICalloutWidgetProps): JSX.Element { + buildWidget(widgetData: CalloutWidgetProps): JSX.Element { return } }) WidgetFactory.registerWidgetBuilder("ICON_WIDGET", { - buildWidget(widgetData: IIconWidgetProps): JSX.Element { + buildWidget(widgetData: IconWidgetProps): JSX.Element { return } }) WidgetFactory.registerWidgetBuilder("SPINNER_WIDGET", { - buildWidget(widgetData: ISpinnerWidgetProps): JSX.Element { + buildWidget(widgetData: SpinnerWidgetProps): JSX.Element { return } }) WidgetFactory.registerWidgetBuilder("INPUT_GROUP_WIDGET", { - buildWidget(widgetData: IInputGroupWidgetProps): JSX.Element { + buildWidget(widgetData: InputGroupWidgetProps): JSX.Element { return } }) WidgetFactory.registerWidgetBuilder("BREADCRUMBS_WIDGET", { - buildWidget(widgetData: IBreadcrumbsWidgetProps): JSX.Element { + buildWidget(widgetData: BreadcrumbsWidgetProps): JSX.Element { return } }) WidgetFactory.registerWidgetBuilder("TAG_INPUT_WIDGET", { - buildWidget(widgetData: ITagInputWidgetProps): JSX.Element { + buildWidget(widgetData: TagInputWidgetProps): JSX.Element { return } }) WidgetFactory.registerWidgetBuilder("NUMERIC_INPUT_WIDGET", { - buildWidget(widgetData: INumericInputWidgetProps): JSX.Element { + buildWidget(widgetData: NumericInputWidgetProps): JSX.Element { return } }) WidgetFactory.registerWidgetBuilder("CHECKBOX_WIDGET", { - buildWidget(widgetData: ICheckboxWidgetProps): JSX.Element { + buildWidget(widgetData: CheckboxWidgetProps): JSX.Element { return } }) WidgetFactory.registerWidgetBuilder("RADIO_GROUP_WIDGET", { - buildWidget(widgetData: IRadioGroupWidgetProps): JSX.Element { + buildWidget(widgetData: RadioGroupWidgetProps): JSX.Element { return } }) + } } diff --git a/app/client/src/utils/generators.tsx b/app/client/src/utils/generators.tsx new file mode 100644 index 0000000000..78fb663915 --- /dev/null +++ b/app/client/src/utils/generators.tsx @@ -0,0 +1,11 @@ +import generate from 'nanoid/generate' + +const ALPHABET = "1234567890abcdefghijklmnopqrstuvwxyz" + +export const generateReactKey = ({prefix = ""}: {prefix?: string}={}): string => { + return prefix + generate(ALPHABET, 10) +} + +export default { + generateReactKey +} \ No newline at end of file diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index d6d9a75523..9c8b3efe8b 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -12,18 +12,22 @@ import { import { Component } from "react" import { BaseStyle } from "../editorComponents/BaseComponent" import _ from "lodash" +import React from "react" +import DraggableComponent from "../editorComponents/DraggableComponent" abstract class BaseWidget< T extends IWidgetProps, K extends IWidgetState -> extends Component> { +> extends Component { constructor(props: T) { super(props) - const initialState: Partial = { + const initialState: IWidgetState = { + height: 0, + width: 0 } initialState.height = 0 initialState.width = 0 - this.state = initialState + this.state = initialState as K } componentDidMount(): void { @@ -93,7 +97,17 @@ abstract class BaseWidget< } getComponentPaneView(): JSX.Element { - return this.getPageView() + return ( + + {this.getPageView()} + + ) } abstract getWidgetType(): WidgetType @@ -101,7 +115,7 @@ abstract class BaseWidget< getPositionStyle(): BaseStyle { return { positionType: - this.props.renderMode === RenderModes.COMPONENT_PANE + this.props.renderMode !== RenderModes.PAGE ? "CONTAINER_DIRECTION" : "ABSOLUTE", height: this.state.height, @@ -114,33 +128,47 @@ abstract class BaseWidget< } static defaultProps: Partial = { - parentRowSpace: 1, - parentColumnSpace: 1, + parentRowSpace: 64, + parentColumnSpace: 64, topRow: 0, leftColumn: 0 } } export interface IWidgetState { - height: number - width: number + height: number; + width: number; +} + +export interface DraggableWidget { + type: string; + widget: IWidgetProps; + key: string; } export interface IWidgetBuilder { - buildWidget(data: T): JSX.Element + buildWidget(data: T): JSX.Element; } export interface IWidgetProps { - widgetType: WidgetType - key?: string - widgetId: string - topRow: number - leftColumn: number - bottomRow: number - rightColumn: number - parentColumnSpace: number - parentRowSpace: number - renderMode: RenderMode + widgetType: WidgetType; + key?: string; + widgetId: string; + topRow: number; + leftColumn: number; + bottomRow: number; + rightColumn: number; + parentColumnSpace: number; + parentRowSpace: number; + renderMode: RenderMode; +} + +export interface IWidgetCardProps { + widgetType: WidgetType; + key?: string; + label: string; + icon: string; + groups: string[]; } export default BaseWidget diff --git a/app/client/src/widgets/BreadcrumbsWidget.tsx b/app/client/src/widgets/BreadcrumbsWidget.tsx index 2f81336bbf..0ba2802903 100644 --- a/app/client/src/widgets/BreadcrumbsWidget.tsx +++ b/app/client/src/widgets/BreadcrumbsWidget.tsx @@ -1,17 +1,13 @@ -import * as React from "react" +import React from "react" import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget" -import { WidgetType, CSSUnits } from "../constants/WidgetConstants" +import { WidgetType } from "../constants/WidgetConstants" import { Boundary, IBreadcrumbProps } from "@blueprintjs/core" import BreadcrumbsComponent from "../editorComponents/BreadcrumbsComponent" -import _ from "lodash" class BreadcrumbsWidget extends BaseWidget< - IBreadcrumbsWidgetProps, + BreadcrumbsWidgetProps, IWidgetState > { - constructor(widgetProps: IBreadcrumbsWidgetProps) { - super(widgetProps) - } getPageView() { return ( @@ -32,12 +28,12 @@ class BreadcrumbsWidget extends BaseWidget< } } -export interface IBreadcrumbsWidgetProps extends IWidgetProps { - width?: number - collapseFrom?: Boundary - className?: string - minVisibleItems?: number - items?: IBreadcrumbProps[] +export interface BreadcrumbsWidgetProps extends IWidgetProps { + width?: number; + collapseFrom?: Boundary; + className?: string; + minVisibleItems?: number; + items?: IBreadcrumbProps[]; } export default BreadcrumbsWidget diff --git a/app/client/src/widgets/ButtonWidget.tsx b/app/client/src/widgets/ButtonWidget.tsx index a337f7f8d6..78b90ac75d 100644 --- a/app/client/src/widgets/ButtonWidget.tsx +++ b/app/client/src/widgets/ButtonWidget.tsx @@ -1,14 +1,9 @@ import * as React from "react" import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget" -import { WidgetType, CSSUnits } from "../constants/WidgetConstants" +import { WidgetType } from "../constants/WidgetConstants" import ButtonComponent from "../editorComponents/ButtonComponent" -import _ from "lodash" -import WidgetFactory from "../utils/WidgetFactory"; -class ButtonWidget extends BaseWidget { - constructor(widgetProps: IButtonWidgetProps) { - super(widgetProps) - } +class ButtonWidget extends BaseWidget { getPageView() { return ( @@ -16,7 +11,7 @@ class ButtonWidget extends BaseWidget { style={this.getPositionStyle()} widgetId={this.props.widgetId} key={this.props.widgetId} - text={this.props.text} + text={this.props.text || "Button"} /> ) } @@ -26,9 +21,9 @@ class ButtonWidget extends BaseWidget { } } -export interface IButtonWidgetProps extends IWidgetProps { - text?: string - ellipsize?: boolean +export interface ButtonWidgetProps extends IWidgetProps { + text?: string; + ellipsize?: boolean; } export default ButtonWidget diff --git a/app/client/src/widgets/CalloutWidget.tsx b/app/client/src/widgets/CalloutWidget.tsx index c7187eefe5..aba29e6743 100644 --- a/app/client/src/widgets/CalloutWidget.tsx +++ b/app/client/src/widgets/CalloutWidget.tsx @@ -1,15 +1,10 @@ -import * as React from "react"; +import React from "react"; import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget"; -import { Callout, Code, H5, Intent, Switch } from "@blueprintjs/core"; -import { WidgetType, CSSUnits } from "../constants/WidgetConstants"; +import { Intent } from "@blueprintjs/core"; +import { WidgetType } from "../constants/WidgetConstants"; import CalloutComponent from "../editorComponents/CalloutComponent"; -import _ from "lodash"; - -class CalloutWidget extends BaseWidget { - constructor(widgetProps: ICalloutWidgetProps) { - super(widgetProps); - } +class CalloutWidget extends BaseWidget { getPageView() { return ( { } } -export interface ICalloutWidgetProps extends IWidgetProps { +export interface CalloutWidgetProps extends IWidgetProps { id?: string; title?: string; description?: string; diff --git a/app/client/src/widgets/CheckboxWidget.tsx b/app/client/src/widgets/CheckboxWidget.tsx index 3a6d1453bb..46a1954ed4 100644 --- a/app/client/src/widgets/CheckboxWidget.tsx +++ b/app/client/src/widgets/CheckboxWidget.tsx @@ -1,16 +1,9 @@ -import * as React from "react" +import React from "react" import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget" -import { WidgetType, CSSUnits } from "../constants/WidgetConstants" -import { Icon, Intent } from "@blueprintjs/core" -import { IconName } from "@blueprintjs/icons" +import { WidgetType } from "../constants/WidgetConstants" import CheckboxComponent from "../editorComponents/CheckboxComponent" -import _ from "lodash" - -class CheckboxWidget extends BaseWidget { - constructor(widgetProps: ICheckboxWidgetProps) { - super(widgetProps) - } +class CheckboxWidget extends BaseWidget { getPageView() { return ( { } } -export interface ICheckboxWidgetProps extends IWidgetProps { +export interface CheckboxWidgetProps extends IWidgetProps { items: Array<{ - label: string - defaultIndeterminate: boolean - value: number | string - }> + label: string; + defaultIndeterminate: boolean; + value: number | string; + }>; } export default CheckboxWidget diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index bdecf6f9fb..b5344d74ea 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -1,49 +1,57 @@ +import React from "react" import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget" -import ContainerComponent, { - IContainerProps -} from "../editorComponents/ContainerComponent" +import ContainerComponent from "../editorComponents/ContainerComponent" import { ContainerOrientation, WidgetType, - CSSUnits } from "../constants/WidgetConstants" import WidgetFactory from "../utils/WidgetFactory" -import React from "react" import _ from "lodash" import { Color } from "../constants/StyleConstants" +import DroppableComponent from "../editorComponents/DroppableComponent" -const DEFAULT_NUM_COLS = 13 -const DEFAULT_NUM_ROWS = 13 +const DEFAULT_NUM_COLS = 16 +const DEFAULT_NUM_ROWS = 16 class ContainerWidget extends BaseWidget< - IContainerWidgetProps, - IWidgetState + ContainerWidgetProps, + ContainerWidgetState > { - snapColumnSpace: number = 100 - snapRowSpace: number = 100 - - constructor(props: IContainerWidgetProps) { + constructor(props: ContainerWidgetProps) { super(props) this.renderChildWidget = this.renderChildWidget.bind(this) this.state = { width: 0, - height: 0 + height: 0, + snapColumnSpace: DEFAULT_NUM_COLS, + snapRowSpace: DEFAULT_NUM_ROWS } } - componentDidUpdate(previousProps: IContainerWidgetProps) { + componentDidUpdate(previousProps: ContainerWidgetProps) { super.componentDidUpdate(previousProps) + let snapColumnSpace = this.state.snapColumnSpace + let snapRowSpace = this.state.snapRowSpace if (this.state.width) - this.snapColumnSpace = + snapColumnSpace = this.state.width / (this.props.snapColumns || DEFAULT_NUM_COLS) if (this.state.height) - this.snapRowSpace = + snapRowSpace = this.state.height / (this.props.snapRows || DEFAULT_NUM_ROWS) + if ( + this.state.snapColumnSpace !== snapColumnSpace || + this.state.snapRowSpace !== snapRowSpace + ) { + this.setState({ + snapColumnSpace: snapColumnSpace, + snapRowSpace: snapRowSpace + }) + } } renderChildWidget(childWidgetData: IWidgetProps) { - childWidgetData.parentColumnSpace = this.snapColumnSpace - childWidgetData.parentRowSpace = this.snapRowSpace + childWidgetData.parentColumnSpace = this.state.snapColumnSpace + childWidgetData.parentRowSpace = this.state.snapRowSpace return WidgetFactory.createWidget(childWidgetData) } @@ -61,18 +69,36 @@ class ContainerWidget extends BaseWidget< ) } + getCanvasView() { + return ( + + {super.getCanvasView()} + + ) + } + getWidgetType(): WidgetType { return "CONTAINER_WIDGET" } } -export interface IContainerWidgetProps +export interface ContainerWidgetState extends IWidgetState { + snapColumnSpace: number; + snapRowSpace: number; +} + +export interface ContainerWidgetProps extends IWidgetProps { - children?: T[] - snapColumns?: number - snapRows?: number - orientation?: ContainerOrientation - backgroundColor?: Color + children?: T[]; + snapColumns?: number; + snapRows?: number; + orientation?: ContainerOrientation; + backgroundColor?: Color; } export default ContainerWidget diff --git a/app/client/src/widgets/IconWidget.tsx b/app/client/src/widgets/IconWidget.tsx index 08f37f8db2..24399c560b 100644 --- a/app/client/src/widgets/IconWidget.tsx +++ b/app/client/src/widgets/IconWidget.tsx @@ -1,16 +1,11 @@ -import * as React from "react"; +import React from "react"; import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget"; -import { WidgetType, CSSUnits } from "../constants/WidgetConstants"; -import { Icon, Intent } from "@blueprintjs/core"; +import { WidgetType } from "../constants/WidgetConstants"; +import { Intent } from "@blueprintjs/core"; import { IconName } from "@blueprintjs/icons"; import IconComponent from "../editorComponents/IconComponent"; -import _ from "lodash"; - -class IconWidget extends BaseWidget { - constructor(widgetProps: IIconWidgetProps) { - super(widgetProps); - } +class IconWidget extends BaseWidget { getPageView() { return ( { } } -export interface IIconWidgetProps extends IWidgetProps { +export interface IconWidgetProps extends IWidgetProps { icon?: IconName; iconSize?: number; ellipsize?: boolean; diff --git a/app/client/src/widgets/InputGroupWidget.tsx b/app/client/src/widgets/InputGroupWidget.tsx index 876ff89edd..fda9f9cf14 100644 --- a/app/client/src/widgets/InputGroupWidget.tsx +++ b/app/client/src/widgets/InputGroupWidget.tsx @@ -1,17 +1,13 @@ -import * as React from "react"; +import React from "react"; import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget"; -import { WidgetType, CSSUnits } from "../constants/WidgetConstants"; +import { WidgetType } from "../constants/WidgetConstants"; import InputGroupComponent from "../editorComponents/InputGroupComponent"; -import { IconName, InputGroup, Intent } from "@blueprintjs/core"; -import _ from "lodash"; +import { IconName, Intent } from "@blueprintjs/core"; class InputGroupWidget extends BaseWidget< - IInputGroupWidgetProps, + InputGroupWidgetProps, IWidgetState > { - constructor(widgetProps: IInputGroupWidgetProps) { - super(widgetProps); - } getPageView() { return ( @@ -40,7 +36,7 @@ class InputGroupWidget extends BaseWidget< } } -export interface IInputGroupWidgetProps extends IWidgetProps { +export interface InputGroupWidgetProps extends IWidgetProps { className?: string; disabled?: boolean; large?: boolean; diff --git a/app/client/src/widgets/NumericInputWidget.tsx b/app/client/src/widgets/NumericInputWidget.tsx index 848924e4d2..75133711e1 100644 --- a/app/client/src/widgets/NumericInputWidget.tsx +++ b/app/client/src/widgets/NumericInputWidget.tsx @@ -3,15 +3,11 @@ import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget" import { WidgetType } from "../constants/WidgetConstants" import NumericInputComponent from "../editorComponents/NumericInputComponent" import { Intent, IconName } from "@blueprintjs/core" -import _ from "lodash" class NumericInputWidget extends BaseWidget< - INumericInputWidgetProps, + NumericInputWidgetProps, IWidgetState > { - constructor(widgetProps: INumericInputWidgetProps) { - super(widgetProps) - } getPageView() { return ( @@ -47,27 +43,27 @@ class NumericInputWidget extends BaseWidget< } } -export interface INumericInputWidgetProps extends IWidgetProps { - className?: string - disabled?: boolean - large?: boolean - intent?: Intent - defaultValue?: string - leftIcon?: IconName - rightElement?: JSX.Element - allowNumericCharactersOnly?: boolean - fill?: boolean - majorStepSize?: number | null - max?: number - min?: number - minorStepSize?: number | null - onValueChange?: (valueAsNumber: number, valueAsString: string) => void - onButtonClick?: (valueAsNumber: number, valueAsString: string) => void - inputRef?: (ref: HTMLInputElement | null) => any - selectAllOnFocus?: boolean - selectAllOnIncrement?: boolean - stepSize?: number - placeholder?: string +export interface NumericInputWidgetProps extends IWidgetProps { + className?: string; + disabled?: boolean; + large?: boolean; + intent?: Intent; + defaultValue?: string; + leftIcon?: IconName; + rightElement?: JSX.Element; + allowNumericCharactersOnly?: boolean; + fill?: boolean; + majorStepSize?: number | null; + max?: number; + min?: number; + minorStepSize?: number | null; + onValueChange?: (valueAsNumber: number, valueAsString: string) => void; + onButtonClick?: (valueAsNumber: number, valueAsString: string) => void; + inputRef?: (ref: HTMLInputElement | null) => any; + selectAllOnFocus?: boolean; + selectAllOnIncrement?: boolean; + stepSize?: number; + placeholder?: string; } export default NumericInputWidget diff --git a/app/client/src/widgets/RadioGroupWidget.tsx b/app/client/src/widgets/RadioGroupWidget.tsx index 308940a419..552fcc9cd9 100644 --- a/app/client/src/widgets/RadioGroupWidget.tsx +++ b/app/client/src/widgets/RadioGroupWidget.tsx @@ -1,17 +1,13 @@ import * as React from "react" import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget" -import { WidgetType, CSSUnits } from "../constants/WidgetConstants" +import { WidgetType } from "../constants/WidgetConstants" import RadioGroupComponent from "../editorComponents/RadioGroupComponent" import { IOptionProps } from "@blueprintjs/core" -import _ from "lodash" class RadioButtonWidget extends BaseWidget< - IRadioGroupWidgetProps, + RadioGroupWidgetProps, IWidgetState > { - constructor(widgetProps: IRadioGroupWidgetProps) { - super(widgetProps) - } getPageView() { return ( @@ -37,19 +33,19 @@ class RadioButtonWidget extends BaseWidget< } } -export interface IRadioGroupWidgetProps extends IWidgetProps { - label: string - inline: boolean - selectedValue: string | number - handleRadioChange: (event: React.FormEvent) => void - disabled: boolean - className: string - name: string - options: IOptionProps[] +export interface RadioGroupWidgetProps extends IWidgetProps { + label: string; + inline: boolean; + selectedValue: string | number; + handleRadioChange: (event: React.FormEvent) => void; + disabled: boolean; + className: string; + name: string; + options: IOptionProps[]; items: Array<{ - label: string - value: number | string - }> + label: string; + value: number | string; + }>; } export default RadioButtonWidget diff --git a/app/client/src/widgets/SpinnerWidget.tsx b/app/client/src/widgets/SpinnerWidget.tsx index 27638146f4..3569e9ebc8 100644 --- a/app/client/src/widgets/SpinnerWidget.tsx +++ b/app/client/src/widgets/SpinnerWidget.tsx @@ -1,14 +1,10 @@ -import * as React from "react"; +import React from "react"; import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget"; -import { WidgetType, CSSUnits } from "../constants/WidgetConstants"; -import { Spinner, Intent } from "@blueprintjs/core"; +import { WidgetType } from "../constants/WidgetConstants"; +import { Intent } from "@blueprintjs/core"; import SpinnerComponent from "../editorComponents/SpinnerComponent"; -import _ from "lodash"; -class SpinnerWidget extends BaseWidget { - constructor(widgetProps: ISpinnerWidgetProps) { - super(widgetProps); - } +class SpinnerWidget extends BaseWidget { getPageView() { return ( @@ -28,7 +24,7 @@ class SpinnerWidget extends BaseWidget { } } -export interface ISpinnerWidgetProps extends IWidgetProps { +export interface SpinnerWidgetProps extends IWidgetProps { size?: number; value?: number; ellipsize?: boolean; diff --git a/app/client/src/widgets/TagInputWidget.tsx b/app/client/src/widgets/TagInputWidget.tsx index 9577e933f7..3b1e5db2fa 100644 --- a/app/client/src/widgets/TagInputWidget.tsx +++ b/app/client/src/widgets/TagInputWidget.tsx @@ -1,15 +1,11 @@ -import * as React from "react"; +import React from "react"; import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget"; -import { WidgetType, CSSUnits } from "../constants/WidgetConstants"; +import { WidgetType } from "../constants/WidgetConstants"; import TagInputComponent from "../editorComponents/TagInputComponent"; -import { Intent, ITagProps, TagInput, HTMLInputProps } from "@blueprintjs/core"; +import { Intent, ITagProps, HTMLInputProps } from "@blueprintjs/core"; -import _ from "lodash"; -class TagInputWidget extends BaseWidget { - constructor(widgetProps: ITagInputWidgetProps) { - super(widgetProps); - } +class TagInputWidget extends BaseWidget { getPageView() { return ( @@ -28,7 +24,7 @@ class TagInputWidget extends BaseWidget { } } -export interface ITagInputWidgetProps extends IWidgetProps { +export interface TagInputWidgetProps extends IWidgetProps { addOnPaste?: boolean; className?: string; disabled?: boolean; diff --git a/app/client/src/widgets/TextWidget.tsx b/app/client/src/widgets/TextWidget.tsx index 8e04f5f574..1875a603fc 100644 --- a/app/client/src/widgets/TextWidget.tsx +++ b/app/client/src/widgets/TextWidget.tsx @@ -1,13 +1,9 @@ -import * as React from "react"; +import React from "react"; import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget"; -import { WidgetType, CSSUnits } from "../constants/WidgetConstants"; +import { WidgetType } from "../constants/WidgetConstants"; import TextComponent from "../editorComponents/TextComponent"; -import _ from "lodash"; -class TextWidget extends BaseWidget { - constructor(widgetProps: ITextWidgetProps) { - super(widgetProps); - } +class TextWidget extends BaseWidget { getPageView() { return ( @@ -26,7 +22,7 @@ class TextWidget extends BaseWidget { } } -export interface ITextWidgetProps extends IWidgetProps { +export interface TextWidgetProps extends IWidgetProps { text?: string; ellipsize?: boolean; tagName?: keyof JSX.IntrinsicElements; diff --git a/app/client/tsconfig.json b/app/client/tsconfig.json index 5dae6633f5..d22b42e5d9 100644 --- a/app/client/tsconfig.json +++ b/app/client/tsconfig.json @@ -4,21 +4,26 @@ "lib": [ "dom", "dom.iterable", - "esnext" + "esnext", + "es5", + "es2015.collection", + "es2015.iterable" ], + "strict": true, "outDir": "./out/js/src", "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "strict": true, "forceConsistentCasingInFileNames": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "preserve" + "jsx": "react", + "downlevelIteration": true, + "importHelpers": true }, "include": [ "./src/**/*" diff --git a/app/client/yarn.lock b/app/client/yarn.lock index 98f0f1de69..c88d1a7e1d 100644 --- a/app/client/yarn.lock +++ b/app/client/yarn.lock @@ -1409,6 +1409,13 @@ dependencies: moment ">=2.14.0" +"@types/nanoid@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/nanoid/-/nanoid-2.0.0.tgz#b59002c475e6dfcc26e67ba563ff61b512e5ebf8" + integrity sha512-NtwPHfAyU3IDXdKAB2OMPpAauHBg9gUjpOYr3FAzI84D70nWdS8k5mryteLvT/s1ACeAFAkGg132/XJVN4qx/w== + dependencies: + "@types/node" "*" + "@types/netlify-identity-widget@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@types/netlify-identity-widget/-/netlify-identity-widget-1.4.1.tgz#a963bd0360ac9dd8f4fd437be6764179d396e3a4" @@ -1533,6 +1540,17 @@ regexpp "^2.0.1" tsutils "^3.7.0" +"@typescript-eslint/eslint-plugin@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.0.0.tgz#609a5d7b00ce21a6f94d7ef282eba9da57ca1e42" + integrity sha512-Mo45nxTTELODdl7CgpZKJISvLb+Fu64OOO2ZFc2x8sYSnUpFrBUW3H+H/ZGYmEkfnL6VkdtOSxgdt+Av79j0sA== + dependencies: + "@typescript-eslint/experimental-utils" "2.0.0" + eslint-utils "^1.4.0" + functional-red-black-tree "^1.0.1" + regexpp "^2.0.1" + tsutils "^3.14.0" + "@typescript-eslint/experimental-utils@1.13.0": version "1.13.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz#b08c60d780c0067de2fb44b04b432f540138301e" @@ -1542,6 +1560,15 @@ "@typescript-eslint/typescript-estree" "1.13.0" eslint-scope "^4.0.0" +"@typescript-eslint/experimental-utils@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.0.0.tgz#f3d298bb411357f35c4184e24280b256b6321949" + integrity sha512-XGJG6GNBXIEx/mN4eTRypN/EUmsd0VhVGQ1AG+WTgdvjHl0G8vHhVBHrd/5oI6RRYBRnedNymSYWW1HAdivtmg== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.0.0" + eslint-scope "^4.0.0" + "@typescript-eslint/parser@1.13.0": version "1.13.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.13.0.tgz#61ac7811ea52791c47dc9fd4dd4a184fae9ac355" @@ -1552,6 +1579,16 @@ "@typescript-eslint/typescript-estree" "1.13.0" eslint-visitor-keys "^1.0.0" +"@typescript-eslint/parser@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.0.0.tgz#4273bb19d03489daf8372cdaccbc8042e098178f" + integrity sha512-ibyMBMr0383ZKserIsp67+WnNVoM402HKkxqXGlxEZsXtnGGurbnY90pBO3e0nBUM7chEEOcxUhgw9aPq7fEBA== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "2.0.0" + "@typescript-eslint/typescript-estree" "2.0.0" + eslint-visitor-keys "^1.0.0" + "@typescript-eslint/typescript-estree@1.13.0": version "1.13.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz#8140f17d0f60c03619798f1d628b8434913dc32e" @@ -1560,6 +1597,14 @@ lodash.unescape "4.0.1" semver "5.5.0" +"@typescript-eslint/typescript-estree@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.0.0.tgz#c9f6c0efd1b11475540d6a55dc973cc5b9a67e77" + integrity sha512-NXbmzA3vWrSgavymlzMWNecgNOuiMMp62MO3kI7awZRLRcsA1QrYWo6q08m++uuAGVbXH/prZi2y1AWuhSu63w== + dependencies: + lodash.unescape "4.0.1" + semver "^6.2.0" + "@webassemblyjs/ast@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" @@ -2427,6 +2472,13 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -2722,7 +2774,7 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3702,11 +3754,6 @@ diff-sequences@^24.9.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== -diff@^3.2.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -3944,7 +3991,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^4.1.0: +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== @@ -4053,6 +4100,13 @@ escodegen@^1.11.0, escodegen@^1.9.1: optionalDependencies: source-map "~0.6.1" +eslint-config-prettier@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.1.0.tgz#e6f678ba367fbd1273998d5510f76f004e9dce7b" + integrity sha512-k9fny9sPjIBQ2ftFTesJV21Rg4R/7a7t7LCtZVrYQiHEp8Nnuk3EGaDmsKSAnsPj0BYcgB2zxzHa2NTkIxcOLg== + dependencies: + get-stdin "^6.0.0" + eslint-config-react-app@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.0.1.tgz#5f3d666ba3ee3cb384eb943e260e868f6c72251b" @@ -4126,6 +4180,13 @@ eslint-plugin-jsx-a11y@6.2.3: has "^1.0.3" jsx-ast-utils "^2.2.1" +eslint-plugin-prettier@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz#8695188f95daa93b0dc54b249347ca3b79c4686d" + integrity sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-plugin-react-hooks@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04" @@ -4170,7 +4231,7 @@ eslint-scope@^5.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^1.3.1, eslint-utils@^1.4.2: +eslint-utils@^1.3.1, eslint-utils@^1.4.0, eslint-utils@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== @@ -4445,6 +4506,11 @@ fast-deep-equal@^2.0.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^2.0.2: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" @@ -4538,6 +4604,13 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -5745,6 +5818,11 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-obj@^1.0.0, is-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" @@ -6059,11 +6137,6 @@ jest-environment-node@^24.9.0: jest-mock "^24.9.0" jest-util "^24.9.0" -jest-get-type@^22.1.0: - version "22.4.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" - integrity sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w== - jest-get-type@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" @@ -7064,6 +7137,14 @@ micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: snapdragon "^0.8.1" to-regex "^3.0.2" +micromatch@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -7264,6 +7345,11 @@ nan@^2.12.1, nan@^2.13.2: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +nanoid@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.0.4.tgz#4889355c9ce8e24efad7c65945a4a2875ac3e8f4" + integrity sha512-sOJnBmY3TJQBVIBqKHoifuwygrocXg3NjS9rZSMnVl05XWSHK7Qxb177AIZQyMDjP86bz+yneozj/h9qsPLcCA== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -8046,6 +8132,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picomatch@^2.0.5: + version "2.0.7" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" + integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -8811,6 +8902,13 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + prettier@^1.16.0: version "1.18.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" @@ -8829,14 +8927,6 @@ pretty-error@^2.1.1: renderkid "^2.0.1" utila "~0.4" -pretty-format@^23.6.0: - version "23.6.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760" - integrity sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw== - dependencies: - ansi-regex "^3.0.0" - ansi-styles "^3.2.0" - pretty-format@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" @@ -9213,7 +9303,7 @@ react-router@^4.3.1: prop-types "^15.6.1" warning "^4.0.1" -react-scripts@3.1.1: +react-scripts@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.1.1.tgz#1796bc92447f3a2d3072c3b71ca99f88d099c48d" integrity sha512-dbjTG9vJC61OI62hIswQYg5xHvwlxDTH6QXz6ICEuA5AqkFQWk1LKl76sk8fVL2WsyumbBc4FErALwKcEV2vNA== @@ -10878,6 +10968,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -10953,6 +11050,17 @@ ts-jest@^24.0.2: semver "^5.5" yargs-parser "10.x" +ts-loader@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.0.4.tgz#bc331ad91a887a60632d94c9f79448666f2c4b63" + integrity sha512-p2zJYe7OtwR+49kv4gs7v4dMrfYD1IPpOtqiSPCbe8oR+4zEBtdHwzM7A7M91F+suReqgzZrlClk4LRSSp882g== + dependencies: + chalk "^2.3.0" + enhanced-resolve "^4.0.0" + loader-utils "^1.0.2" + micromatch "^4.0.0" + semver "^6.0.0" + ts-pnp@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.2.tgz#be8e4bfce5d00f0f58e0666a82260c34a57af552" @@ -11016,7 +11124,7 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== -tsutils@^3.7.0: +tsutils@^3.14.0, tsutils@^3.7.0: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==