Skip to content
This repository was archived by the owner on Aug 9, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@

<div id="heading-container"></div>

<div id="notes-container"></div>

<a href="/pages/page1.html">Page #1</a>
<a href="/pages/page1.html">Page #2</a>

<!-- Required by Safari -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser-polyfill.min.js"></script>
<script type="text/javascript" src="http://localhost:5000/assets/build.js"></script>

</body>
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
"name": "webpack-react-workflow",
"version": "0.1.0",
"description": "A dummy project on how to integrate React and Webpack in a Web project",
"dependencies": {
"alt": "^0.17.3",
"node-uuid": "^1.4.3",
"react": "^0.13.3"
},
"devDependencies": {
"autoprefixer": "^5.2.0",
"babel-core": "^5.8.22",
Expand All @@ -22,7 +27,6 @@
"mocha": "^2.2.5",
"node-sass": "3.2.0",
"postcss-loader": "^0.6.0",
"react": "^0.13.3",
"react-hot-loader": "^1.2.8",
"sass-loader": "^2.0.0",
"style-loader": "^0.12.3",
Expand Down
8 changes: 8 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,13 @@ import Book from './book/book.js';
import Section from './book/section.jsx'; // eslint-disable-line no-unused-vars
React.render( <Section />, document.getElementById( 'heading-container' ) );

import PersistentBrowserStore from './core/PersistentBrowserStore';
import persist from './flux/libs/persist';

persist( PersistentBrowserStore );

import Todos from './todos/App.jsx'; // eslint-disable-line no-unused-vars
React.render( <Todos />, document.getElementById( 'notes-container' ) );

var book = new Book();
book.logSomething();
13 changes: 13 additions & 0 deletions src/core/PersistentBrowserStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default class {
static get( key ) {
try {
return JSON.parse( localStorage.getItem( key ) )
} catch( error ) {
return null
}
}

static set( key, value ) {
localStorage.setItem( key, JSON.stringify( value ) )
}
}
3 changes: 3 additions & 0 deletions src/flux/actions/NoteActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import alt from "../libs/alt"

export default alt.generateActions("create", "update", "delete")
5 changes: 5 additions & 0 deletions src/flux/libs/alt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Alt from "alt"

let alt = new Alt()

export default alt
15 changes: 15 additions & 0 deletions src/flux/libs/persist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import alt from './alt'
import makeFinalStore from 'alt/utils/makeFinalStore'

export default function( PersistentBrowserStore, storeName = 'app' ) {
let finalStore = makeFinalStore( alt )

alt.bootstrap( PersistentBrowserStore.get( storeName ) )

finalStore.listen( () => {
if ( PersistentBrowserStore.get( 'debug' ) ) {
return
}
PersistentBrowserStore.set( storeName, alt.takeSnapshot() )
})
}
4 changes: 4 additions & 0 deletions src/flux/stores/NoteStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import alt from "../libs/alt"
import NoteStoreModel from "./NoteStoreModel"

export default alt.createStore( NoteStoreModel, "NoteStore" )
54 changes: 54 additions & 0 deletions src/flux/stores/NoteStoreModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import uuid from "node-uuid"
import NoteActions from "../actions/NoteActions"

export default class {

constructor() {
this.bindActions( NoteActions )

this.notes = []
}

create( note ) {
let notes = this.notes

note.id = uuid.v4()

this.setState({
notes: notes.concat( note )
})
}

update( {id, task} ) {
let noteIndex = this.findNoteIndexById( id )
if ( noteIndex < 0 ) {
return
}

let notes = this.notes
notes[ noteIndex ].task = task
this.setState({ notes })
}

findNoteIndexById( id ) {
let notes = this.notes
let noteIndex = notes.findIndex( ( note ) => note.id === id )

if ( noteIndex < 0 ) {
console.warn( "Could not find note", id, notes )
}

return noteIndex
}

delete( id ) {
let noteIndex = this.findNoteIndexById( id )
if ( noteIndex < 0 ) {
return
}

let notes = this.notes
notes.splice( noteIndex, 1 )
this.setState({ notes })
}
}
53 changes: 53 additions & 0 deletions src/todos/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from "react"
import Button from "./Button.jsx"
import Notes from "./Notes.jsx"
import NoteActions from "../flux/actions/NoteActions.js"
import NoteStore from "../flux/stores/NoteStore.js"
import "./Todos.css"

export default class extends React.Component {
constructor(props) {
super(props)
this.bindInstanceMethods()

this.state = NoteStore.getState()
}

bindInstanceMethods() {
this.setState = this.setState.bind( this )
}

componentDidMount() {
NoteStore.listen( this.setState )
}

componentWillUnmount() {
NoteStore.unlisten( this.setState )
}

render() {
let notes = this.state.notes

return (
<div>
<Button onClick={this.addNote} text="+" className="note-add-button" />
<Notes
items={notes}
onEditCompleted={this.editNote}
onDeleteCompleted={this.deleteNote} />
</div>
)
}

addNote() {
NoteActions.create( {task: "New task"} )
}

editNote( id, task ) {
NoteActions.update( {id, task} )
}

deleteNote( id ) {
NoteActions.delete( id )
}
}
13 changes: 13 additions & 0 deletions src/todos/Button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react"

export default class extends React.Component {

render() {
let text = this.props.text || "OK",
className = this.props.className || null

return (<button onClick={this.props.onClick} className={className}>
{ text }
</button>)
}
}
15 changes: 15 additions & 0 deletions src/todos/EditableTextBox.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react"

export default class extends React.Component {

render() {
let autoFocus = this.props.autoFocus || true

return (<input
autoFocus={autoFocus}
defaultValue={this.props.defaultValue}
onBlur={this.props.onBlur}
onKeyPress={this.props.onKeyPress}
/>)
}
}
92 changes: 92 additions & 0 deletions src/todos/Note.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React from "react"
import Button from "./Button.jsx"
import EditableTextBox from "./EditableTextBox.jsx"

export default class extends React.Component {
constructor(props) {
super( props )
this.bindInstanceMethods()

this.state = {
editing: false
}
}

bindInstanceMethods() {
let methods = [
"startEditing",
"handleInputBlur",
"handleKeyPress",
"renderDelete",
"renderInput",
"renderTask"
]
for ( let method of methods ) {
this[ method ] = this[ method ].bind( this )
}
}

render() {
let editing = this.state.editing
let renderedElement
if ( editing ) {
renderedElement = this.renderInput()
} else {
renderedElement = this.renderTask()
}

return renderedElement
}

renderTask() {
let renderedDelete
if ( this.props.onDeleteCompleted ) {
renderedDelete = this.renderDelete()
}

return (<div onClick={this.startEditing}>
<span>{this.props.task}</span>
{renderedDelete}
</div>)
}

renderDelete() {
return (<Button
text="X"
onClick={this.props.onDeleteCompleted}
/>)
}

renderInput() {
return (<EditableTextBox
defaultValue={this.props.task}
onBlur={this.handleInputBlur}
onKeyPress={this.handleKeyPress}
/>)
}

startEditing() {
this.setState({
editing: true
})
}

stopEditing() {
this.setState({
editing: false
})
}

handleInputBlur( event ) {
this.props.onEditCompleted( event.target.value )
this.stopEditing()
}

handleKeyPress( event ) {
if( event.key !== "Enter" ) {
return
}
this.handleInputBlur( event )
}

}
38 changes: 38 additions & 0 deletions src/todos/Notes.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react"
import Note from "./Note.jsx"

export default class extends React.Component {
constructor(props) {
super( props )
this.bindInstanceMethods()
}

bindInstanceMethods() {
this.renderNote = this.renderNote.bind( this )
}

render() {
let notes = this.props.items,
className = this.props.className || "notes"

return (
<ul className={className}>
{notes.map( this.renderNote )}
</ul>
)
}

renderNote( note ) {
let onEditCompleted = this.props.onEditCompleted.bind( null, note.id )
let onDeleteCompleted = this.props.onDeleteCompleted.bind( null, note.id )
return (
<li key={`note${note.id}`}>
<Note
task={note.task}
onEditCompleted={onEditCompleted}
onDeleteCompleted={onDeleteCompleted}
/>
</li>
)
}
}
31 changes: 31 additions & 0 deletions src/todos/Todos.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.note-add-button {
padding: .5em 1em;
font-weight: bold;
color: green
}

.notes {
margin: 0.5em;
padding-left: 0;
max-width: 15em;
list-style: none
}

.notes li {
margin-bottom: 0.5em;
padding: 0.5em;
background-color: #fdfdfd;
box-shadow: 0 0 0.3em 0.03em rgba(0, 0, 0, 0.3)
}
.notes li:hover {
box-shadow: 0 0 0.3em 0.03em rgba(0, 0, 0, 0.7);
transition: 0.5s
}

.notes li button {
/* force to use inline-block so that it gets minimum height */
display: inline-block;
float: right;
padding: 1.0em;
color: red
}