Last active
November 22, 2016 17:16
-
-
Save kristian76/a41bac0b5a03856337c793008ab55753 to your computer and use it in GitHub Desktop.
task board made with rect
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(); | |
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() { | |
this.setState({open: false}); | |
} | |
componentWillReceiveProps(nextProps) { | |
if (this.state !== nextProps) { | |
this.setState(nextProps); | |
} | |
} | |
} | |
class BoardDialog extends UIComponent { | |
handleSave(e) { | |
} | |
handleCancel(e) { | |
this._hideUI(); | |
} | |
render() { | |
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"> | |
hello | |
</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 | |
); |
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
{ | |
"name": "plana", | |
"version": "1.0.0", | |
"description": "", | |
"main": "index.js", | |
"scripts": { | |
"start": "node server.js" | |
}, | |
"author": "", | |
"license": "ISC", | |
"dependencies": { | |
} | |
} |
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
var path = require('path'), | |
webpack = require('webpack'); | |
module.exports = { | |
devtool: 'source-map', | |
target: 'web', | |
node: { | |
fs: 'empty' | |
}, | |
entry: { | |
dafuck: __dirname +'/dafuck/src/index.js' | |
}, | |
output: { | |
path: __dirname, filename: '[name]/dist/js/bundle.js' | |
}, | |
module: { | |
loaders: [ | |
{ | |
test: /.js?$/, | |
loader: 'babel-loader', | |
exclude: /node_modules/, | |
query: { | |
presets: ['es2015', 'react'] | |
} | |
}, | |
{ | |
test: /\.json$/, | |
loader: 'json' | |
}, | |
{ | |
test: /\.css$/, | |
loader: 'style-loader!css-loader' | |
} | |
] | |
}, | |
plugins: [ | |
new webpack.DefinePlugin({ "global.GENTLY": false }) | |
] | |
}; |
Move https://gist.github.com/kristian76/a41bac0b5a03856337c793008ab55753#file-index-js-L52 to something like localStorage or a cookie
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add Math.floor to https://gist.github.com/kristian76/a41bac0b5a03856337c793008ab55753#file-index-js-L20 to avoid floats