-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
44d5e77
commit 4d45e79
Showing
23 changed files
with
1,673 additions
and
11 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import React, {Component, PropTypes} from 'react' | ||
import marked from 'marked' | ||
import ReactCSSTransitionGroup from 'react-addons-css-transition-group' | ||
import {DragSource, DropTarget} from 'react-dnd' | ||
import CheckList from './CheckList' | ||
import constants from './constants' | ||
|
||
let titlePropType = (props, propName, componentName) => { | ||
if(props[propName]) { | ||
let value = props[propName] | ||
if(typeof value !== 'string' || value.length > 80) { | ||
return new Error(`${propName} in ${componentName} is longer than 80 characters`) | ||
} | ||
} | ||
} | ||
|
||
const cardDragSpec = { | ||
beginDrag(props) { | ||
return { | ||
id: props.id | ||
} | ||
}, | ||
endDrag(props) { | ||
props.cardCallbacks.persistCardDrag(props.id, props.status) | ||
} | ||
} | ||
|
||
const cardDropSpec = { | ||
hover(props, monitor) { | ||
const draggedId = monitor.getItem().id | ||
props.cardCallbacks.updatePosition(draggedId, props.id) | ||
} | ||
} | ||
|
||
let collectDrag = (connect, monitor) => { | ||
return { | ||
connectDragSource: connect.dragSource() | ||
} | ||
} | ||
|
||
let collectDrop = (connect, monitor) => { | ||
return { | ||
connectDropTarget: connect.dropTarget() | ||
} | ||
} | ||
|
||
class Card extends Component { | ||
constructor() { | ||
super(...arguments); | ||
this.state = { | ||
showDetails: false | ||
}; | ||
} | ||
|
||
toggleDetails() { | ||
this.setState({ | ||
showDetails: !this.state.showDetails | ||
}) | ||
} | ||
|
||
render() { | ||
const {connectDragSource, connectDropTarget} = this.props | ||
|
||
let cardDetails; | ||
let sideColor = { | ||
position: 'absolute', | ||
zIndex: -1, | ||
top: 0, | ||
bottom: 0, | ||
left: 0, | ||
width: 7, | ||
backgroundColor: this.props.color | ||
}; | ||
|
||
if(this.state.showDetails) { | ||
cardDetails = ( | ||
<div className="Card__details"> | ||
<span dangerouslySetInnerHTML={{__html:marked(this.props.description)}} /> | ||
<CheckList | ||
cardId={this.props.id} | ||
tasks={this.props.tasks} | ||
tasksCallbacks={this.props.tasksCallbacks} /> | ||
</div> | ||
) | ||
} | ||
return connectDropTarget(connectDragSource( | ||
<div className="Card"> | ||
<div style={sideColor} /> | ||
<div className={this.state.showDetails? "Card__title Card__title--is-open" : "Card__title"} onClick={this.toggleDetails.bind(this)}> | ||
{this.props.title} | ||
</div> | ||
<ReactCSSTransitionGroup | ||
transitionName="toggle" | ||
transitionEnterTimeout={250} | ||
transitionLeaveTimeout={250} | ||
> | ||
{cardDetails} | ||
</ReactCSSTransitionGroup> | ||
</div> | ||
)); | ||
} | ||
} | ||
|
||
|
||
Card.propTypes = { | ||
id: PropTypes.number, | ||
title: titlePropType, | ||
description: PropTypes.string, | ||
color: PropTypes.string, | ||
tasks: PropTypes.arrayOf(PropTypes.object), | ||
tasksCallbacks: PropTypes.object.isRequired, | ||
cardCallbacks: PropTypes.object.isRequired, | ||
connectDragSource: PropTypes.func.isRequired, | ||
connectDropTarget: PropTypes.func.isRequired | ||
} | ||
|
||
const dragHigherOrderCard = DragSource(constants.CARD, cardDragSpec, collectDrag)(Card) | ||
const dragDropHigherOrderCard = DropTarget(constants.CARD, cardDropSpec, collectDrop)(dragHigherOrderCard) | ||
|
||
export default dragDropHigherOrderCard |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import React, {Component, PropTypes} from 'react' | ||
|
||
export default class CheckList extends Component { | ||
checkInputKeyPress(evt) { | ||
if(evt.key === 'Enter') { | ||
this.props.tasksCallbacks.add(this.props.cardId, evt.target.value) | ||
evt.target.value = '' | ||
} | ||
} | ||
|
||
render() { | ||
console.log(this.props.tasksCallbacks); | ||
var tasks = this.props.tasks.map((task, taskindex) => { | ||
return <li key={task.id} className="CheckList__task"> | ||
<input type="checkbox" defaultChecked={task.done} onChange={this.props.tasksCallbacks.toggle.bind(null, this.props.cardId, task.id, taskindex)}/> | ||
{task.name} | ||
<a | ||
href="#" | ||
className="CheckList__task--remove" | ||
onClick={this.props.tasksCallbacks.delete.bind(null, this.props.cardId, task.id, taskindex)} | ||
> | ||
</a> | ||
</li> | ||
}); | ||
|
||
return ( | ||
<div className="CheckList"> | ||
<ul> {tasks} </ul> | ||
<input type="text" className="CheckList--add-task" placeholder="Type then hit Enter to add a task" onKeyPress={this.checkInputKeyPress.bind(this)} /> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
|
||
CheckList.propTypes = { | ||
tasks: PropTypes.arrayOf(PropTypes.object), | ||
cardId: PropTypes.number, | ||
tasksCallbacks: PropTypes.object.isRequired | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import React, {Component, PropTypes} from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import {DragDropContext} from 'react-dnd' | ||
import HTML5Backend from 'react-dnd-html5-backend' | ||
import List from './List'; | ||
|
||
// Recoinciliation, React phase before touch the DOM | ||
// React has a few steps to decide how to udpate the DOM | ||
// depending of the change that happened | ||
// -> React uses event delegation on the ROOT component | ||
// and attach the events and unbind them on umount phase | ||
// Controlled componentes: components that are based on the state | ||
// Uncontrolled componentes, the ones that can be mutated on DOM side | ||
// and they way to get them is via refs or onSubmit | ||
class KanbanBoard extends Component { | ||
render() { | ||
|
||
return ( | ||
<div className="App"> | ||
<List | ||
tasksCallbacks={this.props.tasksCallbacks} | ||
cardCallbacks={this.props.cardCallbacks} | ||
id="todo" | ||
title="To Do" | ||
cards={ this.props.cards.filter((card) => card.status === "todo")}> | ||
</List> | ||
<List | ||
tasksCallbacks={this.props.tasksCallbacks} | ||
cardCallbacks={this.props.cardCallbacks} | ||
id="in-progress" | ||
title="In Progress" | ||
cards={ this.props.cards.filter((card) => card.status === "in-progress")}> | ||
</List> | ||
<List | ||
tasksCallbacks={this.props.tasksCallbacks} | ||
cardCallbacks={this.props.cardCallbacks} | ||
id="done" | ||
title="Done" | ||
cards={ this.props.cards.filter((card) => card.status === "done")}> | ||
</List> | ||
</div> | ||
); | ||
} | ||
} | ||
KanbanBoard.propTypes = { | ||
cards: PropTypes.arrayOf(PropTypes.object), | ||
tasksCallbacks: PropTypes.object.isRequired, | ||
cardCallbacks: PropTypes.object.isRequired | ||
} | ||
|
||
export default DragDropContext(HTML5Backend)(KanbanBoard) |
Oops, something went wrong.