├── .babelrc
├── .gitignore
├── README.md
├── devServer.js
├── index.html
├── package.json
├── src
├── components
│ ├── counter.js
│ ├── list-counters-fancy.js
│ ├── list-counters.js
│ └── two-counters.js
├── index.js
└── start.js
├── webpack.config.dev.js
└── webpack.config.prod.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "stage": 0
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Elmish React attempt with Mini-signals
2 | =====================
3 |
4 | This is my personal attempt with [Elm Architecture](https://github.com/evancz/elm-architecture-tutorial/)
5 |
6 | Inspired in Dan Abramov's [attempt](https://github.com/gaearon/react-elmish-example)
7 |
8 | ## Running
9 |
10 | It's up on [Github Pages](http://paparga.github.io/elmish-react).
11 |
12 | You can also
13 |
14 | ```
15 | git clone https://github.com/paparga/elmish-react.git
16 | cd elmish-react
17 | npm install
18 | npm start
19 | open http://localhost:3000
20 | ```
21 |
22 | ## License
23 |
24 | MIT
25 |
--------------------------------------------------------------------------------
/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 |
15 | app.get('*', function(req, res) {
16 | res.sendFile(path.join(__dirname, 'index.html'));
17 | });
18 |
19 | app.listen(3000, 'localhost', function(err) {
20 | if (err) {
21 | console.log(err);
22 | return;
23 | }
24 |
25 | console.log('Listening at http://localhost:3000');
26 | });
27 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Elmish-React
5 |
6 |
7 | One Counter
8 |
9 | Two Counters
10 |
11 | List of counters
12 |
13 | List of counters fancy
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "elmish-react",
3 | "version": "0.0.5",
4 | "description": "My elmish React attempt with mini-signals",
5 | "main": "dist/bundle.js",
6 | "dependencies": {
7 | "mini-signals": "^1.1.0",
8 | "react": "^0.14.2",
9 | "react-dom": "^0.14.2"
10 | },
11 | "devDependencies": {
12 | "babel-core": "^5.4.7",
13 | "babel-loader": "^5.1.2",
14 | "express": "^4.13.3",
15 | "rimraf": "^2.4.3",
16 | "webpack": "^1.9.6",
17 | "webpack-dev-middleware": "^1.2.0"
18 | },
19 | "keywords": [
20 | "react",
21 | "elm",
22 | "architecture",
23 | "example"
24 | ],
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/paparga/elmish-react.git"
28 | },
29 | "bugs": {
30 | "url": "https://github.com/paparga/elmish-react/issues"
31 | },
32 | "scripts": {
33 | "clean": "rimraf dist",
34 | "build:webpack": "NODE_ENV=production webpack --config webpack.config.prod.js",
35 | "build": "npm run clean && npm run build:webpack",
36 | "publish": "npm run build && mkdir -p dist && cp index.html dist && cd dist && git init && git commit --allow-empty -m 'update site' && git checkout -b gh-pages && touch .nojekyll && git add . && git commit -am 'update site' && git push git@github.com:paparga/elmish-react gh-pages --force",
37 | "start": "node devServer.js"
38 |
39 | },
40 | "author": "Pablo Parga",
41 | "license": "MIT"
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/counter.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const INCREMENT = 'INCREMENT'
4 | const DECREMENT = 'DECREMENT'
5 |
6 | export function update(actions, model){
7 | let action = actions.shift()
8 | switch (action) {
9 | case INCREMENT:
10 | return model + 1
11 | case DECREMENT:
12 | return model - 1
13 | default:
14 | return model
15 | }
16 | }
17 |
18 | export function view({dispatch, model}){
19 |
20 | const style = { fontSize: '20px',
21 | fontFamily: 'monospace',
22 | display: 'inline-block',
23 | width: '50px',
24 | textAlign: 'center'
25 | }
26 |
27 | return (
28 |
29 |
30 |
{model}
31 |
32 |
33 | )
34 | }
35 |
36 | export function viewWithRemove({context, model}){
37 |
38 | const style = { fontSize: '20px',
39 | fontFamily: 'monospace',
40 | display: 'inline-block',
41 | width: '50px',
42 | textAlign: 'center'
43 | }
44 |
45 | return (
46 |
47 |
48 |
{model}
49 |
50 |
51 |
52 |
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/list-counters-fancy.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import * as Counter from './counter'
3 | import signals from 'mini-signals'
4 |
5 | const INSERT = 'INSERT'
6 | const REMOVE = 'REMOVE'
7 | const MODIFY = 'MODIFY'
8 |
9 | const forward = (dispatch, actions) => (child) => {
10 | dispatch(actions.concat(child))
11 | }
12 |
13 | export function update(actions, model){
14 | let action = actions.shift()
15 | if (action == undefined) {
16 | return model
17 | }
18 | switch (action.type || action) {
19 | case INSERT:
20 | let counter = {id: model.nextId, model: 0}
21 | let nextId = model.nextId + 1
22 | model.counters.push(counter)
23 | return {counters: model.counters, nextId }
24 |
25 | case REMOVE:
26 | for (var i = 0; i < model.counters.length; i++) {
27 | if (action.id == model.counters[i].id) {
28 | model.counters.splice(i, 1);
29 | }
30 | }
31 | return model
32 |
33 | case MODIFY:
34 | for (var i = 0; i < model.counters.length; i++) {
35 | if (action.id == model.counters[i].id) {
36 | let counterModel = model.counters[i].model
37 | model.counters[i].model = Counter.update(actions, counterModel)
38 | }
39 | }
40 | return model
41 |
42 | default:
43 | return model
44 | }
45 | }
46 |
47 | export function view({dispatch, model}){
48 | let counterList = model.counters.map((counter) => {
49 |
50 | let context = {
51 | actions: forward(dispatch,[{type: MODIFY, id: counter.id}]),
52 | remove: forward(dispatch,[{type: REMOVE, id: counter.id}])}
53 |
54 | return
55 |
56 |
57 | })
58 | return (
59 |
60 | {counterList}
61 |
62 |
63 | )
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/list-counters.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import * as Counter from './counter'
3 | import signals from 'mini-signals'
4 |
5 | const INSERT = 'INSERT'
6 | const REMOVE = 'REMOVE'
7 | const MODIFY = 'MODIFY'
8 |
9 | const forward = (dispatch, actions) => (child) => {
10 | dispatch(actions.concat(child))
11 | }
12 |
13 | export function update(actions, model){
14 | let action = actions.shift()
15 | if (action == undefined) {
16 | return model
17 | }
18 | switch (action.type || action) {
19 | case INSERT:
20 | let counter = {id: model.nextId, model: 0}
21 | let nextId = model.nextId + 1
22 | model.counters.push(counter)
23 | return {counters: model.counters, nextId }
24 |
25 | case REMOVE:
26 | model.counters.pop()
27 | return model
28 |
29 | case MODIFY:
30 | for (var i = 0; i < model.counters.length; i++) {
31 | if (action.id == model.counters[i].id) {
32 | let counterModel = model.counters[i].model
33 | model.counters[i].model = Counter.update(actions, counterModel)
34 | }
35 | }
36 | return model
37 |
38 | default:
39 | return model
40 | }
41 | }
42 |
43 | export function view({dispatch, model}){
44 | let counterList = model.counters.map((counter) => {
45 | return
46 |
47 |
48 | })
49 | return (
50 |
51 | {counterList}
52 |
53 |
54 |
55 | )
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/two-counters.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import * as Counter from './counter'
3 | import signals from 'mini-signals'
4 |
5 | const RESET = 'RESET'
6 | const TOP = 'TOP'
7 | const BOTTOM = 'BOTTOM'
8 |
9 | const forward = (dispatch, actions) => (child) => {
10 | dispatch(actions.concat(child))
11 | }
12 |
13 | export function update(actions, model){
14 |
15 | let action = actions.shift()
16 |
17 | switch (action) {
18 | case RESET:
19 | return {topCounter: 0, bottomCounter: 0}
20 | case TOP:
21 | return {topCounter: Counter.update(actions, model.topCounter),
22 | bottomCounter: model.bottomCounter}
23 | case BOTTOM:
24 | return {topCounter: model.topCounter,
25 | bottomCounter: Counter.update(actions, model.bottomCounter)}
26 | default:
27 | return model
28 | }
29 | }
30 |
31 | export function view({dispatch, model}){
32 | return (
33 |
34 |
35 |
36 |
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import * as Counter from './components/counter'
2 | import * as TwoCounters from './components/two-counters'
3 | import * as ListCounters from './components/list-counters'
4 | import * as ListCountersFancy from './components/list-counters-fancy'
5 | import start from './start'
6 |
7 | const oneCounterModel = 0
8 | const listCounterModel = {counters: [], nextId: 1}
9 | const listCounterFancyModel = {counters: [], nextId: 10000000}
10 | const twoCountersModel = {topCounter: 0, bottomCounter: 0}
11 |
12 | start(oneCounterModel, Counter, 'one-counter')
13 |
14 | start(twoCountersModel, TwoCounters, 'two-counters')
15 |
16 | start(listCounterModel, ListCounters, 'list-counters')
17 |
18 | start(listCounterFancyModel, ListCountersFancy, 'list-counters-fancy')
19 |
--------------------------------------------------------------------------------
/src/start.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import signals from 'mini-signals'
4 |
5 | export default function start(model, Component, nodeId) {
6 |
7 | const signal = new signals()
8 |
9 | const node = document.getElementById(nodeId)
10 |
11 | function redraw(actions){
12 | model = Component.update(actions,model)
13 | render(
14 | ,
15 | node
16 | )
17 | }
18 |
19 | signal.add(redraw)
20 | signal.dispatch([])
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 |
2 | var path = require('path');
3 | var webpack = require('webpack');
4 |
5 | module.exports = {
6 | devtool: 'eval',
7 | entry: [
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/'
14 | },
15 | plugins: [
16 | new webpack.NoErrorsPlugin()
17 | ],
18 | module: {
19 | loaders: [{
20 | test: /\.js$/,
21 | loaders: ['babel'],
22 | include: path.join(__dirname, 'src')
23 | }]
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 |
2 | var path = require('path');
3 | var webpack = require('webpack');
4 |
5 | module.exports = {
6 | devtool: 'source-map',
7 | entry: [
8 | './src/index'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | 'NODE_ENV': JSON.stringify('production')
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | module: {
29 | loaders: [{
30 | test: /\.js$/,
31 | loaders: ['babel'],
32 | include: path.join(__dirname, 'src')
33 | }]
34 | }
35 | };
36 |
--------------------------------------------------------------------------------