added type to state

moved css to style object
added style units
migrated all widgets to work with JSX
This commit is contained in:
Nikhil Nandgopal 2019-02-11 23:52:23 +05:30
parent f7d310bcda
commit 85b8525c4d
14 changed files with 223 additions and 117 deletions

View File

@ -14273,6 +14273,17 @@
} }
} }
}, },
"react": {
"version": "16.8.1",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.1.tgz",
"integrity": "sha512-wLw5CFGPdo7p/AgteFz7GblI2JPOos0+biSoxf1FPsGxWQZdN/pj6oToJs1crn61DL3Ln7mN86uZ4j74p31ELQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.1"
}
},
"react-app-polyfill": { "react-app-polyfill": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-0.2.0.tgz", "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-0.2.0.tgz",
@ -14456,6 +14467,17 @@
"lodash": "^4.17.11" "lodash": "^4.17.11"
} }
}, },
"react-dom": {
"version": "16.8.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.1.tgz",
"integrity": "sha512-N74IZUrPt6UiDjXaO7UbDDFXeUXnVhZzeRLy/6iqqN1ipfjrhR60Bp5NuBK+rv3GMdqdIuwIl22u1SYwf330bg==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.1"
}
},
"react-error-overlay": { "react-error-overlay": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.2.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.2.tgz",
@ -15758,6 +15780,15 @@
"xmlchars": "^1.3.1" "xmlchars": "^1.3.1"
} }
}, },
"scheduler": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.1.tgz",
"integrity": "sha512-VJKOkiKIN2/6NOoexuypwSrybx13MY7NSy9RNt8wPvZDMRT1CW6qlpF5jXRToXNHz3uWzbm2elNpZfXfGPqP9A==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"schema-utils": { "schema-utils": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",

View File

@ -33,7 +33,7 @@ const defaultFont: IFontInterface = {
} }
export const theme = { export const theme = {
primaryColor: Colors.FullWhite, primaryColor: Colors.FullBlack,
secondaryColor: Colors.FullWhite, secondaryColor: Colors.FullWhite,
ascentColor: Colors.FullBlack, ascentColor: Colors.FullBlack,
headerFont: defaultFont, headerFont: defaultFont,

View File

@ -1,8 +1,8 @@
export type Color = "full-white" | "full-black" export type Color = "#ffffff" | "#000000"
export const Colors: { [id: string]: Color } = { export const Colors: { [id: string]: Color } = {
FullWhite: "full-white", FullWhite: "#ffffff",
FullBlack: "full-black" FullBlack: "#000000"
} }
export type FontFamily = "Brandon-Regular" | "Roboto-Bold" export type FontFamily = "Brandon-Regular" | "Roboto-Bold"

View File

@ -1,2 +1,10 @@
export type WidgetType = "TEXT_WIDGET" | "IMAGE_WIDGET" | "CONTAINER_WIDGET" | "LIST_WIDGET" | "INPUT_TEXT_WIDGET" export type WidgetType = "TEXT_WIDGET" | "IMAGE_WIDGET" | "CONTAINER_WIDGET" | "LIST_WIDGET" | "INPUT_TEXT_WIDGET"
export type ContainerOrientation = "HORIZONTAL" | "VERTICAL" | "ABSOLUTE" export type ContainerOrientation = "HORIZONTAL" | "VERTICAL"
export type PositionType = "ABSOLUTE" | "CONTAINER_DIRECTION"
export type CSSUnit = "px" | "cm" | "mm" | "in" | "pt" | "pc" | "em" | "ex" | "ch" | "rem" | "vw" | "vh" | "vmin" | "vmax" | "%"
export const CSSUnits: { [id: string]: CSSUnit } = {
PIXEL: "px",
RELATIVE_FONTSIZE: "em",
RELATIVE_PARENT: "%"
}

View File

@ -1,14 +1,13 @@
import * as React from "react" import { Component } from "react"
import React from "react"
import { PositionType, CSSUnit } from "../constants/WidgetConstants"
/*** /***
* Components are responsible for binding render inputs to corresponding UI SDKs * Components are responsible for binding render inputs to corresponding UI SDKs
*/ */
abstract class BaseComponent<T extends IComponentProps> extends React.Component<T> { abstract class BaseComponent<T extends IComponentProps> extends Component<T> {
componentData: T
constructor(componentProps: T) { constructor(componentProps: T) {
super(componentProps) super(componentProps)
this.componentData = componentProps
} }
} }
@ -16,11 +15,18 @@ abstract class BaseComponent<T extends IComponentProps> extends React.Component<
export interface BaseStyle { export interface BaseStyle {
height?: number height?: number
width?: number width?: number
positionType: PositionType
xPosition: number
yPosition: number
xPositionUnit: CSSUnit
yPositionUnit: CSSUnit
heightUnit?: CSSUnit
widthUnit?: CSSUnit
} }
export interface IComponentProps { export interface IComponentProps {
widgetId: string widgetId: string
style?: BaseStyle style: BaseStyle
} }
export default BaseComponent export default BaseComponent

View File

@ -1,17 +1,24 @@
import * as React from "react"
import BaseComponent, { IComponentProps } from "./BaseComponent" import BaseComponent, { IComponentProps } from "./BaseComponent"
import { ContainerOrientation } from "../constants/WidgetConstants" import { ContainerOrientation } from "../constants/WidgetConstants"
import styled from "../constants/DefaultTheme" import styled from "../constants/DefaultTheme"
import React from "react"
const Container = styled.div` const Container = styled("div")<IContainerProps>`
background: ${props => props.theme.primaryColor}; background: ${props => props.theme.secondaryColor};
color: ${props => props.theme.primaryColor}; color: ${props => props.theme.primaryColor};
position: ${props => props.style.positionType};
left: ${props => {
return props.style.xPosition + props.style.xPositionUnit
}};
top: ${props => {
return props.style.yPosition + props.style.yPositionUnit
}};
` `
class ContainerComponent extends BaseComponent<IContainerProps> { class ContainerComponent extends BaseComponent<IContainerProps> {
render() { render() {
return ( return (
<Container key={this.componentData.widgetId}> <Container {...this.props}>
{this.props.children {this.props.children
? this.props.children.map(child => { ? this.props.children.map(child => {
return child return child
@ -23,12 +30,12 @@ class ContainerComponent extends BaseComponent<IContainerProps> {
} }
export interface IContainerProps extends IComponentProps { export interface IContainerProps extends IComponentProps {
children?: React.Component[] children?: JSX.Element[]
snapColumnSpace?: number snapColumnSpace: number
snapRowSpace?: number snapRowSpace: number
snapColumns?: number snapColumns: number
snapRows?: number snapRows: number
orientation?: ContainerOrientation orientation: ContainerOrientation
} }
export default ContainerComponent export default ContainerComponent

View File

@ -1,11 +1,21 @@
import * as React from "react" import * as React from "react"
import { Text } from "@blueprintjs/core" import { IComponentProps } from "./BaseComponent"
import { IComponentProps } from "./BaseComponent"; import styled from "../constants/DefaultTheme"
const TextContainer = styled("span")<ITextComponentProps>`
color: ${props => props.theme.primaryColor};
position: ${props => props.style.positionType};
left: ${props => {
return props.style.xPosition + props.style.xPositionUnit
}};
top: ${props => {
return props.style.yPosition + props.style.yPositionUnit
}};
`
class TextComponent extends React.Component<ITextComponentProps> { class TextComponent extends React.Component<ITextComponentProps> {
render() { render() {
return <Text ellipsize={this.props.ellipsize}>{this.props.text}</Text> return <TextContainer {...this.props}>{this.props.text}</TextContainer>
} }
} }

View File

@ -8,8 +8,7 @@ class Canvas extends Component<{ canvas: CanvasReduxState<any> }> {
render() { render() {
const canvasWidgetData = this.props.canvas.canvasWidgetProps const canvasWidgetData = this.props.canvas.canvasWidgetProps
if (canvasWidgetData) { if (canvasWidgetData) {
const canvasWidget = WidgetFactory.createWidget(canvasWidgetData) return WidgetFactory.createWidget(canvasWidgetData)
return canvasWidget.getWidgetView()
} else return <div></div> } else return <div></div>
} }
} }

View File

@ -6,7 +6,6 @@ import {
} from "../../constants/ActionConstants" } from "../../constants/ActionConstants"
import { IContainerWidgetProps } from "../../widgets/ContainerWidget" import { IContainerWidgetProps } from "../../widgets/ContainerWidget"
import { IWidgetProps } from "../../widgets/BaseWidget"; import { IWidgetProps } from "../../widgets/BaseWidget";
import { ITextWidgetProps } from "../../widgets/TextWidget";
const initialState: CanvasReduxState<any> = { const initialState: CanvasReduxState<any> = {
canvasWidgetProps: { canvasWidgetProps: {
@ -17,7 +16,7 @@ const initialState: CanvasReduxState<any> = {
widgetId: "1", widgetId: "1",
widgetType: "TEXT_WIDGET", widgetType: "TEXT_WIDGET",
topRow: 0, topRow: 0,
leftColumn: 0, leftColumn: 2,
bottomRow: 5, bottomRow: 5,
rightColumn: 5, rightColumn: 5,
parentColumnSpace: 100, parentColumnSpace: 100,
@ -26,9 +25,9 @@ const initialState: CanvasReduxState<any> = {
} }
], ],
topRow: 0, topRow: 0,
bottomRow: 100, bottomRow: 600,
leftColumn: 0, leftColumn: 0,
rightColumn: 100, rightColumn: 1200,
parentColumnSpace: 1, parentColumnSpace: 1,
parentRowSpace: 1 parentRowSpace: 1
} }

View File

@ -1,16 +1,16 @@
import { WidgetType } from "../constants/WidgetConstants"; import { WidgetType } from "../constants/WidgetConstants";
import BaseWidget, { IWidgetBuilder, IWidgetProps } from "../widgets/BaseWidget"; import { IWidgetBuilder, IWidgetProps } from "../widgets/BaseWidget";
import { IComponentProps } from "../editorComponents/BaseComponent";
class WidgetFactory { class WidgetFactory {
static widgetMap: Map<WidgetType, IWidgetBuilder<IWidgetProps, IComponentProps>> = new Map() static widgetMap: Map<WidgetType, IWidgetBuilder<IWidgetProps>> = new Map()
static registerWidgetBuilder(widgetType: WidgetType, widgetBuilder: IWidgetBuilder<IWidgetProps, IComponentProps>) { static registerWidgetBuilder(widgetType: WidgetType, widgetBuilder: IWidgetBuilder<IWidgetProps>) {
this.widgetMap.set(widgetType, widgetBuilder) this.widgetMap.set(widgetType, widgetBuilder)
} }
static createWidget(widgetData: IWidgetProps): BaseWidget<IWidgetProps, IComponentProps> { static createWidget(widgetData: IWidgetProps): JSX.Element {
widgetData.key = widgetData.widgetId
const widgetBuilder = this.widgetMap.get(widgetData.widgetType) const widgetBuilder = this.widgetMap.get(widgetData.widgetType)
if (widgetBuilder) if (widgetBuilder)
return widgetBuilder.buildWidget(widgetData) return widgetBuilder.buildWidget(widgetData)

View File

@ -5,9 +5,8 @@ import ContainerWidget, {
import TextWidget, { import TextWidget, {
ITextWidgetProps ITextWidgetProps
} from "../widgets/TextWidget" } from "../widgets/TextWidget"
import { IContainerProps } from "../editorComponents/ContainerComponent"
import WidgetFactory from "./WidgetFactory" import WidgetFactory from "./WidgetFactory"
import { ITextComponentProps } from "../editorComponents/TextComponent"; import React from "react"
class WidgetBuilderRegistry { class WidgetBuilderRegistry {
static registerWidgetBuilders() { static registerWidgetBuilders() {
@ -15,16 +14,16 @@ class WidgetBuilderRegistry {
WidgetFactory.registerWidgetBuilder("CONTAINER_WIDGET", { WidgetFactory.registerWidgetBuilder("CONTAINER_WIDGET", {
buildWidget( buildWidget(
widgetData: IContainerWidgetProps<IWidgetProps> widgetData: IContainerWidgetProps<IWidgetProps>
): BaseWidget<IContainerWidgetProps<IWidgetProps>, IContainerProps> { ): JSX.Element {
return new ContainerWidget(widgetData) return <ContainerWidget {...widgetData }/>
} }
}) })
WidgetFactory.registerWidgetBuilder("TEXT_WIDGET", { WidgetFactory.registerWidgetBuilder("TEXT_WIDGET", {
buildWidget( buildWidget(
widgetData: ITextWidgetProps widgetData: ITextWidgetProps
): BaseWidget<ITextWidgetProps, ITextComponentProps> { ): JSX.Element {
return new TextWidget(widgetData) return <TextWidget {...widgetData} />
} }
}) })

View File

@ -1,42 +1,73 @@
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 * Widget are responsible for accepting the abstraction layer inputs, interpretting them into rederable props and
* spawing components based on those props * spawing components based on those props
* Widgets are also responsible for dispatching actions and updating the state tree * Widgets are also responsible for dispatching actions and updating the state tree
*/ */
abstract class BaseWidget<T extends IWidgetProps, K extends IComponentProps> { import { WidgetType } from "../constants/WidgetConstants"
widgetData: T import { Component } from "react"
width: number
height: number
constructor(widgetProps: T) { abstract class BaseWidget<
this.widgetData = widgetProps T extends IWidgetProps,
this.width = K extends IWidgetState
(this.widgetData.rightColumn - this.widgetData.leftColumn) * > extends Component<T, K> {
widgetProps.parentColumnSpace componentDidMount(): void {
this.height = this.calculateWidgetBounds(
(this.widgetData.bottomRow - this.widgetData.topRow) * this.props.rightColumn,
widgetProps.parentRowSpace this.props.leftColumn,
this.props.topRow,
this.props.bottomRow,
this.props.parentColumnSpace,
this.props.parentRowSpace
)
} }
abstract getWidgetView(): React.Component<K> componentWillReceiveProps(prevProps: T, nextProps: T) {
this.calculateWidgetBounds(
nextProps.rightColumn,
nextProps.leftColumn,
nextProps.topRow,
nextProps.bottomRow,
nextProps.parentColumnSpace,
nextProps.parentRowSpace
)
}
abstract getComponentProps(): K calculateWidgetBounds(
rightColumn: number,
leftColumn: number,
topRow: number,
bottomRow: number,
parentColumnSpace: number,
parentRowSpace: number
) {
const widgetState: IWidgetState = {
width: (rightColumn - leftColumn) * parentColumnSpace,
height: (bottomRow - topRow) * parentRowSpace
}
this.setState(widgetState)
}
render() {
return this.getWidgetView()
}
abstract getWidgetView(): JSX.Element
abstract getWidgetType(): WidgetType abstract getWidgetType(): WidgetType
} }
export interface IWidgetBuilder< export interface IWidgetState {
T extends IWidgetProps, height: number
K extends IComponentProps width: number
> { }
buildWidget(data: T): BaseWidget<T, K>
export interface IWidgetBuilder<T extends IWidgetProps> {
buildWidget(data: T): JSX.Element
} }
export interface IWidgetProps { export interface IWidgetProps {
widgetType: WidgetType widgetType: WidgetType
key?: string
widgetId: string widgetId: string
topRow: number topRow: number
leftColumn: number leftColumn: number

View File

@ -1,49 +1,68 @@
import * as React from "react" import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget"
import BaseWidget, { IWidgetProps } from "./BaseWidget"
import ContainerComponent, { import ContainerComponent, {
IContainerProps IContainerProps
} from "../editorComponents/ContainerComponent" } from "../editorComponents/ContainerComponent"
import { ContainerOrientation, WidgetType } from "../constants/WidgetConstants" import { ContainerOrientation, WidgetType, CSSUnits } from "../constants/WidgetConstants"
import WidgetFactory from "../utils/WidgetFactory" import WidgetFactory from "../utils/WidgetFactory"
import React from "react"
import _ from "lodash"
const DEFAULT_NUM_COLS = 13
const DEFAULT_NUM_ROWS = 13
class ContainerWidget extends BaseWidget< class ContainerWidget extends BaseWidget<
IContainerWidgetProps<IWidgetProps>, IContainerWidgetProps<IWidgetProps>,
IContainerProps IWidgetState
> { > {
constructor(widgetProps: IContainerWidgetProps<IWidgetProps>) { snapColumnSpace: number = 100
super(widgetProps) snapRowSpace: number = 100
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 { constructor(props: IContainerWidgetProps<IWidgetProps>) {
return { super(props)
widgetId: this.widgetData.widgetId, this.renderChildWidget = this.renderChildWidget.bind(this)
snapColumnSpace: this.widgetData.snapColumnSpace, this.state = {
snapRowSpace: this.widgetData.snapRowSpace, height: 0,
snapColumns: this.widgetData.snapColumns, width: 0
snapRows: this.widgetData.snapRows,
orientation: this.widgetData.orientation
} }
} }
getWidgetView(): any { componentWillReceiveProps(
previousProps: IContainerWidgetProps<IWidgetProps>,
nextProps: IContainerWidgetProps<IWidgetProps>
) {
super.componentWillReceiveProps(previousProps, nextProps)
this.snapColumnSpace =
this.state.width / (nextProps.snapColumns || DEFAULT_NUM_COLS)
this.snapRowSpace =
this.state.height / (nextProps.snapRows || DEFAULT_NUM_ROWS)
}
renderChildWidget(childWidgetData: IWidgetProps) {
childWidgetData.parentColumnSpace = this.snapColumnSpace
childWidgetData.parentRowSpace = this.snapRowSpace
return WidgetFactory.createWidget(childWidgetData)
}
getWidgetView() {
return ( return (
<ContainerComponent {...this.getComponentProps()}> <ContainerComponent
{this.widgetData.children widgetId={this.props.widgetId}
? this.widgetData.children.map(childWidgetData => { style={{
childWidgetData.parentColumnSpace = this.widgetData height: this.state.height,
.snapColumnSpace width: this.state.width,
? this.widgetData.snapColumnSpace positionType: "ABSOLUTE",
: 0 yPosition: this.props.topRow * this.props.parentRowSpace,
childWidgetData.parentRowSpace = this.widgetData.snapRowSpace xPosition: this.props.leftColumn * this.props.parentColumnSpace,
? this.widgetData.snapRowSpace xPositionUnit: CSSUnits.PIXEL,
: 0 yPositionUnit: CSSUnits.PIXEL
return WidgetFactory.createWidget(childWidgetData).getWidgetView() }}
}) snapColumnSpace={this.snapColumnSpace}
: undefined} snapRowSpace={this.snapRowSpace}
snapColumns={this.props.snapColumns || DEFAULT_NUM_COLS}
snapRows={this.props.snapRows || DEFAULT_NUM_ROWS}
orientation={this.props.orientation || "VERTICAL"}
>
{_.map(this.props.children, this.renderChildWidget)}
</ContainerComponent> </ContainerComponent>
) )
} }
@ -53,10 +72,9 @@ class ContainerWidget extends BaseWidget<
} }
} }
export interface IContainerWidgetProps<T extends IWidgetProps> extends IWidgetProps { export interface IContainerWidgetProps<T extends IWidgetProps>
extends IWidgetProps {
children?: T[] children?: T[]
snapColumnSpace?: number
snapRowSpace?: number
snapColumns?: number snapColumns?: number
snapRows?: number snapRows?: number
orientation?: ContainerOrientation orientation?: ContainerOrientation

View File

@ -1,29 +1,27 @@
import * as React from "react" import * as React from "react"
import BaseWidget, { IWidgetProps } from "./BaseWidget" import BaseWidget, { IWidgetProps, IWidgetState } from "./BaseWidget"
import { WidgetType } from "../constants/WidgetConstants" import { WidgetType, CSSUnits } from "../constants/WidgetConstants"
import TextComponent, { import TextComponent from "../editorComponents/TextComponent"
ITextComponentProps
} from "../editorComponents/TextComponent"
import _ from "lodash" import _ from "lodash"
class TextWidget extends BaseWidget<ITextWidgetProps, ITextComponentProps> { class TextWidget extends BaseWidget<ITextWidgetProps, IWidgetState> {
constructor(widgetProps: ITextWidgetProps) { constructor(widgetProps: ITextWidgetProps) {
super(widgetProps) super(widgetProps)
} }
getComponentProps(): ITextComponentProps { getWidgetView() {
return {
widgetId: this.widgetData.widgetId,
text: !_.isNil(this.widgetData.text) ? this.widgetData.text : "Hello World",
ellipsize: this.widgetData.ellipsize === true
}
}
getWidgetView(): any {
return ( return (
<TextComponent <TextComponent
key={this.widgetData.widgetId} style={{
{...this.getComponentProps()} positionType: "ABSOLUTE",
yPosition: this.props.topRow * this.props.parentRowSpace,
xPosition: this.props.leftColumn * this.props.parentColumnSpace,
xPositionUnit: CSSUnits.PIXEL,
yPositionUnit: CSSUnits.PIXEL
}}
widgetId={this.props.widgetId}
key={this.props.widgetId}
text={this.props.text}
/> />
) )
} }