diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Main.scss | 25 | ||||
-rw-r--r-- | src/Main.tsx | 32 | ||||
-rw-r--r-- | src/Utils.ts | 11 | ||||
-rw-r--r-- | src/stores/NodeCollectionStore.ts | 21 | ||||
-rw-r--r-- | src/stores/NodeStore.ts | 24 | ||||
-rw-r--r-- | src/stores/RootStore.ts | 16 | ||||
-rw-r--r-- | src/stores/StaticTextNodeStore.ts | 16 | ||||
-rw-r--r-- | src/stores/VideoNodeStore.ts | 17 | ||||
-rw-r--r-- | src/views/freeformcanvas/FreeFormCanvas.scss | 15 | ||||
-rw-r--r-- | src/views/freeformcanvas/FreeFormCanvas.tsx | 54 | ||||
-rw-r--r-- | src/views/freeformcanvas/NodeContainer.tsx | 30 | ||||
-rw-r--r-- | src/views/nodes/NodeView.scss | 33 | ||||
-rw-r--r-- | src/views/nodes/TextNodeView.tsx | 28 | ||||
-rw-r--r-- | src/views/nodes/TopBar.tsx | 46 | ||||
-rw-r--r-- | src/views/nodes/VideoNodeView.scss | 5 | ||||
-rw-r--r-- | src/views/nodes/VideoNodeView.tsx | 29 |
16 files changed, 402 insertions, 0 deletions
diff --git a/src/Main.scss b/src/Main.scss new file mode 100644 index 000000000..f986d2c96 --- /dev/null +++ b/src/Main.scss @@ -0,0 +1,25 @@ +html, +body { + width: 100%; + height: 100%; + overflow: hidden; + font-family: 'Hind Siliguri', sans-serif; +} + +h1 { + font-size: 50px; + position: fixed; + top: 30px; + left: 50%; + transform: translateX(-50%); + color: black; + text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff; + z-index: 9999; + font-family: 'Fjalla One', sans-serif; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +}
\ No newline at end of file diff --git a/src/Main.tsx b/src/Main.tsx new file mode 100644 index 000000000..99401bf65 --- /dev/null +++ b/src/Main.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import "./Main.scss"; +import { NodeCollectionStore } from './stores/NodeCollectionStore'; +import { RootStore } from './stores/RootStore'; +import { StaticTextNodeStore } from './stores/StaticTextNodeStore'; +import { VideoNodeStore } from './stores/VideoNodeStore'; +import { FreeFormCanvas } from './views/freeformcanvas/FreeFormCanvas'; + + +const mainNodeCollection = new NodeCollectionStore(); +ReactDOM.render(( + <div> + <h1>Dash Web</h1> + <FreeFormCanvas store={mainNodeCollection} /> + </div>), document.getElementById('root')); + + + +// create a bunch of text and video nodes (you probably want to delete this at some point) +let numNodes = 300; +let maxX = 10000; +let maxY = 10000; +let nodes = [] +for (let i = 0; i < numNodes; i++) { + nodes.push(new StaticTextNodeStore({ X: Math.random() * maxX, Y: Math.random() * maxY, Title: "Text Node Title", Text: "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?" })); +} + +for (let i = 0; i < 20; i++) { + nodes.push(new VideoNodeStore({ X: Math.random() * maxX, Y: Math.random() * maxY, Title: "Video Node Title", Url: "http://cs.brown.edu/people/peichman/downloads/cted.mp4" })); +} +mainNodeCollection.AddNodes(nodes);
\ No newline at end of file diff --git a/src/Utils.ts b/src/Utils.ts new file mode 100644 index 000000000..7f26bc394 --- /dev/null +++ b/src/Utils.ts @@ -0,0 +1,11 @@ + + +export class Utils { + + public static GenerateGuid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + } +}
\ No newline at end of file diff --git a/src/stores/NodeCollectionStore.ts b/src/stores/NodeCollectionStore.ts new file mode 100644 index 000000000..8c7f7623b --- /dev/null +++ b/src/stores/NodeCollectionStore.ts @@ -0,0 +1,21 @@ +import { computed, observable, action } from "mobx"; +import { NodeStore } from "./NodeStore"; + +export class NodeCollectionStore extends NodeStore { + + @observable + public Scale: number = 1; + + @observable + public Nodes: NodeStore[] = new Array<NodeStore>(); + + @computed + public get Transform(): string { + return "translate(" + this.X + "px," + this.Y + "px) scale(" + this.Scale + "," + this.Scale + ")"; + } + + @action + public AddNodes(stores: NodeStore[]): void { + stores.forEach(store => this.Nodes.push(store)); + } +}
\ No newline at end of file diff --git a/src/stores/NodeStore.ts b/src/stores/NodeStore.ts new file mode 100644 index 000000000..6a734cf44 --- /dev/null +++ b/src/stores/NodeStore.ts @@ -0,0 +1,24 @@ +import { computed, observable } from "mobx"; +import { Utils } from "../Utils"; + +export class NodeStore { + + public Id: string = Utils.GenerateGuid(); + + @observable + public X: number = 0; + + @observable + public Y: number = 0; + + @observable + public Width: number = 0; + + @observable + public Height: number = 0; + + @computed + public get Transform(): string { + return "translate(" + this.X + "px, " + this.Y + "px)"; + } +}
\ No newline at end of file diff --git a/src/stores/RootStore.ts b/src/stores/RootStore.ts new file mode 100644 index 000000000..fa551c1d1 --- /dev/null +++ b/src/stores/RootStore.ts @@ -0,0 +1,16 @@ +import { action, observable } from "mobx"; +import { NodeStore } from "./NodeStore"; + +// This globally accessible store might come in handy, although you may decide that you don't need it. +export class RootStore { + + private constructor() { + // initialization code + } + + private static _instance: RootStore; + + public static get Instance():RootStore { + return this._instance || (this._instance = new this()); + } +}
\ No newline at end of file diff --git a/src/stores/StaticTextNodeStore.ts b/src/stores/StaticTextNodeStore.ts new file mode 100644 index 000000000..7c342a7a2 --- /dev/null +++ b/src/stores/StaticTextNodeStore.ts @@ -0,0 +1,16 @@ +import { observable } from "mobx"; +import { NodeStore } from "./NodeStore"; + +export class StaticTextNodeStore extends NodeStore { + + constructor(initializer: Partial<StaticTextNodeStore>) { + super(); + Object.assign(this, initializer); + } + + @observable + public Title: string = ""; + + @observable + public Text: string = ""; +}
\ No newline at end of file diff --git a/src/stores/VideoNodeStore.ts b/src/stores/VideoNodeStore.ts new file mode 100644 index 000000000..41fae2aff --- /dev/null +++ b/src/stores/VideoNodeStore.ts @@ -0,0 +1,17 @@ +import { observable } from "mobx"; +import { NodeStore } from "./NodeStore"; + +export class VideoNodeStore extends NodeStore { + + constructor(initializer: Partial<VideoNodeStore>) { + super(); + Object.assign(this, initializer); + } + + @observable + public Title: string; + + @observable + public Url: string; + +}
\ No newline at end of file diff --git a/src/views/freeformcanvas/FreeFormCanvas.scss b/src/views/freeformcanvas/FreeFormCanvas.scss new file mode 100644 index 000000000..884ef90e6 --- /dev/null +++ b/src/views/freeformcanvas/FreeFormCanvas.scss @@ -0,0 +1,15 @@ +.freeformcanvas-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + + .freeformcanvas { + position: absolute; + top: 0; + left: 0; + } +} + diff --git a/src/views/freeformcanvas/FreeFormCanvas.tsx b/src/views/freeformcanvas/FreeFormCanvas.tsx new file mode 100644 index 000000000..f02f677a3 --- /dev/null +++ b/src/views/freeformcanvas/FreeFormCanvas.tsx @@ -0,0 +1,54 @@ +import { observer } from "mobx-react"; +import { NodeCollectionStore } from "../../stores/NodeCollectionStore"; +import "./FreeFormCanvas.scss"; +import { NodeContainer } from "./NodeContainer"; +import React = require("react"); + +interface IProps { + store: NodeCollectionStore +} + +@observer +export class FreeFormCanvas extends React.Component<IProps> { + + private _isPointerDown: boolean; + + onPointerDown = (e: React.PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isPointerDown = true; + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + } + + onPointerUp = (e: PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isPointerDown = false; + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } + + onPointerMove = (e: PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + if (!this._isPointerDown) { + return; + } + this.props.store.X += e.movementX; + this.props.store.Y += e.movementY; + } + + render() { + let store = this.props.store; + return ( + <div className="freeformcanvas-container" onPointerDown={this.onPointerDown}> + <div className="freeformcanvas" style={{ transform: store.Transform }}> + <NodeContainer store={store} /> + </div> + </div> + ); + } +}
\ No newline at end of file diff --git a/src/views/freeformcanvas/NodeContainer.tsx b/src/views/freeformcanvas/NodeContainer.tsx new file mode 100644 index 000000000..a2edb38a4 --- /dev/null +++ b/src/views/freeformcanvas/NodeContainer.tsx @@ -0,0 +1,30 @@ +import { observer } from "mobx-react"; +import { NodeCollectionStore } from "../../stores/NodeCollectionStore"; +import { StaticTextNodeStore } from "../../stores/StaticTextNodeStore"; +import { VideoNodeStore } from "../../stores/VideoNodeStore"; +import { TextNodeView } from "../nodes/TextNodeView"; +import { VideoNodeView } from "../nodes/VideoNodeView"; +import "./FreeFormCanvas.scss"; +import React = require("react"); + +interface IProps { + store: NodeCollectionStore +} + +@observer +export class NodeContainer extends React.Component<IProps> { + + render() { + return ( + <div className="node-container"> + {this.props.store.Nodes.map(nodeStore => { + if (nodeStore instanceof StaticTextNodeStore) { + return (<TextNodeView key={nodeStore.Id} store={nodeStore as StaticTextNodeStore} />) + } else if (nodeStore instanceof VideoNodeStore) { + return (<VideoNodeView key={nodeStore.Id} store={nodeStore as VideoNodeStore} />) + } + })} + </div> + ); + } +}
\ No newline at end of file diff --git a/src/views/nodes/NodeView.scss b/src/views/nodes/NodeView.scss new file mode 100644 index 000000000..2dfdee6fa --- /dev/null +++ b/src/views/nodes/NodeView.scss @@ -0,0 +1,33 @@ +.node { + position: absolute; + background: #cdcdcd; + + width: 300px; + height: 300px; + overflow: hidden; + + + &.minimized { + width: 30px; + height: 30px; + } + + .top { + background: #232323; + height: 20px; + cursor: pointer; + } + + .content { + padding: 20px 20px; + height: auto; + box-sizing: border-box; + + } + + .scroll-box { + overflow-y: scroll; + height: calc(100% - 20px); + } +} + diff --git a/src/views/nodes/TextNodeView.tsx b/src/views/nodes/TextNodeView.tsx new file mode 100644 index 000000000..4831e658c --- /dev/null +++ b/src/views/nodes/TextNodeView.tsx @@ -0,0 +1,28 @@ +import { observer } from "mobx-react"; +import { StaticTextNodeStore } from "../../stores/StaticTextNodeStore"; +import "./NodeView.scss"; +import { TopBar } from "./TopBar"; +import React = require("react"); + +interface IProps { + store: StaticTextNodeStore; +} + +@observer +export class TextNodeView extends React.Component<IProps> { + + render() { + let store = this.props.store; + return ( + <div className="node text-node" style={{ transform: store.Transform }}> + <TopBar store={store} /> + <div className="scroll-box"> + <div className="content"> + <h3 className="title">{store.Title}</h3> + <p className="paragraph">{store.Text}</p> + </div> + </div> + </div> + ); + } +}
\ No newline at end of file diff --git a/src/views/nodes/TopBar.tsx b/src/views/nodes/TopBar.tsx new file mode 100644 index 000000000..bb126e8b5 --- /dev/null +++ b/src/views/nodes/TopBar.tsx @@ -0,0 +1,46 @@ +import { observer } from "mobx-react"; +import { NodeStore } from "../../stores/NodeStore"; +import "./NodeView.scss"; +import React = require("react"); + +interface IProps { + store: NodeStore; +} + +@observer +export class TopBar extends React.Component<IProps> { + + private _isPointerDown = false; + + onPointerDown = (e: React.PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isPointerDown = true; + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + } + + onPointerUp = (e: PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isPointerDown = false; + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } + + onPointerMove = (e: PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + if (!this._isPointerDown) { + return; + } + this.props.store.X += e.movementX; + this.props.store.Y += e.movementY; + } + + render() { + return <div className="top" onPointerDown={this.onPointerDown}></div> + } +} diff --git a/src/views/nodes/VideoNodeView.scss b/src/views/nodes/VideoNodeView.scss new file mode 100644 index 000000000..f412c3519 --- /dev/null +++ b/src/views/nodes/VideoNodeView.scss @@ -0,0 +1,5 @@ +.node { + video { + width: 100%; + } +}
\ No newline at end of file diff --git a/src/views/nodes/VideoNodeView.tsx b/src/views/nodes/VideoNodeView.tsx new file mode 100644 index 000000000..0a7b3d174 --- /dev/null +++ b/src/views/nodes/VideoNodeView.tsx @@ -0,0 +1,29 @@ +import { observer } from "mobx-react"; +import { VideoNodeStore } from "../../stores/VideoNodeStore"; +import "./NodeView.scss"; +import { TopBar } from "./TopBar"; +import "./VideoNodeView.scss"; +import React = require("react"); + +interface IProps { + store: VideoNodeStore; +} + +@observer +export class VideoNodeView extends React.Component<IProps> { + + render() { + let store = this.props.store; + return ( + <div className="node text-node" style={{ transform: store.Transform }}> + <TopBar store={store} /> + <div className="scroll-box"> + <div className="content"> + <h3 className="title">{store.Title}</h3> + <video src={store.Url} controls /> + </div> + </div> + </div> + ); + } +}
\ No newline at end of file |