Update counter example.
This commit is contained in:
Родитель
8df7602f6b
Коммит
90eccc044e
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"sourceMaps": "inline",
|
||||
"comments": false,
|
||||
"presets": [
|
||||
"es2015"
|
||||
],
|
||||
"plugins": [
|
||||
"syntax-flow",
|
||||
"transform-flow-strip-types",
|
||||
"remove-comments"
|
||||
]
|
||||
}
|
|
@ -1,16 +1,14 @@
|
|||
[ignore]
|
||||
.*/src/test/.*
|
||||
.*/dist/.*
|
||||
.*/node_modules/reflex/examples/.*
|
||||
.*/node_modules/reflex-virtual-dom-driver/lib/.*
|
||||
.*/node_modules/reflex/lib/.*
|
||||
.*/node_modules/babel.*
|
||||
.*/node_modules/tap/.*
|
||||
.*/node_modules/reflex-virtual-dom-driver/examlpes/.*
|
||||
|
||||
[libs]
|
||||
./node_modules/reflex/interfaces/
|
||||
./node_modules/reflex-virtual-dom-driver/interfaces/
|
||||
|
||||
[include]
|
||||
|
||||
[options]
|
||||
module.name_mapper='reflex-virtual-dom-driver' -> 'reflex-virtual-dom-driver/src/index'
|
||||
module.name_mapper='reflex' -> 'reflex/src/index'
|
||||
|
||||
suppress_comment= \\(.\\|\n\\)*\\@FlowIssue
|
||||
suppress_comment= \\(.\\|\n\\)*\\@FlowIgnore
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# Counter
|
||||
|
||||
|
||||
|
||||
### Demo
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
Navigate to [demo][]
|
||||
|
||||
|
||||
[demo]:http://localhost:6061/
|
|
@ -1,5 +1,3 @@
|
|||
'use strict';
|
||||
|
||||
import browserify from 'browserify';
|
||||
import gulp from 'gulp';
|
||||
import source from 'vinyl-source-stream';
|
||||
|
@ -16,120 +14,119 @@ import sequencial from 'gulp-sequence';
|
|||
import ecstatic from 'ecstatic';
|
||||
import hmr from 'browserify-hmr';
|
||||
import hotify from 'hotify';
|
||||
import manifest from './package.json';
|
||||
|
||||
var settings = {
|
||||
port: process.env.DEV_PORT || '6061',
|
||||
cache: {},
|
||||
plugin: [],
|
||||
transform: [
|
||||
babelify.configure({
|
||||
"optional": [
|
||||
"spec.protoToAssign",
|
||||
"runtime"
|
||||
],
|
||||
"blacklist": []
|
||||
})
|
||||
],
|
||||
debug: true,
|
||||
watch: false,
|
||||
compression: null
|
||||
};
|
||||
class Bundler {
|
||||
constructor(options) {
|
||||
this.options = options
|
||||
|
||||
var Bundler = function(entry) {
|
||||
this.entry = entry
|
||||
this.compression = settings.compression
|
||||
this.build = this.build.bind(this);
|
||||
this.build = this.build.bind(this);
|
||||
this.plugin = []
|
||||
this.transform = []
|
||||
this.cache = {}
|
||||
this.entry = path.join
|
||||
( options.input
|
||||
, options.main
|
||||
)
|
||||
|
||||
this.bundler = browserify({
|
||||
entries: ['./src/' + entry],
|
||||
debug: settings.debug,
|
||||
cache: {},
|
||||
transform: settings.transform,
|
||||
plugin: settings.plugin
|
||||
});
|
||||
|
||||
this.watcher = settings.watch &&
|
||||
watchify(this.bundler)
|
||||
.on('update', this.build);
|
||||
}
|
||||
Bundler.prototype.bundle = function() {
|
||||
gutil.log(`Begin bundling: '${this.entry}'`);
|
||||
return this.watcher ? this.watcher.bundle() : this.bundler.bundle();
|
||||
if (options.babel != null) {
|
||||
this.transform.push(babelify)
|
||||
}
|
||||
|
||||
if (options.hotreload != null) {
|
||||
this.transform.push(hotify)
|
||||
this.plugin.push(hmr)
|
||||
}
|
||||
|
||||
this.bundler = browserify
|
||||
( { entries: [this.entry]
|
||||
, debug:
|
||||
( options.sourceMaps != null
|
||||
? false
|
||||
: options.sourceMaps === false
|
||||
? false
|
||||
: true
|
||||
)
|
||||
, cache: this.cache
|
||||
, transform: this.transform
|
||||
, plugin: this.plugin
|
||||
}
|
||||
)
|
||||
|
||||
if (options.watch) {
|
||||
this.watcher =
|
||||
watchify(this.bundler)
|
||||
.on('update', this.build)
|
||||
}
|
||||
}
|
||||
bundle() {
|
||||
gutil.log(`Begin bundling: '${this.entry}'`);
|
||||
const output =
|
||||
( this.options.watch
|
||||
? this.watcher.bundle()
|
||||
: this.bundler.bundle()
|
||||
)
|
||||
return output
|
||||
}
|
||||
build() {
|
||||
const transforms =
|
||||
[ source(this.options.main)
|
||||
, buffer()
|
||||
, ( this.options.sourceMaps == null
|
||||
? null
|
||||
: sourcemaps.init({loadMaps: true})
|
||||
)
|
||||
, ( this.options.compression == null
|
||||
? null
|
||||
: uglify(this.options.compression)
|
||||
)
|
||||
, ( this.options.sourceMaps == null
|
||||
? null
|
||||
: sourcemaps.write(this.options.sourceMaps.output)
|
||||
)
|
||||
, gulp.dest(this.options.output)
|
||||
]
|
||||
|
||||
const output =
|
||||
transforms.reduce
|
||||
( (input, transform) =>
|
||||
( transform != null
|
||||
? input.pipe(transform)
|
||||
: input
|
||||
)
|
||||
, this
|
||||
.bundle()
|
||||
.on('error', (error) => {
|
||||
gutil.beep();
|
||||
console.error(`Failed to browserify: '${this.entry}'`, error.message)
|
||||
})
|
||||
)
|
||||
|
||||
return output.on('end', () => gutil.log(`Completed bundling: '${this.options.input}'`))
|
||||
}
|
||||
}
|
||||
|
||||
Bundler.prototype.build = function() {
|
||||
var bundle = this
|
||||
.bundle()
|
||||
.on('error', (error) => {
|
||||
gutil.beep();
|
||||
console.error(`Failed to browserify: '${this.entry}'`, error.message);
|
||||
})
|
||||
.pipe(source(this.entry + '.js'))
|
||||
.pipe(buffer())
|
||||
.pipe(sourcemaps.init({loadMaps: true}))
|
||||
.on('error', (error) => {
|
||||
gutil.beep();
|
||||
console.error(`Failed to make source maps for: '${this.entry}'`,
|
||||
error.message);
|
||||
});
|
||||
|
||||
return (this.compression ? bundle.pipe(uglify(this.compression)) : bundle)
|
||||
.on('error', (error) => {
|
||||
gutil.beep();
|
||||
console.error(`Failed to bundle: '${this.entry}'`,
|
||||
error.message);
|
||||
})
|
||||
.pipe(sourcemaps.write('./'))
|
||||
.pipe(gulp.dest('./dist/'))
|
||||
.on('end', () => {
|
||||
gutil.log(`Completed bundling: '${this.entry}'`);
|
||||
});
|
||||
}
|
||||
|
||||
var bundler = function(entry) {
|
||||
return gulp.task(entry, function() {
|
||||
return new Bundler(entry).build();
|
||||
});
|
||||
}
|
||||
|
||||
// Starts a static http server that serves browser.html directory.
|
||||
gulp.task('server', function() {
|
||||
var server = config => () => {
|
||||
var server = http.createServer(ecstatic({
|
||||
root: path.join(module.filename, '../'),
|
||||
cache: 0
|
||||
root: path.join(module.filename, config.root),
|
||||
cache: config.cache
|
||||
}));
|
||||
server.listen(settings.port);
|
||||
});
|
||||
server.listen(config.port);
|
||||
gutil.log(`Navigate to http://localhost:${config.port}/`)
|
||||
}
|
||||
var bundler = config => () => new Bundler(config).build()
|
||||
|
||||
gulp.task('compressor', function() {
|
||||
settings.compression = {
|
||||
mangle: true,
|
||||
compress: true,
|
||||
acorn: true
|
||||
};
|
||||
});
|
||||
|
||||
gulp.task('watcher', function() {
|
||||
settings.watch = true
|
||||
});
|
||||
Object.keys(manifest.builds).forEach(name => {
|
||||
const config = manifest.builds[name]
|
||||
|
||||
gulp.task('hotreload', function() {
|
||||
settings.plugin.push(hmr);
|
||||
settings.transform.push(hotify);
|
||||
});
|
||||
|
||||
bundler('index');
|
||||
|
||||
gulp.task('build', [
|
||||
'compressor',
|
||||
'index'
|
||||
]);
|
||||
|
||||
gulp.task('watch', [
|
||||
'watcher',
|
||||
'index'
|
||||
]);
|
||||
|
||||
gulp.task('develop', sequencial('watch', 'server'));
|
||||
gulp.task('live', ['hotreload', 'develop']);
|
||||
gulp.task('default', ['live']);
|
||||
if (config.server) {
|
||||
gulp.task(`serve ${name}`, server(config.server))
|
||||
gulp.task(`build ${name}`, bundler(config))
|
||||
gulp.task(name, sequencial(`build ${name}`, `serve ${name}`))
|
||||
}
|
||||
else {
|
||||
gulp.task(name, bundler(config))
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Sample App</title>
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<div id='root'>
|
||||
</div>
|
||||
<script src="./dist/index.js"></script>
|
||||
<main>
|
||||
</main>
|
||||
<script src="./dist/virtual-dom-driver.js"></script>
|
||||
<scrit src="./dist/reflex-driver.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,34 +1,58 @@
|
|||
{
|
||||
"name": "counter",
|
||||
"description": "Reflex counter example",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"test": "flow check",
|
||||
"start": "gulp live",
|
||||
"build": "NODE_ENV=production gulp build"
|
||||
"start": "gulp"
|
||||
},
|
||||
"dependencies": {
|
||||
"reflex": "latest",
|
||||
"reflex-virtual-dom-driver": "latest"
|
||||
"reflex": "0.2.0",
|
||||
"reflex-virtual-dom-driver": "0.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browserify": "11.0.1",
|
||||
"watchify": "3.3.1",
|
||||
|
||||
"babelify": "6.1.3",
|
||||
"browserify-hmr": "0.3.0",
|
||||
"hotify": "0.0.1",
|
||||
|
||||
"babel-core": "5.8.23",
|
||||
"babel-runtime": "5.8.20",
|
||||
"babel-core": "6.7.2",
|
||||
"babel-plugin-remove-comments": "2.0.0",
|
||||
"babel-plugin-syntax-flow": "6.3.13",
|
||||
"babel-plugin-transform-es2015-modules-umd": "6.4.3",
|
||||
"babel-plugin-transform-flow-strip-types": "6.4.0",
|
||||
"babel-preset-es2015": "6.3.13",
|
||||
"babelify": "7.2.0",
|
||||
"browserify": "13.0.0",
|
||||
"browserify-hmr": "0.3.1",
|
||||
"ecstatic": "0.8.0",
|
||||
"flow-bin": "0.17.0",
|
||||
|
||||
"gulp": "3.9.0",
|
||||
"gulp-sequence": "0.4.1",
|
||||
"gulp-sourcemaps": "1.5.2",
|
||||
"gulp-uglify": "^1.2.0",
|
||||
"gulp-util": "^3.0.6",
|
||||
"flow-bin": "0.22.1",
|
||||
"gulp": "3.9.1",
|
||||
"gulp-sequence": "0.4.5",
|
||||
"gulp-sourcemaps": "1.6.0",
|
||||
"gulp-uglify": "1.5.3",
|
||||
"gulp-util": "3.0.7",
|
||||
"hotify": "0.1.0",
|
||||
"vinyl-buffer": "1.0.0",
|
||||
"vinyl-source-stream": "1.1.0"
|
||||
"vinyl-source-stream": "1.1.0",
|
||||
"watchify": "3.7.0"
|
||||
},
|
||||
"builds": {
|
||||
"default": {
|
||||
"//compression": {
|
||||
"mangle": true,
|
||||
"compress": true,
|
||||
"acorn": true
|
||||
},
|
||||
"babel": true,
|
||||
"hotreload": true,
|
||||
"watch": true,
|
||||
"server": {
|
||||
"port": 6061,
|
||||
"cache": 0,
|
||||
"root": "../"
|
||||
},
|
||||
"sourceMaps": {
|
||||
"output": "./"
|
||||
},
|
||||
"main": "./virtual-dom-driver.js",
|
||||
"input": "./src/",
|
||||
"output": "./dist/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,36 @@
|
|||
/* @flow */
|
||||
import {html, forward} from "reflex";
|
||||
|
||||
import {html, forward} from "reflex"
|
||||
|
||||
/*::
|
||||
import * as type from "../type/counter"
|
||||
import type {Model, Action} from "./counter"
|
||||
import type {Address, VirtualTree} from "reflex"
|
||||
*/
|
||||
|
||||
export const asIncrement/*:type.asIncrement*/ = () =>
|
||||
({type: "Counter.Increment"})
|
||||
export const asDecrement/*:type.asDecrement*/ = () =>
|
||||
({type: "Counter.Decrement"})
|
||||
const Increment =
|
||||
{ type: "Increment"
|
||||
, create: () => Increment
|
||||
}
|
||||
|
||||
const Decrement =
|
||||
{ type: "Decrement"
|
||||
, create: () => Decrement
|
||||
}
|
||||
|
||||
export const create/*:type.create*/ = ({value}) =>
|
||||
({type: "Counter.Model", value})
|
||||
export const init =
|
||||
(value/*:number*/)/*:Model*/ =>
|
||||
({ value })
|
||||
|
||||
export const update/*:type.update*/ = (model, action) =>
|
||||
action.type === "Counter.Increment" ?
|
||||
{type:model.type, value: model.value + 1} :
|
||||
action.type === "Counter.Decrement" ?
|
||||
{type:model.type, value: model.value - 1} :
|
||||
model
|
||||
export const update =
|
||||
( model/*:Model*/
|
||||
, action/*:Action*/
|
||||
)/*:Model*/ =>
|
||||
( action.type === Increment.type
|
||||
? { value: model.value + 1 }
|
||||
: action.type === Decrement.type
|
||||
? { value: model.value - 1 }
|
||||
: model
|
||||
)
|
||||
|
||||
const counterStyle = {
|
||||
value: {
|
||||
|
@ -27,19 +38,31 @@ const counterStyle = {
|
|||
}
|
||||
}
|
||||
|
||||
// View
|
||||
export const view/*:type.view*/ = (model, address) =>
|
||||
html.span({key: "counter"}, [
|
||||
html.button({
|
||||
key: "decrement",
|
||||
onClick: forward(address, asDecrement)
|
||||
}, ["-"]),
|
||||
html.span({
|
||||
key: "value",
|
||||
style: counterStyle.value,
|
||||
}, [String(model.value)]),
|
||||
html.button({
|
||||
key: "increment",
|
||||
onClick: forward(address, asIncrement)
|
||||
}, ["+"])
|
||||
])
|
||||
|
||||
export const view =
|
||||
( model/*:Model*/
|
||||
, address/*:Address<Action>*/
|
||||
)/*:VirtualTree*/ =>
|
||||
html.span
|
||||
( { key: "counter"
|
||||
}
|
||||
, [ html.button
|
||||
( { key: "decrement"
|
||||
, onClick: forward(address, Decrement.create)
|
||||
}
|
||||
, ["-"]
|
||||
)
|
||||
, html.span
|
||||
( { key: "value"
|
||||
, style: counterStyle.value
|
||||
}
|
||||
, [ `${model.value}` ]
|
||||
)
|
||||
, html.button
|
||||
( { key: "increment"
|
||||
, onClick: forward(address, Increment.create)
|
||||
}
|
||||
, ["+"]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/* @flow */
|
||||
|
||||
import type {Address, VirtualTree} from "reflex"
|
||||
|
||||
type Tag <type> =
|
||||
{ type: type
|
||||
}
|
||||
|
||||
export type Model =
|
||||
{ value: number
|
||||
}
|
||||
|
||||
export type Action
|
||||
= Tag<"Increment">
|
||||
| Tag<"Decrement">
|
||||
|
||||
|
||||
declare export var Increment:Tag<"Increment">
|
||||
declare export var Decrement:Tag<"Decrement">
|
||||
|
||||
declare export function init (value:number):
|
||||
Model
|
||||
|
||||
declare export function update (model:Model, action:Action):
|
||||
Model
|
||||
|
||||
declare export function view (model:Model, address:Address<Action>):
|
||||
VirtualTree
|
|
@ -1,18 +0,0 @@
|
|||
/* @flow */
|
||||
|
||||
import * as Counter from "./counter"
|
||||
import {start} from "reflex"
|
||||
import {Renderer} from "reflex-virtual-dom-driver"
|
||||
|
||||
const app = start({
|
||||
initial: Counter.create(window.app != null ?
|
||||
window.app.model.value :
|
||||
{value: 0}),
|
||||
update: Counter.update,
|
||||
view: Counter.view
|
||||
});
|
||||
window.app = app
|
||||
|
||||
const renderer = new Renderer({target: document.body})
|
||||
|
||||
app.view.subscribe(renderer.address)
|
|
@ -0,0 +1,18 @@
|
|||
/* @flow */
|
||||
|
||||
import {init, update, view} from "./counter"
|
||||
import {start, beginner} from "reflex"
|
||||
|
||||
export const app = start
|
||||
( beginner
|
||||
( { model:
|
||||
( window.app == null
|
||||
? init(0)
|
||||
: window.app.model.value
|
||||
)
|
||||
, update: update
|
||||
, view: view
|
||||
}
|
||||
)
|
||||
)
|
||||
window.app = app
|
|
@ -0,0 +1,13 @@
|
|||
/* @flow */
|
||||
|
||||
import {app} from "./main"
|
||||
import {Renderer} from "reflex-virtual-dom-driver"
|
||||
|
||||
const renderer = new Renderer
|
||||
( { target: document.body
|
||||
}
|
||||
)
|
||||
|
||||
app.view.subscribe
|
||||
( renderer.address
|
||||
)
|
|
@ -1,15 +0,0 @@
|
|||
/* @flow */
|
||||
|
||||
import type {Address, VirtualNode} from "reflex/type"
|
||||
|
||||
export type Model = {type: "Counter.Model", value:number}
|
||||
export type Increment = {type: "Counter.Increment"}
|
||||
export type Decrement = {type: "Counter.Decrement"}
|
||||
export type Action = Increment|Decrement
|
||||
|
||||
export type asIncrement = () => Increment
|
||||
export type asDecrement = () => Decrement
|
||||
|
||||
export type create = (options:{value:number}) => Model
|
||||
export type update = (model:Model, action:Action) => Model
|
||||
export type view = (model:Model, address:Address<Action>) => VirtualNode
|
Загрузка…
Ссылка в новой задаче