Created
January 30, 2017 13:58
-
-
Save kristian76/4988d17e1653b8b74bfda4e1e74524e6 to your computer and use it in GitHub Desktop.
React material UI planning board
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* index.js | |
*/ | |
"use strict"; | |
// Require app specific CSS | |
require('./app.css'); | |
import React from 'react'; | |
import ReactDOM from 'react-dom'; | |
// Stores a userfriendly message | |
const TASK_STATUS = { | |
to_do: 'To Do', | |
in_progress: 'In Progress', | |
done: 'Done' | |
}; | |
function futureDate() { | |
return (new Date()).getTime() + ((Math.random() * 30) * 25*60*60*1000); | |
} | |
class Task extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
name: '', | |
owner: '', | |
due: '', | |
status: '' | |
}; | |
this.changeStatus = this.changeStatus.bind(this); | |
} | |
componentDidMount() { | |
// this.setState(this.props); | |
} | |
componentWillMount() { | |
this.setState(this.props.data); | |
} | |
changeStatus(e) { | |
e.preventDefault(); | |
this.props.onUpdate(this.state); | |
} | |
render() { | |
let dueDate = new Date(this.state.due); | |
let local = { | |
day: 'numeric', | |
year: '2-digit', | |
month: 'short' | |
}; | |
let taskStatusView = Object.keys(TASK_STATUS).map((key) => { | |
return <div className="mdl-cell mdl-cell--4-col" key={ key } onClick={ this.changeStatus }> | |
{ TASK_STATUS[key] } | |
</div> | |
}); | |
return <div className="mdl-grid mdl-grid--no-spacing data-row"> | |
<div className="mdl-cell mdl-cell--4-col">{ this.state.name }</div> | |
<div className="mdl-cell mdl-cell--2-col"> | |
{ this.state.owner } | |
</div> | |
<div className="mdl-cell mdl-cell--2-col">{ dueDate.toLocaleString('da-DK', local) }</div> | |
<div className="mdl-cell mdl-cell--4-col mdl-grid mdl-grid--no-spacing"> | |
{ taskStatusView } | |
</div> | |
</div>; | |
} | |
} | |
class Board extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
tasks: [] | |
}; | |
this.updateTask = this.updateTask.bind(this); | |
this.addTask = this.addTask.bind(this); | |
} | |
updateTask(payLoad) { | |
this.props.onTaskUpdate(payLoad); | |
} | |
addTask(e) { | |
e.preventDefault(); | |
this.props.onTaskUpdate({ | |
board: this.props.name | |
}); | |
} | |
render() { | |
this.props.tasks.sort((a, b) => { | |
if (a.due < b.due) { | |
return -1; | |
} | |
if (a.due > b.due) { | |
return 1; | |
} | |
return 0; | |
}); | |
let tasks = this.props.tasks.map((task, key) => { | |
let obj = task; | |
obj.board = this.props.name; | |
return <Task data={ obj } key={key} onUpdate={ this.updateTask } /> | |
}); | |
return <div className="mdl-grid"> | |
<div className="mdl-cell mdl-cell--12-col"> | |
<div className="mdl-grid"> | |
<div className="mdl-cell mdl-cell--4-col data-row-title">{ this.props.name }</div> | |
<div className="mdl-cell mdl-cell--2-col data-row-title">Owner</div> | |
<div className="mdl-cell mdl-cell--2-col data-row-title">Due</div> | |
<div className="mdl-cell mdl-cell--4-col data-row-title">Status</div> | |
</div> | |
<div className="mdl-grid"> | |
<div className="mdl-cell mdl-cell--12-col"> | |
{ tasks } | |
</div> | |
</div> | |
</div> | |
<div className="mdl-cell mdl-cell--2-col mdl-cell--10-offset"> | |
<button type="button" className="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" | |
onClick={ this.addTask }>Add Task</button> | |
</div> | |
</div>; | |
} | |
} | |
/** | |
* App | |
*/ | |
class App extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
data: [], | |
visible: false, | |
dialog: { | |
data: {}, | |
open: false | |
}, | |
boarddialog: { | |
data: {}, | |
open: false | |
} | |
}; | |
this.handleTaskUpdate = this.handleTaskUpdate.bind(this); | |
this.addBoard = this.addBoard.bind(this); | |
} | |
componentDidMount() { | |
let stub = [ | |
{ | |
"name": "board 1", | |
"tasks": [ | |
{ | |
"name": "super", | |
"id": 1, | |
"owner": "superman", | |
"status": "in_progress", | |
"due": futureDate() | |
}, | |
{ | |
"name": "nice", | |
"id": 2, | |
"owner": "batman", | |
"status": "to_do", | |
"due": futureDate() | |
}, | |
{ | |
"name": "awesome", | |
"id": 3, | |
"owner": "cat woman", | |
"status": "done", | |
"due": futureDate() | |
}, | |
{ | |
"name": "sweet", | |
"id": 4, | |
"owner": "hulk", | |
"status": "to_do", | |
"due": futureDate() | |
}, | |
] | |
}, | |
{ | |
"name": "board 2", | |
"tasks": [ | |
{ | |
"name": "super", | |
"id": 5, | |
"owner": "superman", | |
"status": "in_progress", | |
"due": futureDate() | |
}, | |
{ | |
"name": "nice", | |
"id": 6, | |
"owner": "batman", | |
"status": "to_do", | |
"due": futureDate() | |
}, | |
{ | |
"name": "awesome", | |
"id": 7, | |
"owner": "cat woman", | |
"status": "done", | |
"due": futureDate() | |
}, | |
{ | |
"name": "sweet", | |
"id": 8, | |
"owner": "hulk", | |
"status": "on_hold", | |
"due": futureDate() | |
}, | |
] | |
}, | |
]; | |
this.setState({ data: stub }); | |
} | |
handleTaskUpdate(payload) { | |
// Receive task data, set state and open dialog | |
this.setState({ | |
dialog: { | |
open: true, | |
data: payload, | |
title: 'Update' | |
} | |
}); | |
} | |
saveTask(data) { | |
console.log('saving task', data); | |
} | |
addBoard(e) { | |
e.preventDefault(); | |
console.log('here'); | |
this.setState({ | |
boarddialog: { | |
open: true, | |
data: {} | |
} | |
}); | |
} | |
render() { | |
// console.log(this.state); | |
// Render all boards | |
let boards = this.state.data.map((board, id) => { | |
return <Board key={id} tasks={board.tasks} name={board.name} onTaskUpdate={ this.handleTaskUpdate } /> | |
}); | |
let boardNames = this.state.data.map((board, id) => { | |
return <button className="mdl-chip" key={ id }> | |
<span className="mdl-chip__text"> | |
{ board.name } | |
</span> | |
<a href="#" className="mdl-chip__action"> | |
<i className="material-icons">sort</i> | |
</a> | |
</button>; | |
}); | |
return <div className="mdl-layout mdl-js-layout mdl-layout--fixed-header is-upgraded"> | |
<header className="mdl-layout__header is-casting-shadow mdl-color--grey-100 mdl-color-text--grey-600"> | |
<div className="mdl-layout__header-row"> | |
<span className="mdl-layout-title">Title</span> | |
</div> | |
</header> | |
<div className="mdl-layout__drawer"> | |
<header> | |
<button className="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon"></button> | |
</header> | |
<nav className="mdl-navigation"> | |
<a href="" className="mdl-navigation__link">Hello</a> | |
</nav> | |
</div> | |
<main className="mdl-layout__content"> | |
<div className="mdl-grid"> | |
<div className="mdl-cell mdl-cell--12-col"> | |
<button className="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" onClick={ this.addBoard.bind(this) }>Add Board</button> | |
</div> | |
</div> | |
<div className="mdl-grid"> | |
<div className="mdl-cell mdl-cell--12-col"> | |
{ boards } | |
</div> | |
</div> | |
</main> | |
<TaskDialog open={ this.state.dialog.open } data={ this.state.dialog.data } onSave={ this.saveTask.bind(this) } /> | |
<BoardDialog open={ this.state.boarddialog.open } data={ this.state.boarddialog.data } /> | |
</div>; | |
} | |
} | |
/** | |
* UI Components | |
*/ | |
class UIComponent extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
open: false, | |
data: null | |
}; | |
} | |
_hideUI() { | |
console.log('Hiding UI'); | |
this.setState({open: false}); | |
} | |
componentWillReceiveProps(nextProps) { | |
if (this.state !== nextProps) { | |
this.setState(nextProps); | |
} | |
} | |
} | |
class BoardDialog extends UIComponent { | |
changeName(e) { | |
let data = this.state.data; | |
data.name = e.target.value; | |
this.setState({data: data}); | |
} | |
handleSave(e) { | |
} | |
handleCancel(e) { | |
this._hideUI(); | |
} | |
render() { | |
if ( this.state.data !== null ) { | |
return <dialog className="mdl-dialog" id="dialog" open={ this.state.open } ref={(dialog) => this.dialog = dialog}> | |
<h3 className="mdl-dialog__title">Board</h3> | |
<div className="mdl-dialog__content"> | |
<div className="mdl-textfield mdl-js-textfield is-upgraded"> | |
<input className="mdl-textfield__input" type="text" | |
value={ this.state.data.name } onChange={ this.changeName.bind(this) } /> | |
<label className="mdl-textfield__label">Name</label> | |
</div> | |
</div> | |
<div className="mdl-dialog__actions"> | |
<button type="button" className="mdl-button" onClick={ this.handleSave.bind(this) }>Save</button> | |
<button type="button" className="mdl-button" onClick={ this.handleCancel.bind(this) }>Cancel</button> | |
</div> | |
</dialog>; | |
} else { | |
return <dialog className="mdl-dialog" id="dialog" open={ this.state.open } ref={(dialog) => this.dialog = dialog}> | |
<h3 className="mdl-dialog__title">Board</h3> | |
<div className="mdl-dialog__content"> | |
<div className="mdl-textfield mdl-js-textfield is-upgraded"> | |
<input className="mdl-textfield__input" type="text" | |
value="" onChange={ this.changeName.bind(this) } /> | |
<label className="mdl-textfield__label">Name</label> | |
</div> | |
</div> | |
<div className="mdl-dialog__actions"> | |
<button type="button" className="mdl-button" onClick={ this.handleSave.bind(this) }>Save</button> | |
<button type="button" className="mdl-button" onClick={ this.handleCancel.bind(this) }>Cancel</button> | |
</div> | |
</dialog>; | |
} | |
} | |
} | |
class TaskDialog extends UIComponent { | |
handleSave(e) { | |
e.preventDefault(); | |
this.props.onSave(this.state.data); | |
this._hideUI(); | |
} | |
handleCancel(e) { | |
e.preventDefault(); | |
this._hideUI(); | |
} | |
changeDue(e) { | |
// FIXME handle date format | |
let data = this.state.data; | |
data.due = parseFloat(e.target.value); | |
this.setState({data: data}); | |
} | |
changeName(e) { | |
let data = this.state.data; | |
data.name = e.target.value; | |
this.setState({data: data}); | |
} | |
changeOwner(e) { | |
let data = this.state.data; | |
data.owner = e.target.value; | |
this.setState({data: data}); | |
} | |
changeStatus(e) { | |
let data = this.state.data; | |
data.status = e.target.value; | |
this.setState({data: data}); | |
} | |
render() { | |
// console.log(this.state); | |
let form = <form></form>; | |
if (this.state.data !== null) { | |
form = <form> | |
<div className="mdl-textfield mdl-js-textfield is-upgraded"> | |
<input className="mdl-textfield__input" type="text" | |
value={ this.state.data.due } onChange={ this.changeDue.bind(this) } /> | |
<label className="mdl-textfield__label">Due</label> | |
</div> | |
<div className="mdl-textfield mdl-js-textfield is-upgraded"> | |
<input className="mdl-textfield__input" type="text" | |
value={ this.state.data.name } onChange={ this.changeName.bind(this) } /> | |
<label className="mdl-textfield__label">Name</label> | |
</div> | |
<div className="mdl-textfield mdl-js-textfield is-upgraded"> | |
<input className="mdl-textfield__input" type="text" | |
value={ this.state.data.owner } onChange={ this.changeOwner.bind(this) } /> | |
<label className="mdl-textfield__label">Owner</label> | |
</div> | |
<div className="mdl-textfield mdl-js-textfield is-upgraded"> | |
<input className="mdl-textfield__input" list="status" | |
value={ this.state.data.status } onChange={ this.changeStatus.bind(this) } /> | |
<datalist id="status"> | |
<option value="to_do"/> | |
<option value="in_progress"/> | |
<option value="on_hold"/> | |
<option value="done" /> | |
</datalist> | |
</div> | |
</form>; | |
} | |
return <dialog className="mdl-dialog" id="dialog" open={ this.state.open } ref={(dialog) => this.dialog = dialog}> | |
<h3 className="mdl-dialog__title">Task</h3> | |
<div className="mdl-dialog__content"> | |
{ form } | |
</div> | |
<div className="mdl-dialog__actions"> | |
<button type="button" className="mdl-button" onClick={ this.handleSave.bind(this) }>Save</button> | |
<button type="button" className="mdl-button" onClick={ this.handleCancel.bind(this) }>Cancel</button> | |
</div> | |
</dialog>; | |
} | |
} | |
const ROOT = document.getElementById('appmount'); | |
ROOT.setAttribute('class', 'mdl-layout__container'); | |
ReactDOM.render( | |
<App />, | |
ROOT | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment