added basic redux state

added basic inheritance hierarchy of widgets and components
added basic redux action types
added basic widget building logic
This commit is contained in:
Nikhil Nandgopal 2019-02-10 18:36:05 +05:30
parent 1915b45e52
commit bf4dc5a052
21 changed files with 435 additions and 25 deletions

View File

@ -1117,6 +1117,15 @@
"@types/react": "*"
}
},
"@types/react-redux": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.0.1.tgz",
"integrity": "sha512-+DIH7TI2MT4Ke4lOrRMgNy//DzTDIzv5QwkJSD6AVrlsIgzf7yMM0JoWL5wJUXYwKQ2f1FgvwlvIVGD2QWQnew==",
"requires": {
"@types/react": "*",
"redux": "^4.0.0"
}
},
"@types/react-router": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-4.4.3.tgz",
@ -14998,6 +15007,27 @@
"symbol-observable": "^1.2.0"
}
},
"redux-devtools": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/redux-devtools/-/redux-devtools-3.5.0.tgz",
"integrity": "sha512-pGU8TZNvWxPaCCE432AGm6H6alQbAz80gQM5CzM3SjX9/oSNu/HPF17xFdPQJOXasqyih1Gv167kZDTRe7r0iQ==",
"dev": true,
"requires": {
"lodash": "^4.2.0",
"prop-types": "^15.5.7",
"redux-devtools-instrument": "^1.9.0"
}
},
"redux-devtools-instrument": {
"version": "1.9.6",
"resolved": "https://registry.npmjs.org/redux-devtools-instrument/-/redux-devtools-instrument-1.9.6.tgz",
"integrity": "sha512-MwvY4cLEB2tIfWWBzrUR02UM9qRG2i7daNzywRvabOSVdvAY7s9BxSwMmVRH1Y/7QWjplNtOwgT0apKhHg2Qew==",
"dev": true,
"requires": {
"lodash": "^4.2.0",
"symbol-observable": "^1.0.2"
}
},
"redux-saga": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.0.0.tgz",

View File

@ -12,12 +12,14 @@
"@types/node": "^10.12.18",
"@types/react": "^16.8.2",
"@types/react-dom": "^16.8.0",
"@types/react-redux": "^7.0.1",
"@types/react-router-dom": "^4.3.1",
"@types/styled-components": "^4.1.8",
"axios": "^0.18.0",
"flow-bin": "^0.91.0",
"husky": "^1.3.1",
"lint-staged": "^8.1.0",
"lodash": "^4.17.11",
"prettier": "^1.16.0",
"react": "^16.7.0",
"react-dnd": "^7.0.2",
@ -27,6 +29,7 @@
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.3",
"redux": "^4.0.1",
"redux-saga": "^1.0.0",
"styled-components": "^4.1.3",
"typescript": "^3.2.4"
@ -46,5 +49,8 @@
"not dead",
"not ie <= 11",
"not op_mini all"
]
],
"devDependencies": {
"redux-devtools": "^3.5.0"
}
}

View File

@ -1,6 +1,10 @@
import React, { Component } from 'react';
import logo from './assets/images/logo.svg';
import './App.css';
import { createStore } from 'redux'
import appReducer from './reducers';
const store = createStore(appReducer)
class App extends Component {
render() {

View File

@ -0,0 +1,18 @@
import ContainerWidget from "../widgets/ContainerWidget"
export type ActionType = "LOAD_CANVAS" | "CLEAR_CANVAS" | "DROP_WIDGET_CANVAS" | "REMOVE_WIDGET_CANVAS"
export const ActionTypes = {
LOAD_CANVAS: "LOAD_CANVAS",
CLEAR_CANVAS: "CLEAR_CANVAS",
DROP_WIDGET_CANVAS: "DROP_WIDGET_CANVAS",
REMOVE_WIDGET_CANVAS: "REMOVE_WIDGET_CANVAS"
}
export interface ReduxAction<T> {
actionType: ActionType
payload: T
}
export interface LoadCanvasPayload {
containerWidget: ContainerWidget
}

View File

@ -1,3 +0,0 @@
export const a = () => {
}

View File

@ -0,0 +1,2 @@
export type WidgetType = "TEXT_WIDGET" | "IMAGE_WIDGET" | "CONTAINER_WIDGET" | "LIST_WIDGET" | "INPUT_TEXT_WIDGET"
export type ContainerOrientation = "HORIZONTAL" | "VERTICAL" | "ABSOLUTE"

View File

@ -0,0 +1,26 @@
import * as React from "react"
/***
* Components are responsible for binding render inputs to corresponding UI SDKs
*/
abstract class BaseComponent<T extends IComponentProps> extends React.Component<T> {
componentData: T
constructor(componentProps: T) {
super(componentProps)
this.componentData = componentProps
}
}
export interface BaseStyle {
height?: number
width?: number
}
export interface IComponentProps {
widgetId: string
style?: BaseStyle
}
export default BaseComponent

View File

@ -0,0 +1,15 @@
import * as React from "react"
import { Button, MaybeElement } from "@blueprintjs/core"
import { ITextComponentProps } from "./TextComponent";
class ButtomComponent extends React.Component<IButtonComponentProps> {
render() {
return <Button text={this.props.text} icon={this.props.icon} />
}
}
interface IButtonComponentProps extends ITextComponentProps {
icon?: MaybeElement
}
export default ButtomComponent

View File

@ -0,0 +1,34 @@
import * as React from "react"
import BaseComponent, { IComponentProps } from "./BaseComponent"
import { ContainerOrientation } from "../constants/WidgetConstants"
import styled from "styled-components"
const Container = styled.div`
background: papayawhip;
color: ${props => (props.theme ? props.theme.colors.text : "white")};
`
class ContainerComponent extends BaseComponent<IContainerProps> {
render() {
return (
<Container>
{this.props.children
? this.props.children.map(child => {
return child
})
: undefined}
</Container>
)
}
}
export interface IContainerProps extends IComponentProps {
children?: React.Component[]
snapColumnSpace: number
snapRowSpace: number
snapColumns: number
snapRows: number
orientation: ContainerOrientation
}
export default ContainerComponent

View File

@ -0,0 +1,17 @@
import * as React from "react"
import { Text } from "@blueprintjs/core"
import { IComponentProps } from "./BaseComponent";
class TextComponent extends React.Component<ITextComponentProps> {
render() {
return <Text ellipsize={this.props.ellipsize}>{this.props.text}</Text>
}
}
export interface ITextComponentProps extends IComponentProps {
text: string
ellipsize: boolean
}
export default TextComponent

View File

@ -1,14 +1,30 @@
import React, { Component } from 'react';
import { Card } from '@blueprintjs/core';
import React, { Component } from "react"
import { connect } from "react-redux"
import { AppState } from "../../reducers"
import WidgetFactory from "../../utils/WidgetFactory"
import { CanvasReduxState } from "../../reducers/uiReducers/canvasReducer"
class Canvas extends Component {
class Canvas extends Component<{ canvas: CanvasReduxState }> {
render() {
return (
<Card>
</Card>
);
const canvasWidgetData = this.props.canvas.canvasWidgetProps
if (canvasWidgetData) {
const canvasWidget = WidgetFactory.createWidget(canvasWidgetData)
return canvasWidget.getWidgetView()
} else return undefined
}
}
export default Canvas;
const mapStateToProps = (state: AppState, props: any) => {
return {
canvas: state.ui.canvas
}
}
const mapDispatchToProps = (dispatch: any) => {
return {}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Canvas)

View File

@ -0,0 +1,29 @@
import { createReducer } from "../../utils/PicassoUtils"
import {
ActionTypes,
LoadCanvasPayload,
ReduxAction
} from "../../constants/ActionConstants"
import { IWidgetProps } from "../../widgets/BaseWidget";
const initialState: CanvasWidgetsReduxState = {
}
const canvasWidgetsReducer = createReducer(
initialState,
{
[ActionTypes.LOAD_CANVAS]: (
state: CanvasWidgetsReduxState,
action: ReduxAction<LoadCanvasPayload>
) => {
return { ...action.payload }
}
}
)
export interface CanvasWidgetsReduxState {
[widgetId: string]: IWidgetProps
}
export default canvasWidgetsReducer

View File

@ -1,3 +1,5 @@
export const a = () => {
import { combineReducers } from "redux"
import canvasWidgetsReducer from "./canvasWidgetsReducers"
}
const entityReducer = combineReducers({ canvasWidgets: canvasWidgetsReducer })
export default entityReducer

View File

@ -1,3 +1,21 @@
export const a = () => {
import { combineReducers } from "redux"
import entityReducer from "./entityReducers"
import uiReducer from "./uiReducers"
import { CanvasReduxState } from "./uiReducers/canvasReducer"
import { CanvasWidgetsReduxState } from "./entityReducers/canvasWidgetsReducers"
const appReducer = combineReducers({
entities: entityReducer,
ui: uiReducer
})
export default appReducer
export interface AppState {
ui: {
canvas: CanvasReduxState
}
entities: {
canvasWidgets: CanvasWidgetsReduxState
}
}

View File

@ -0,0 +1,26 @@
import { createReducer } from "../../utils/PicassoUtils"
import {
ActionTypes,
LoadCanvasPayload,
ReduxAction
} from "../../constants/ActionConstants"
import { IContainerWidgetProps } from "../../widgets/ContainerWidget"
const initialState: CanvasReduxState = {
canvasWidgetProps: undefined
}
const canvasReducer = createReducer(initialState, {
[ActionTypes.LOAD_CANVAS]: (
state: CanvasReduxState,
action: ReduxAction<LoadCanvasPayload>
) => {
return { containerWidget: action.payload }
}
})
export interface CanvasReduxState {
canvasWidgetProps?: IContainerWidgetProps
}
export default canvasReducer

View File

@ -1,3 +1,5 @@
export const a = () => {
import { combineReducers } from "redux"
import canvasReducer from "./canvasReducer"
}
const uiReducer = combineReducers({ canvas: canvasReducer })
export default uiReducer

View File

@ -1,3 +1,14 @@
export const a = () => {
import { ReduxAction } from "../constants/ActionConstants"
export const createReducer = (
initialState: any,
handlers: { [actionType: string]: Function }
) => {
return function reducer(state = initialState, action: ReduxAction<any>) {
if (handlers.hasOwnProperty(action.actionType)) {
return handlers[action.actionType](state, action)
} else {
return state
}
}
}

View File

@ -0,0 +1,31 @@
import { WidgetType } from "../constants/WidgetConstants";
import BaseWidget, { IWidgetBuilder, IWidgetProps } from "../widgets/BaseWidget";
import { IComponentProps } from "../editorComponents/BaseComponent";
class WidgetFactory {
static widgetMap: Map<WidgetType, IWidgetBuilder<IWidgetProps, IComponentProps>>
static registerWidgetBuilder(widgetType: WidgetType, widgetBuilder: IWidgetBuilder<IWidgetProps, IComponentProps>) {
this.widgetMap.set(widgetType, widgetBuilder)
}
static createWidget(widgetData: IWidgetProps): BaseWidget<IWidgetProps, IComponentProps> {
const widgetBuilder = this.widgetMap.get(widgetData.widgetType)
if (widgetBuilder)
return widgetBuilder.buildWidget(widgetData)
else {
const ex: IWidgetCreationException = {
message: "Widget Builder not registered for widget type" + widgetData.widgetType
}
throw ex
}
}
}
export interface IWidgetCreationException {
message: string
}
export default WidgetFactory

View File

@ -0,0 +1,14 @@
import BaseWidget from "../widgets/BaseWidget"
import ContainerWidget, {
IContainerWidgetProps
} from "../widgets/ContainerWidget"
import { IContainerProps } from "../editorComponents/ContainerComponent"
import WidgetFactory from "./WidgetFactory"
WidgetFactory.registerWidgetBuilder("CONTAINER_WIDGET", {
buildWidget(
widgetData: IContainerWidgetProps
): BaseWidget<IContainerWidgetProps, IContainerProps> {
return new ContainerWidget(widgetData)
}
})

View File

@ -0,0 +1,49 @@
import { WidgetType } from "../constants/WidgetConstants"
import { IComponentProps } from "../editorComponents/BaseComponent"
/***
* Widget are responsible for accepting the abstraction layer inputs, interpretting them into rederable props and
* spawing components based on those props
* Widgets are also responsible for dispatching actions and updating the state tree
*/
abstract class BaseWidget<T extends IWidgetProps, K extends IComponentProps> {
widgetData: T
width: number
height: number
constructor(widgetProps: T) {
this.widgetData = widgetProps
this.width =
(this.widgetData.rightColumn - this.widgetData.leftColumn) *
widgetProps.parentColumnSpace
this.height =
(this.widgetData.bottomRow - this.widgetData.topRow) *
widgetProps.parentRowSpace
}
abstract getWidgetView(): React.Component<K>
abstract getComponentProps(): K
abstract getWidgetType(): WidgetType
}
export interface IWidgetBuilder<
T extends IWidgetProps,
K extends IComponentProps
> {
buildWidget(data: T): BaseWidget<T, K>
}
export interface IWidgetProps {
widgetType: WidgetType
widgetId: string
topRow: number
leftColumn: number
bottomRow: number
rightColumn: number
parentColumnSpace: number
parentRowSpace: number
}
export default BaseWidget

View File

@ -0,0 +1,63 @@
import * as React from "react"
import BaseWidget, { IWidgetProps } from "./BaseWidget"
import ContainerComponent, {
IContainerProps
} from "../editorComponents/ContainerComponent"
import { ContainerOrientation, WidgetType } from "../constants/WidgetConstants"
import WidgetFactory from "../utils/WidgetFactory"
class ContainerWidget extends BaseWidget<
IContainerWidgetProps,
IContainerProps
> {
constructor(widgetProps: IContainerWidgetProps) {
super(widgetProps)
if (this.widgetData) {
this.widgetData.snapColumns = 13
this.widgetData.snapColumnSpace = this.width / this.widgetData.snapColumns
this.widgetData.snapRowSpace = 100
this.widgetData.snapRows = this.height / this.widgetData.snapRowSpace
}
}
getComponentProps(): IContainerProps {
return {
widgetId: this.widgetData.widgetId,
snapColumnSpace: this.widgetData.snapColumnSpace,
snapRowSpace: this.widgetData.snapRowSpace,
snapColumns: this.widgetData.snapColumns,
snapRows: this.widgetData.snapRows,
orientation: this.widgetData.orientation
}
}
getWidgetView(): any {
return (
<ContainerComponent {...this.getComponentProps()}>
{this.widgetData.children
? this.widgetData.children.map(childWidgetData => {
childWidgetData.parentColumnSpace = this.widgetData.snapColumnSpace
childWidgetData.parentRowSpace = this.widgetData.snapRowSpace
return WidgetFactory.createWidget(childWidgetData).getWidgetView()
})
: undefined}
</ContainerComponent>
)
}
getWidgetType(): WidgetType {
return "CONTAINER_WIDGET"
}
}
export interface IContainerWidgetProps extends IWidgetProps {
children?: IWidgetProps[]
snapColumnSpace: number
snapRowSpace: number
snapColumns: number
snapRows: number
orientation: ContainerOrientation
}
export default ContainerWidget