diff --git a/assets/screenshots/canvas-dial.png b/assets/screenshots/canvas-dial.png new file mode 100644 index 0000000000..b0503c2ac9 Binary files /dev/null and b/assets/screenshots/canvas-dial.png differ diff --git a/assets/screenshots/commit-table-ssr.png b/assets/screenshots/commit-table-ssr.png new file mode 100644 index 0000000000..17bb736c5a Binary files /dev/null and b/assets/screenshots/commit-table-ssr.png differ diff --git a/assets/crypto-chart.png b/assets/screenshots/crypto-chart.png similarity index 100% rename from assets/crypto-chart.png rename to assets/screenshots/crypto-chart.png diff --git a/assets/screenshots/estuary.jpg b/assets/screenshots/estuary.jpg new file mode 100644 index 0000000000..63f6d12c97 Binary files /dev/null and b/assets/screenshots/estuary.jpg differ diff --git a/assets/iges.png b/assets/screenshots/iges.png similarity index 100% rename from assets/iges.png rename to assets/screenshots/iges.png diff --git a/assets/screenshots/rstream-grid.png b/assets/screenshots/rstream-grid.png new file mode 100644 index 0000000000..07ec344a90 Binary files /dev/null and b/assets/screenshots/rstream-grid.png differ diff --git a/assets/screenshots/svg-waveform.png b/assets/screenshots/svg-waveform.png new file mode 100644 index 0000000000..29de537048 Binary files /dev/null and b/assets/screenshots/svg-waveform.png differ diff --git a/examples/README.md b/examples/README.md index cf3c9dcfbc..e18bb82346 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,27 +4,30 @@ This directory contains a growing number of standalone example projects, includi If you want to [contribute](../CONTRIBUTING.md) an example, please get in touch via PR, issue tracker, email or twitter! -| # | Name | Description | Packages of interest | Difficulty | -|----|----------------------------------------------|-----------------------------------------------------|-------------------------------------------------------------------|--------------| -| 1 | [async-effect](./async-effect) | Async side effect handling (JSON I/O) | atom, hdom, interceptors | intermediate | -| 2 | [cellular-automata](./cellular-automata) | Transducer based, customizable 2D cellular automata | hdom, hdom-components, transducers | basic | -| 3 | [crypto-chart](./crypto-chart) | Interactive rstream & transducer based SVG chart | hdom, hiccup-svg, rstream, transducers | advanced | -| 4 | [dashboard](./cellular-automata) | Barebones components w/ local state | hdom, transducers | basic | -| 5 | [devcards](./devcards) | Multiple app instances with/without shared state | atom, hdom | intermediate | -| 6 | [hdom-basics](./hdom-basics) | Hello world | hdom, hiccup | basic | -| 7 | [hdom-benchmark](./hdom-benchmark) | hdom rendering perf / stress test, FPS counter | hdom, rstream, transducers | intermediate | -| 8 | [hdom-theme-adr-0003](./hdom-theme-adr-0003) | hdom themed components proposal | hdom | intermediate | -| 9 | [interceptor-basics](./hdom-benchmark) | Event handling w/ interceptors and side effects | atom, hdom, interceptors | intermediate | -| 10 | [json-components](./json-components) | JSON->component transformation, live editor | hdom, transducers | intermediate | -| 11 | [login-form](./login-form) | Basic SPA without router | atom, hdom | intermediate | -| 12 | [pointfree-svg](./pointfree-svg) | Generate SVG using pointfree DSL | hiccup, hiccup-svg, pointfree-lang | intermediate | -| 13 | [router-basics](./router-basics) | Complete mini SPA | atom, hdom, interceptors, router | advanced | -| 14 | [rstream-dataflow](./rstream-dataflow) | Dataflow graph | atom, hdom, rstream, rstream-gestures, rstream-graph, transducers | intermediate | -| 15 | [rstream-grid](./rstream-grid) | Dataflow graph SVG grid | atom, hdom, hiccup-svg, interceptors, rstream-graph, transducers | advanced | -| 16 | [rstream-hdom](./rstream-hdom) | rstream based UI updates & state handling | hdom, rstream, transducers | intermediate | -| 17 | [svg-particles](./svg-particles) | hdom SVG generation / animation | hdom, transducers | basic | -| 18 | [svg-waveform](./svg-waveform) | hdom SVG generation / undo history | atom, hdom, hiccup-svg, interceptors, iterators | intermediate | -| 19 | [todo-list](./todo-list) | Canonical Todo list with undo/redo | atom, hdom, transducers | intermediate | -| 20 | [transducers-hdom](./transducers-hdom) | Transducer & rstream based hdom UI updates | hdom, rstream, transducers-hdom | basic | -| 21 | [triple-query](./triple-query) | Triple store query results & sortable table | atom, hdom, hdom-components, rstream-query, transducers | intermediate | -| 22 | [webgl](./webgl) | Canvas component handling | hdom, hdom-components | basic | +| # | Name | Description | Packages of interest | Difficulty | +|----|----------------------------------------------|----------------------------------------------------------|-------------------------------------------------------------------|--------------| +| 1 | [async-effect](./async-effect) | Async side effect handling (JSON I/O) | atom, hdom, interceptors | intermediate | +| 2 | [canvas-dial](./canvas-dial) | Canvas dial component w/ mouse & touch events | hdom, rstream, rstream-gestures, transducers, transducers-hdom | intermediate | +| 3 | [cellular-automata](./cellular-automata) | Transducer based, customizable 2D cellular automata | hdom, hdom-components, transducers | basic | +| 4 | [commit-table-ssr](./commit-table-ssr) | Server-side & static file rendering of hiccup components | hiccup, transducers | intermediate | +| 5 | [crypto-chart](./crypto-chart) | Interactive rstream & transducer based SVG chart | hdom, hiccup-svg, rstream, transducers | advanced | +| 6 | [dashboard](./dashboard) | Barebones components w/ local state | hdom, transducers | basic | +| 7 | [devcards](./devcards) | Multiple app instances with/without shared state | atom, hdom | intermediate | +| 8 | [hdom-basics](./hdom-basics) | Hello world | hdom, hiccup | basic | +| 9 | [hdom-benchmark](./hdom-benchmark) | hdom rendering perf / stress test, FPS counter | hdom, rstream, transducers | intermediate | +| 10 | [hdom-theme-adr-0003](./hdom-theme-adr-0003) | hdom themed components proposal | hdom | intermediate | +| 11 | [hydrate-basics](./hydrate-basics) | hiccup / hdom DOM hydration | hiccup, hdom | intermediate | +| 12 | [interceptor-basics](./interceptor-basics) | Event handling w/ interceptors and side effects | atom, hdom, interceptors | intermediate | +| 13 | [json-components](./json-components) | JSON->component transformation, live editor | hdom, transducers | intermediate | +| 14 | [login-form](./login-form) | Basic SPA without router | atom, hdom | intermediate | +| 15 | [pointfree-svg](./pointfree-svg) | Generate SVG using pointfree DSL | hiccup, hiccup-svg, pointfree-lang | intermediate | +| 16 | [router-basics](./router-basics) | Complete mini SPA | atom, hdom, interceptors, router | advanced | +| 17 | [rstream-dataflow](./rstream-dataflow) | Dataflow graph | atom, hdom, rstream, rstream-gestures, rstream-graph, transducers | intermediate | +| 18 | [rstream-grid](./rstream-grid) | Dataflow graph SVG grid | atom, hdom, hiccup-svg, interceptors, rstream-graph, transducers | advanced | +| 19 | [rstream-hdom](./rstream-hdom) | rstream based UI updates & state handling | hdom, rstream, transducers | intermediate | +| 20 | [svg-particles](./svg-particles) | hdom SVG generation / animation | hdom, transducers | basic | +| 21 | [svg-waveform](./svg-waveform) | hdom SVG generation / undo history | atom, hdom, hiccup-svg, interceptors, iterators | intermediate | +| 22 | [todo-list](./todo-list) | Canonical Todo list with undo/redo | atom, hdom, transducers | intermediate | +| 23 | [transducers-hdom](./transducers-hdom) | Transducer & rstream based hdom UI updates | hdom, rstream, transducers-hdom | basic | +| 24 | [triple-query](./triple-query) | Triple store query results & sortable table | atom, hdom, hdom-components, rstream-query, transducers | intermediate | +| 25 | [webgl](./webgl) | Canvas component handling | hdom, hdom-components | basic | \ No newline at end of file diff --git a/examples/async-effect/src/index.ts b/examples/async-effect/src/index.ts index f7f1af5a9f..f60a8c5da1 100644 --- a/examples/async-effect/src/index.ts +++ b/examples/async-effect/src/index.ts @@ -88,4 +88,4 @@ const app = () => { } }; -start("app", app()); +start(app()); diff --git a/examples/canvas-dial/.gitignore b/examples/canvas-dial/.gitignore new file mode 100644 index 0000000000..9c418ce79f --- /dev/null +++ b/examples/canvas-dial/.gitignore @@ -0,0 +1,3 @@ +node_modules +yarn.lock +*.js diff --git a/examples/canvas-dial/README.md b/examples/canvas-dial/README.md new file mode 100644 index 0000000000..d74977360d --- /dev/null +++ b/examples/canvas-dial/README.md @@ -0,0 +1,21 @@ +# canvas-dial + +[Live demo](http://demo.thi.ng/umbrella/canvas-dial/) + +Customizable canvas-based radial dial component with mouse & touch +support. + +```bash +git clone https://github.com/thi-ng/umbrella.git +cd umbrella/examples/canvas-dial +yarn install +yarn start +``` + +## Authors + +- Karsten Schmidt + +## License + +© 2018 Karsten Schmidt // Apache Software License 2.0 diff --git a/examples/canvas-dial/index.html b/examples/canvas-dial/index.html new file mode 100644 index 0000000000..62ff6957ad --- /dev/null +++ b/examples/canvas-dial/index.html @@ -0,0 +1,19 @@ + + + + + + + + canvas-dial + + + + + +
+ + + + \ No newline at end of file diff --git a/examples/canvas-dial/package.json b/examples/canvas-dial/package.json new file mode 100644 index 0000000000..de2be1f868 --- /dev/null +++ b/examples/canvas-dial/package.json @@ -0,0 +1,28 @@ +{ + "name": "canvas-dial", + "version": "0.0.1", + "repository": "https://github.com/thi-ng/umbrella", + "author": "Karsten Schmidt ", + "license": "Apache-2.0", + "scripts": { + "build": "webpack --mode production --display-reasons --display-modules", + "start": "webpack-dev-server --open --mode development --devtool inline-source-map" + }, + "devDependencies": { + "ts-loader": "^4.4.2", + "typescript": "^3.0.1", + "webpack": "^4.16.3", + "webpack-cli": "^3.1.0", + "webpack-dev-server": "^3.1.5" + }, + "dependencies": { + "@thi.ng/api": "latest", + "@thi.ng/checks": "latest", + "@thi.ng/hdom": "latest", + "@thi.ng/hdom-components": "latest", + "@thi.ng/rstream": "latest", + "@thi.ng/rstream-gestures": "latest", + "@thi.ng/strings": "latest", + "@thi.ng/transducers": "latest" + } +} \ No newline at end of file diff --git a/examples/canvas-dial/src/dial.ts b/examples/canvas-dial/src/dial.ts new file mode 100644 index 0000000000..139f8e4d8d --- /dev/null +++ b/examples/canvas-dial/src/dial.ts @@ -0,0 +1,209 @@ +import { Fn } from "@thi.ng/api/api"; +import { isString } from "@thi.ng/checks/is-string"; +import { canvas2D } from "@thi.ng/hdom-components/canvas"; +import { GestureEvent, gestureStream, GestureType } from "@thi.ng/rstream-gestures"; +import { Subscription } from "@thi.ng/rstream/subscription"; +import { peek } from "@thi.ng/transducers/func/peek"; +import { fitClamped1 } from "@thi.ng/vectors/math"; +import { heading2, sub2 } from "@thi.ng/vectors/vec2"; + +/** + * Dial component options. + */ +export interface DialOpts { + /** + * Dial center X (normalized) + * Default: 0.5 + */ + cx: number; + /** + * Dial center Y (normalized) + * Default: 0.5 + */ + cy: number; + /** + * Inner radius (normalized) + * Default: 0.5 + */ + r1: number; + /** + * Outer radius (normalized) + * Default: 0.99 + */ + r2: number; + /** + * Dial min value + * Default: 0 + */ + min: number; + /** + * Dial min value + * Default: 1 + */ + max: number; + /** + * Orientation / start angle (in radians) + * Default: PI/2 + */ + base: number; + /** + * Angular gap between min / max values + * Default: PI/10 + */ + gap: number; + /** + * Fill color (or gradient) for value area + * Default: black + */ + color: string | GradientDef; + /** + * Fill color (or gradient) for background ring + * Default: rgba(0,0,0,0.1) + */ + bgColor: string | GradientDef; + /** + * Label formatter. No label will be displayed, if missing. + */ + label: (x: number) => string; + /** + * Label Y offset from `cy` + * Default: 0 + */ + labelYOffset: number; + /** + * Default: black + */ + labelColor: string; + /** + * Label font CSS string + * Default (10px sans-serif) + */ + font: string; + /** + * Event callback (receives new dial value) + */ + onchange: Fn; +} + +/** + * Multi-stop linear gradient definition. + */ +export interface GradientDef { + /** + * Start point (normalized) + */ + from: number[]; + /** + * End point (normalized) + */ + to: number[]; + /** + * Color stops (position normalized) + */ + stops: [number, string][]; +} + +const PI = Math.PI; +const TAU = 2 * PI; + +/** + * HOF component. Returns pre-configured dial component. + * + * @param opts + */ +export const dial = (opts: Partial) => { + opts = { + cx: 0.5, + cy: 0.5, + r1: 0.5, + r2: 0.99, + min: 0, + max: 1, + base: PI * 0.5, + gap: PI * 0.1, + color: "black", + bgColor: "rgba(0,0,0,0.1)", + labelColor: "black", + labelYOffset: 0, + font: "10px sans-serif", + ...opts + }; + let events: Subscription; + let cx, cy; + const startTheta = opts.base + opts.gap / 2; + + const drawRing = (ctx: CanvasRenderingContext2D, amount: number, col: any) => { + const endTheta = startTheta + (TAU - opts.gap) * amount; + ctx.fillStyle = col; + ctx.beginPath(); + ctx.arc(cx, cy, opts.r2, startTheta, endTheta, false); + ctx.arc(cx, cy, opts.r1, endTheta, startTheta, true); + ctx.fill(); + }; + + const makeGradient = (el: HTMLCanvasElement, ctx: CanvasRenderingContext2D, def: GradientDef) => { + const g = ctx.createLinearGradient( + def.from[0] * el.width, def.from[1] * el.height, + def.to[0] * el.width, def.to[1] * el.height + ); + def.stops.forEach(([pos, col]) => g.addColorStop(pos, col)); + return g; + }; + + return canvas2D({ + + init: (el, ctx) => { + cx = el.width * opts.cx; + cy = el.height * opts.cy; + ctx.strokeStyle = "none"; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.font = opts.font; + const scale = Math.min(cx, cy); + opts.r1 *= scale; + opts.r2 *= scale; + if (!isString(opts.bgColor)) { + opts.bgColor = makeGradient(el, ctx, opts.bgColor); + } + if (!isString(opts.color)) { + opts.color = makeGradient(el, ctx, opts.color); + } + if (opts.onchange) { + // add interaction event stream (mouse & touch) + // configure stream to return scaled coords (devicePixelRatio) + events = gestureStream(el, { scale: true }) + .subscribe({ + next: (e) => { + if (e[0] === GestureType.START || e[0] === GestureType.DRAG) { + let theta = heading2(sub2(e[1].pos, [cx, cy])) - startTheta; + if (theta < 0) theta += TAU; + theta %= TAU; + opts.onchange.call( + null, + fitClamped1(Math.min(theta / (TAU - opts.gap)), 0, 1, opts.min, opts.max) + ); + } + } + }); + } + }, + + // clean up gesture event stream when component is released + release: () => { + events && events.unsubscribe(); + }, + + // there're a few args we're not interested in here, so we use var args instead. + // the dial value is the last arg + update: (el, ctx, ...args: any[]) => { + const val = peek(args); + ctx.clearRect(0, 0, el.width, el.height); + drawRing(ctx, 1, opts.bgColor); + drawRing(ctx, fitClamped1(val, opts.min, opts.max, 0.005, 1), opts.color); + if (opts.label) { + ctx.fillStyle = opts.labelColor; + ctx.fillText(opts.label(val), cx, cy + opts.labelYOffset); + } + } + }); +}; diff --git a/examples/canvas-dial/src/index.ts b/examples/canvas-dial/src/index.ts new file mode 100644 index 0000000000..b3fe03cc38 --- /dev/null +++ b/examples/canvas-dial/src/index.ts @@ -0,0 +1,76 @@ +import { stream } from "@thi.ng/rstream/stream"; +import { sync } from "@thi.ng/rstream/stream-sync"; +import { percent } from "@thi.ng/strings/percent"; +import { updateDOM } from "@thi.ng/transducers-hdom"; +import { comp } from "@thi.ng/transducers/func/comp"; +import { map } from "@thi.ng/transducers/xform/map"; + +import { dial } from "./dial"; + +// hdom context & app state object +export const ctx = { + // streams to hold dial values + streams: { + a: stream(), + b: stream(), + c: stream(), + }, + // component styling + ui: { + root: { class: "vh-100 flex justify-center items-center" }, + dial: { width: 100, height: 100, class: "pointer ma1" }, + } +}; + +/** + * This HOF (higher-order function) returns a mapping function which + * receives a tuple of object of all current stream values and returns a + * hdom root component. It will be used by the `sync()` construct + * further below. + * + * The dial components used here are HOFs themselves and therefore must + * be pre-initialized before use. + */ +const app = () => { + const dialA = dial({ + r1: 0.5, + color: { from: [0, 0], to: [1, 1], stops: [[0, "#075"], [1, "#6f9"]] }, + font: "20px Menlo", + label: (x) => percent(0)(x), + onchange: (x) => ctx.streams.a.next(x) + }); + const dialB = dial({ + r1: 0.66, + base: -Math.PI / 2, + gap: Math.PI / 2, + color: { from: [0, 0], to: [1, 0.75], stops: [[0, "#00f"], [0.5, "#f60"], [1, "#ff0"]] }, + font: "20px Menlo", + label: (x) => percent(1)(x), + onchange: (x) => ctx.streams.b.next(x) + }); + const dialC = dial({ + r1: 0.75, + gap: Math.PI, + color: { from: [0, 0], to: [1, 0], stops: [[0, "#407"], [1, "#09f"]] }, + font: "20px Menlo", + label: (x) => percent(2)(x), + onchange: (x) => ctx.streams.c.next(x) + }); + return ({ a, b, c }) => + ["div", ctx.ui.root, + [dialA, ctx.ui.dial, a], + [dialB, ctx.ui.dial, b], + [dialC, ctx.ui.dial, c] + ]; +}; + +// stream combinator & reactive DOM update +sync({ + src: ctx.streams, + xform: comp(map(app()), updateDOM()) +}); + +// seed dials with initial values +ctx.streams.a.next(0.66); +ctx.streams.b.next(1); +ctx.streams.c.next(0.75); diff --git a/examples/canvas-dial/tsconfig.json b/examples/canvas-dial/tsconfig.json new file mode 100644 index 0000000000..bd6481a5a6 --- /dev/null +++ b/examples/canvas-dial/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "." + }, + "include": [ + "./src/**/*.ts" + ] +} diff --git a/examples/canvas-dial/webpack.config.js b/examples/canvas-dial/webpack.config.js new file mode 100644 index 0000000000..e2bf1e8d3a --- /dev/null +++ b/examples/canvas-dial/webpack.config.js @@ -0,0 +1,18 @@ +module.exports = { + entry: "./src/index.ts", + output: { + path: __dirname, + filename: "bundle.js" + }, + resolve: { + extensions: [".ts", ".js"] + }, + module: { + rules: [ + { test: /\.ts$/, use: "ts-loader" } + ] + }, + devServer: { + contentBase: "." + } +}; diff --git a/examples/cellular-automata/src/index.ts b/examples/cellular-automata/src/index.ts index 56d6e44f4b..996f69a46b 100644 --- a/examples/cellular-automata/src/index.ts +++ b/examples/cellular-automata/src/index.ts @@ -115,18 +115,20 @@ const isPreset = (id) => presets.findIndex((x) => x[0] === id) !== -1; applyRules(location.hash.length > 18 ? location.hash.substr(1) : presets[1][0]); // define & start main app component -start("app", () => { - const id = location.hash.substr(1); - return ["div", - ruleBoxes("birth", 0), - ruleBoxes("survive", 1), - ["div", - ["button", { onclick: () => randomizeRules() }, "randomize rules"], - ["button", { onclick: () => randomizeGrid() }, "reset grid"], - [dropdown, { onchange: (e) => applyRules(e.target.value) }, - presets, - isPreset(id) ? id : ""] - ], - ["pre", format(grid = convolve(grid, rules, W, H), W)] - ]; -}); +start( + () => { + const id = location.hash.substr(1); + return ["div", + ruleBoxes("birth", 0), + ruleBoxes("survive", 1), + ["div", + ["button", { onclick: () => randomizeRules() }, "randomize rules"], + ["button", { onclick: () => randomizeGrid() }, "reset grid"], + [dropdown, { onchange: (e) => applyRules(e.target.value) }, + presets, + isPreset(id) ? id : ""] + ], + ["pre", format(grid = convolve(grid, rules, W, H), W)] + ]; + } +); diff --git a/examples/commit-table-ssr/.gitignore b/examples/commit-table-ssr/.gitignore new file mode 100644 index 0000000000..9c418ce79f --- /dev/null +++ b/examples/commit-table-ssr/.gitignore @@ -0,0 +1,3 @@ +node_modules +yarn.lock +*.js diff --git a/examples/commit-table-ssr/README.md b/examples/commit-table-ssr/README.md new file mode 100644 index 0000000000..ee39157b5c --- /dev/null +++ b/examples/commit-table-ssr/README.md @@ -0,0 +1,83 @@ +# commit-table-ssr + +[Live version](http://demo.thi.ng/umbrella/commit-table-ssr/) + +This example demonstrates isomorphic, +[@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/master/packages/hiccup)-based +server-side rendering, static file generation and an extended +interactive browser version of a git repo commit log. The server is a +simple [express](https://expressjs.com/) app. + +All of the UI components used on the server side too work in the browser +without change, though the browser version has additional functionality +(i.e. interactive filtering of commits via user provided search filter). + +The server example builds a large table (~700KB worth of HTML) of this +repo's 1460+ commits by shelling out to `git` to retrieve and transform +the raw history / log using +[transducer](https://github.com/thi-ng/umbrella/tree/master/packages/transducers) +pipelines. Since this process doesn't need to be performed for each +server request, the app uses +[TLRUCaches](https://github.com/thi-ng/umbrella/tree/master/packages/cache#tlru) +to cache both the raw commits and the rendered HTML. Reloading the page +will show the timing difference. + +To use another local repo on your hard drive, [update the settings +here](./src/common/config.ts#L24). + +## Building & running + +### Server-side rendering + +See [/src/server/index.ts](./src/server/index.ts) for details... + +```bash +git clone https://github.com/thi-ng/umbrella.git +cd umbrella/examples/commit-table-ssr +yarn install +yarn dev +``` + +Then open http://localhost:3000/ssr in your browser. + +### Browser version + +The browser version uses the same UI components, but realizes them via +[@thi.ng/hdom](https://github.com/thi-ng/umbrella/tree/master/packages/hdom). + +In addition to the SSR version above, this version displays additional +repo stats and allows for interactive filtering of the commits. The +commits themselves are loaded as JSON and therefore also require the +server app. + +Furthermore, this version utilizes +[@thi.ng/rstream](https://github.com/thi-ng/umbrella/tree/master/packages/rstream) +to build a simple dataflow graph and handle app state changes via +various reactive stream constructs. Comments are included. + +See [/src/client/index.ts](./src/client/index.ts) for details... + +``` +yarn dev-client +``` + +Once you see a message that the server is running, open +http://localhost:3000 in your browser. + +### Static file generation + +The result will be saved to `table.html` in this example's root directory. + +See [/src/server/static.ts](./src/server/static.ts) for details... + +```bash +yarn build-static +``` + +## Authors + +- Karsten Schmidt + +## License + +© 2018 Karsten Schmidt // Apache Software License 2.0 diff --git a/examples/commit-table-ssr/package.json b/examples/commit-table-ssr/package.json new file mode 100644 index 0000000000..5cf5e0f931 --- /dev/null +++ b/examples/commit-table-ssr/package.json @@ -0,0 +1,31 @@ +{ + "name": "commit-table-ssr", + "version": "0.0.1", + "repository": "https://github.com/thi-ng/umbrella", + "author": "Karsten Schmidt ", + "license": "Apache-2.0", + "scripts": { + "build-static": "tsc && node build/server/static.js", + "build-client": "webpack --mode production --display-reasons --display-modules", + "dev": "tsc && node build/server/index.js", + "dev-client": "yarn build-client && yarn dev" + }, + "devDependencies": { + "ts-loader": "^4.4.2", + "typescript": "^3.0.1", + "webpack": "^4.16.3", + "webpack-cli": "^3.1.0", + "webpack-dev-server": "^3.1.5" + }, + "dependencies": { + "express": "^4.16.3", + "@thi.ng/associative": "latest", + "@thi.ng/cache": "latest", + "@thi.ng/hiccup": "latest", + "@thi.ng/resolve-map": "latest", + "@thi.ng/rstream": "latest", + "@thi.ng/transducers": "latest", + "@thi.ng/transducers-hdom": "latest", + "@types/express": "^4.16.0" + } +} \ No newline at end of file diff --git a/examples/commit-table-ssr/src/client/index.ts b/examples/commit-table-ssr/src/client/index.ts new file mode 100644 index 0000000000..453fa9887d --- /dev/null +++ b/examples/commit-table-ssr/src/client/index.ts @@ -0,0 +1,126 @@ +import { resolve as resolveMap } from "@thi.ng/resolve-map"; +import { fromInterval } from "@thi.ng/rstream/from/interval"; +import { stream } from "@thi.ng/rstream/stream"; +import { sync } from "@thi.ng/rstream/stream-sync"; +import { resolve as resolvePromise } from "@thi.ng/rstream/subs/resolve"; +import { updateDOM } from "@thi.ng/transducers-hdom"; +import { add } from "@thi.ng/transducers/rfn/add"; +import { conj } from "@thi.ng/transducers/rfn/conj"; +import { transduce } from "@thi.ng/transducers/transduce"; +import { map } from "@thi.ng/transducers/xform/map"; +import { pluck } from "@thi.ng/transducers/xform/pluck"; +import { throttleTime } from "@thi.ng/transducers/xform/throttle-time"; + +import { AppContext, Commit } from "../common/api"; +import { header } from "../common/components/header"; +import { link } from "../common/components/link"; +import { repoTable } from "../common/components/repo-table"; +import { ctx } from "../common/config"; + +// UI root component +const app = (state) => + ["div", + [header, ctx.repo.name], + [stats, state], + [repoTable, state.commits], + ]; + +// stats container component +const stats = (ctx: AppContext, state) => + ["div", ctx.ui.stats.root, + ["div.tl", ctx.ui.stats.col, + [searchFilter, state]], + ["div.tc", ctx.ui.stats.col, + ["div", `Authors: ${state.authors}`], + ["div", `Total adds: ${state.adds} (${state.avgAdds} avg / commit)`], + ["div", `Total dels: ${state.dels} (${state.avgDels} avg / commit)`] + ], + ["div.tr", ctx.ui.stats.col, + [link, { ...ctx.ui.stats.link, href: ctx.repo.url }, ctx.repo.url] + ] + ]; + +// search filter input component +const searchFilter = (ctx: AppContext, state) => + ["div", + "Filter:", + ["input", { + ...ctx.ui.search, + type: "text", + value: state.search, + // emit changes on `search` stream + oninput: (e) => search.next(e.target.value.toLowerCase()) + }], + `(${state.commits.length} commits)` + ]; + +// transformation function to filter commits with search string +// doesn't apply filter if search term is empty +const filterCommits = ({ commits, search }) => + ({ + search, + commits: search ? + commits.filter((x) => x.msg.toLowerCase().indexOf(search) !== -1) : + commits + }); + +// transformation function to compute stats of filtered commits +// uses `resolve-map` package to execute given functions in dependency order +const computeStats = (state) => resolveMap({ + ...state, + adds: ({ commits }) => transduce(map((x: Commit) => x.add || 0), add(), commits), + dels: ({ commits }) => transduce(map((x: Commit) => x.del || 0), add(), commits), + authors: ({ commits }) => transduce(pluck("author"), conj(), commits).size, + avgAdds: ({ commits, adds }) => (adds / commits.length) | 0, + avgDels: ({ commits, dels }) => (dels / commits.length) | 0 +}); + +// error stream & handler +const error = stream(); +error.subscribe({ next: (e) => alert(`An error occurred:\n${e}`) }); + +// commit log stream, reloads every 1h +const commits = fromInterval(60 * 60 * 1000) + // fetch commits from server + .transform( + map(() => fetch("./commits").then( + (res) => res.ok ? res.json() : error.next("error loading commits"), + (e) => error.next(e.message) + )) + ) + // the above transducer returns a promise + // this next subscription resolves it and only then + // passes the result downstream + .subscribe( + resolvePromise({ fail: (e) => error.next(e.message) }) + ); + +// stream of commit filter terms +const search = stream(); + +// stream combinator & transformation into UI / DOM update +sync({ + // streams to synchronize + src: { + commits, + // throttle search stream @ 10Hz (100ms) to minimize + // UI lag for fast typists + search: search.transform(throttleTime(100)), + }, +}) + // now transform the combined stream + // each value is an object tuple of: `{ commits, search }` + .transform( + map(filterCommits), + map(computeStats), + // apply root component + map(app), + // apply hdom tree to real DOM + updateDOM({ ctx }) + ); + +// manual kick off is needed here, since the above stream sync construct +// will only execute once all of its inputs have delivered a value. +// the other input `commits` is triggered automatically because it's +// tied to a timer +search.next(""); diff --git a/examples/commit-table-ssr/src/common/api.ts b/examples/commit-table-ssr/src/common/api.ts new file mode 100644 index 0000000000..294fb33501 --- /dev/null +++ b/examples/commit-table-ssr/src/common/api.ts @@ -0,0 +1,56 @@ +/** + * Basic HTML document abstraction for hiccup serialization. + * See ./html.ts for usage + */ +export interface HTMLDoc { + lang?: string; + head?: Partial; + body: any[]; + /** + * This object will be passed to all component functions. + */ + ctx: AppContext; +} + +export interface HTMLHead { + title: string; + meta: any[]; + links: { rel: string, href: string }[]; + scripts: { src: string, type?: string }[]; + styles: string[]; +} + +/** + * App context / config object. + * Contains repo information & component styles + */ +export interface AppContext { + repo: Repo; + ui: { + body: any; + link: any; + header: any; + table: any; + stats: any; + search: any; + } +} + +export interface Repo { + name: string; + path: string; + url: string; +} + +/** + * Data structure of a single commit. + */ +export interface Commit { + sha: string; + date: string; + author: string; + msg: string; + files: number; + add: number; + del: number; +} diff --git a/examples/commit-table-ssr/src/common/components/commit-link.ts b/examples/commit-table-ssr/src/common/components/commit-link.ts new file mode 100644 index 0000000000..0015a8f304 --- /dev/null +++ b/examples/commit-table-ssr/src/common/components/commit-link.ts @@ -0,0 +1,13 @@ +import { AppContext } from "../api"; +import { link } from "./link"; + +/** + * Link component which links to given SHA commit hash using the + * context's repo URL. + * + * @param ctx + * @param sha + * @param body + */ +export const commitLink = (ctx: AppContext, sha: string, body: string) => + [link, { ...ctx.ui.link, href: `${ctx.repo.url}/commit/${sha}` }, body]; diff --git a/examples/commit-table-ssr/src/common/components/header.ts b/examples/commit-table-ssr/src/common/components/header.ts new file mode 100644 index 0000000000..8b54cb395e --- /dev/null +++ b/examples/commit-table-ssr/src/common/components/header.ts @@ -0,0 +1,6 @@ +import { AppContext } from "../api"; + +export const header = (ctx: AppContext, title: string) => + ["section", ctx.ui.header.root, + ["h1", ctx.ui.header.title, title] + ]; diff --git a/examples/commit-table-ssr/src/common/components/link.ts b/examples/commit-table-ssr/src/common/components/link.ts new file mode 100644 index 0000000000..69f78e00bc --- /dev/null +++ b/examples/commit-table-ssr/src/common/components/link.ts @@ -0,0 +1,11 @@ +import { AppContext } from "../api"; + +/** + * Generic HTML link component. + * + * @param ctx + * @param href + * @param body + */ +export const link = (_: AppContext, attribs: any, body: string) => + ["a", attribs, body]; diff --git a/examples/commit-table-ssr/src/common/components/repo-table.ts b/examples/commit-table-ssr/src/common/components/repo-table.ts new file mode 100644 index 0000000000..13a672c284 --- /dev/null +++ b/examples/commit-table-ssr/src/common/components/repo-table.ts @@ -0,0 +1,44 @@ +import { comp } from "@thi.ng/transducers/func/comp"; +import { repeat } from "@thi.ng/transducers/iter/repeat"; +import { iterator } from "@thi.ng/transducers/iterator"; +import { map } from "@thi.ng/transducers/xform/map"; +import { mapIndexed } from "@thi.ng/transducers/xform/map-indexed"; +import { partitionBy } from "@thi.ng/transducers/xform/partition-by"; + +import { AppContext, Commit } from "../api"; +import { commitLink } from "./commit-link"; +import { table } from "./table"; + +/** + * Git commit log table component. Consumes iterable of `Commit` objects + * and transforms each into a table row. + * + * @param _ + * @param commits + */ +export const repoTable = (_: AppContext, commits: Iterable) => + [table, + ["15%", "15%", "55%", "5%", "5%", "5%"], + ["Date", "Author", "Description", "Files", "Adds", "Dels"], + iterator( + comp( + // convert commit into tuple, one value per table cell + map((x: Commit) => [ + x.date.substr(0, 10), + x.author, + [commitLink, x.sha, x.msg], + x.files, + x.add ? ["span.green", `+${x.add}`] : null, + x.del ? ["span.red", `-${x.del}`] : null, + ]), + // partition rows by month + partitionBy((row: any[]) => row[0].split("-")[1]), + // insert month headers (but not in 1st chunk) + mapIndexed((i, month) => [ + i > 0 ? [month[0][0].substr(0, 7), ...repeat("", 5)] : null, + month + ]) + ), + commits + ) + ]; diff --git a/examples/commit-table-ssr/src/common/components/table.ts b/examples/commit-table-ssr/src/common/components/table.ts new file mode 100644 index 0000000000..4a32cb6258 --- /dev/null +++ b/examples/commit-table-ssr/src/common/components/table.ts @@ -0,0 +1,58 @@ +import { map } from "@thi.ng/transducers/xform/map"; + +import { AppContext } from "../api"; +import { mapcat } from "@thi.ng/transducers/xform/mapcat"; + +const thead = (ctx: AppContext, head: Iterable) => + ["thead", + [row, ctx.ui.table.head.row, + map((x) => ["th", ctx.ui.table.head.cell, x], head)]]; + +const row = (ctx: AppContext, attribs: any, body: Iterable) => + ["tr", { ...ctx.ui.table.row, ...attribs }, ...body]; + +/** + * Generic HTML table component w/ column layout support & intermediate + * headers. The `body` iterable MUST contain groups of rows, each group + * with an optional new header row: + * + * ``` + * [table, + * // column layout + * ["25%", "25%", "50%"], + * // global header + * ["Price","Item","Description"], + * // body + * [ + * // row group #1 w/o header + * [null, [ + * [10.99, "Yaki Udon", "noodle dish"], + * [4.99, "Asahi", "beer"], + * ]], + * // row group #2 w/ header + * [["Subtotal", "VAT", "Total"], [ + * [15.98, "20%", 19.18] + * ]] + * ] + * ] + * ``` + * + * @param ctx + * @param layout column sizes + * @param head header cell values + * @param body row chunks + */ +export const table = (ctx: AppContext, layout: (string | number)[], head: Iterable, body: Iterable>) => + ["table", ctx.ui.table.root, + map((x) => ["col", { style: { width: x } }], layout || []), + [thead, head], + mapcat(([hd, rows]) => + [ + hd ? [thead, hd] : null, + ["tbody", + map((cols: any) => [row, null, map((x) => ["td", ctx.ui.table.cell, x], cols)], rows) + ] + ], + body + ) + ]; diff --git a/examples/commit-table-ssr/src/common/config.ts b/examples/commit-table-ssr/src/common/config.ts new file mode 100644 index 0000000000..a406e81c6e --- /dev/null +++ b/examples/commit-table-ssr/src/common/config.ts @@ -0,0 +1,56 @@ +import { HTMLDoc, AppContext } from "./api"; + +export const DEFAULT_DOC: HTMLDoc = { + head: { + meta: [ + { "http-equiv": "Content-Type", content: "text/html;charset=UTF-8" }, + { "http-equiv": "X-UA-Compatible", content: "ie=edge" } + ], + links: [ + { rel: "stylesheet", href: "https://unpkg.com/tachyons@4.11.1/css/tachyons.min.css" }, + { rel: "stylesheet", href: "https://fonts.googleapis.com/css?family=Inconsolata" } + ], + scripts: [], + styles: [], + title: "", + }, + ctx: null, + body: [], +}; + +/** + * Main app config. + */ +export const ctx: AppContext = { + repo: { + name: "thi.ng/umbrella", + path: ".", + url: "https://github.com/thi-ng/umbrella", + }, + ui: { + body: { class: "sans-serif vh-100" }, + link: { class: "link blue hover-light-blue" }, + header: { + root: { class: "bg-dark-gray white pa3 ma0 w-100" }, + title: { class: "tc ma0 pa0 fw1" } + }, + table: { + root: { + class: "w-100 collapse ba br2 b--black-10 pv2 ph3 f7 f6-ns", + style: { "font-family": "Inconsolata, monospace" } + }, + head: { + row: { class: "tl bg-black white" }, + cell: { class: "pv1 pv2-ns ph2 ph3-ns" }, + }, + row: { class: "striped--light-gray" }, + cell: { class: "pv1 pv2-ns ph2 ph3-ns" } + }, + stats: { + root: { class: "flex items-center pa2 bg-light-green dark-gray f7" }, + col: { class: "w-33" }, + link: { class: "link dark-gray" } + }, + search: { class: "pa1 mh2" } + } +}; diff --git a/examples/commit-table-ssr/src/server/build-table.ts b/examples/commit-table-ssr/src/server/build-table.ts new file mode 100644 index 0000000000..bf2f22765b --- /dev/null +++ b/examples/commit-table-ssr/src/server/build-table.ts @@ -0,0 +1,18 @@ +import { Commit } from "../common/api"; +import { header } from "../common/components/header"; +import { repoTable } from "../common/components/repo-table"; +import { ctx } from "../common/config"; +import { html } from "./html"; + +/** + * Shared function used by both the server and for static file + * generation. Returns serialized HTML string of commit table. + */ +export const buildRepoTableHTML = (commits: Iterable) => + html({ + ctx, + body: [ + [header, ctx.repo.name], + [repoTable, commits], + ] + }); diff --git a/examples/commit-table-ssr/src/server/git.ts b/examples/commit-table-ssr/src/server/git.ts new file mode 100644 index 0000000000..8120bcca59 --- /dev/null +++ b/examples/commit-table-ssr/src/server/git.ts @@ -0,0 +1,86 @@ +import { + assocObj, + comp, + filter, + iterator, + map, + mapcat, + partitionBy, + transduce, + tuples +} from "@thi.ng/transducers"; +import { execSync } from "child_process"; +import { resolve } from "path"; + +import { Commit } from "../common/api"; + +/** + * Calls out to git to retrieve raw log string. + * + * @param repoPath + */ +const gitLog = (repoPath: string) => + execSync( + `git log --pretty=format:"%ad~~%an~~%h~~%s" --shortstat --date=iso-strict`, + { cwd: resolve(repoPath) } + ).toString().trim(); + +/** + * Transforms 1st line of a raw commit log into a partial commit + * object. + * + * @param log + */ +const parseLog = ([log]: string[]): Partial => { + const [date, author, sha, msg] = log.split("~~"); + return { date, author, sha, msg }; +}; + +/** + * Transforms 2nd line (if present) of a raw commit log into a partial + * commit object. + * + * @param log + */ +const parseStats = ([_, stats]: string[]): Partial => + stats ? + transduce( + map(([k, v]) => [k, parseInt(v)]), + assocObj(), + tuples(["files", "add", "del"], stats.split(",")) + ) : + null; + +/** + * Retrieves git log for given `repoPath` and transforms it into an + * iterable of `Commit` objects. + * + * @param repoPath + */ +export const repoCommits = (repoPath: string) => + iterator( + comp( + // get raw log + map(gitLog), + // split into lines + mapcat((x: string) => x.split("\n")), + // group related lines: + // normal commits have 2 lines + 1 empty + // merge commits have only 1 line + // pick a random number for merge commits + // in case there're successive ones + partitionBy( + (x) => x.indexOf("~~Merge ") !== -1 ? + Math.random() : + x.length > 0 ? 1 : 0 + ), + // remove empty lines + filter((x) => x[0].length > 0), + // parse commit details + map((commit) => { + ...parseLog(commit), + ...parseStats(commit) + }) + ), + [repoPath] + ); diff --git a/examples/commit-table-ssr/src/server/html.ts b/examples/commit-table-ssr/src/server/html.ts new file mode 100644 index 0000000000..07781d8d94 --- /dev/null +++ b/examples/commit-table-ssr/src/server/html.ts @@ -0,0 +1,39 @@ +import { mergeDeepObj } from "@thi.ng/associative/merge-deep"; +import { serialize } from "@thi.ng/hiccup"; +import { map } from "@thi.ng/transducers/xform/map"; + +import { AppContext, HTMLDoc } from "../common/api"; +import { DEFAULT_DOC } from "../common/config"; + +/** + * Takes a `HTMLDoc` object and serializes it into an HTML5 string. The + * `body` field of the document must contain elements in thi.ng/hiccup + * format, i.e. it's an array in which each element is a nested array, + * string or ES6 iterable, each encoding a part of the full DOM to be + * generated. The resulting HTML string will not contain any whitespace + * unless it's part of string values. + * + * See here for more reference: + * https://github.com/thi-ng/umbrella/tree/master/packages/hiccup + * + * @param doc + */ +export const html = (doc: HTMLDoc) => { + doc = mergeDeepObj(DEFAULT_DOC, doc); + return `${serialize( + ["html", { lang: doc.lang || "en" }, + ["head", + map((meta) => ["meta", meta], doc.head.meta), + map((s) => script(null, s), doc.head.scripts), + map((link) => ["link", link], doc.head.links), + map((css) => ["style", css], doc.head.styles), + ["title", doc.head.title], + ], + ["body", doc.ctx.ui.body, ...doc.body] + ], + doc.ctx + )}`; +}; + +export const script = (_: AppContext, script: { src: string, type?: string }) => + ["script", { type: "text/javascript", ...script }]; \ No newline at end of file diff --git a/examples/commit-table-ssr/src/server/index.ts b/examples/commit-table-ssr/src/server/index.ts new file mode 100644 index 0000000000..302ed03339 --- /dev/null +++ b/examples/commit-table-ssr/src/server/index.ts @@ -0,0 +1,65 @@ +import { TLRUCache } from "@thi.ng/cache"; +import * as express from "express"; + +import { Commit } from "../common/api"; +import { ctx } from "../common/config"; +import { buildRepoTableHTML } from "./build-table"; +import { repoCommits } from "./git"; +import { html, script } from "./html"; + +// building the repo commit table takes quite some time +// therefore we cache results with 1h expiry time +// (which is also the default) +const rawCache = new TLRUCache(null, { ttl: 60 * 60 * 1000 }); +const htmlCache = new TLRUCache(null, { ttl: 60 * 60 * 1000 }); + +const app = express(); + +app.use(express.static(".")); + +// route for browser version +// here we simply return a barebone html doc +// with a reference to the built client JS +app.get("/", (_, res) => { + res.send(html({ + ctx, + head: { + title: "commit-table-hdom", + }, + body: [ + ["div#app"], + [script, { src: "bundle.js" }] + ], + })); +}); + +// route for the client to retrieve the commit log as JSON +app.get("/commits", (_, res) => { + // retrieve raw commit log from cache or + // (re)create if missing... + rawCache.getSet( + ctx.repo.path, + async () => [...repoCommits(ctx.repo.path)] + ).then( + (commits) => res.type("json").send(commits) + ) +}); + +// route for server-side rendering +// uses both caches +app.get("/ssr", (_, res) => { + // retrieve rendered html from cache or + // (re)create if missing... + htmlCache.getSet( + ctx.repo.path, + async () => buildRepoTableHTML( + await rawCache.getSet( + ctx.repo.path, + async () => [...repoCommits(ctx.repo.path)] + ) + ) + ).then((doc) => res.send(doc)) +}); + +console.log("starting server @ http://localhost:3000"); +app.listen(3000); diff --git a/examples/commit-table-ssr/src/server/static.ts b/examples/commit-table-ssr/src/server/static.ts new file mode 100644 index 0000000000..4170ca5191 --- /dev/null +++ b/examples/commit-table-ssr/src/server/static.ts @@ -0,0 +1,8 @@ +import { writeFileSync } from "fs"; + +import { ctx } from "../common/config"; +import { buildRepoTableHTML } from "./build-table"; +import { repoCommits } from "./git"; + +// generate as file in example directory +writeFileSync("table.html", buildRepoTableHTML(repoCommits(ctx.repo.path))); diff --git a/examples/commit-table-ssr/tsconfig.json b/examples/commit-table-ssr/tsconfig.json new file mode 100644 index 0000000000..9c5143a908 --- /dev/null +++ b/examples/commit-table-ssr/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "build" + }, + "include": [ + "./src/**/*.ts" + ] +} \ No newline at end of file diff --git a/examples/crypto-chart/README.md b/examples/crypto-chart/README.md index f6fb86990c..e3188dedaf 100644 --- a/examples/crypto-chart/README.md +++ b/examples/crypto-chart/README.md @@ -2,7 +2,7 @@ [Live demo](https://s3.amazonaws.com/demo.thi.ng/umbrella/crypto-chart/index.html) -![chart](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/crypto-chart.png) +![chart](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/screenshots/crypto-chart.png) Price data provided by [cryptocompare.com](https://min-api.cryptocompare.com/). diff --git a/examples/crypto-chart/src/index.ts b/examples/crypto-chart/src/index.ts index 0039ac4aa2..773266a98c 100644 --- a/examples/crypto-chart/src/index.ts +++ b/examples/crypto-chart/src/index.ts @@ -1,6 +1,4 @@ import { dropdown, DropDownOption } from "@thi.ng/hdom-components/dropdown"; -import { diffElement } from "@thi.ng/hdom/diff"; -import { normalizeTree } from "@thi.ng/hdom/normalize"; import { group } from "@thi.ng/hiccup-svg/group"; import { line } from "@thi.ng/hiccup-svg/line"; import { polygon } from "@thi.ng/hiccup-svg/polygon"; @@ -16,6 +14,7 @@ import { sync } from "@thi.ng/rstream/stream-sync"; import { resolve as resolvePromise } from "@thi.ng/rstream/subs/resolve"; import { trace } from "@thi.ng/rstream/subs/trace"; import { padLeft } from "@thi.ng/strings/pad-left"; +import { updateDOM } from "@thi.ng/transducers-hdom"; import { ema } from "@thi.ng/transducers-stats/ema"; import { hma } from "@thi.ng/transducers-stats/hma"; import { sma } from "@thi.ng/transducers-stats/sma"; @@ -23,7 +22,6 @@ import { wma } from "@thi.ng/transducers-stats/wma"; import { comp } from "@thi.ng/transducers/func/comp"; import { pairs } from "@thi.ng/transducers/iter/pairs"; import { range } from "@thi.ng/transducers/iter/range"; -import { reducer } from "@thi.ng/transducers/reduce"; import { max } from "@thi.ng/transducers/rfn/max"; import { min } from "@thi.ng/transducers/rfn/min"; import { push } from "@thi.ng/transducers/rfn/push"; @@ -33,7 +31,6 @@ import { map } from "@thi.ng/transducers/xform/map"; import { mapIndexed } from "@thi.ng/transducers/xform/map-indexed"; import { mapcat } from "@thi.ng/transducers/xform/mapcat"; import { pluck } from "@thi.ng/transducers/xform/pluck"; -import { scan } from "@thi.ng/transducers/xform/scan"; // this example demonstrates how to use @thi.ng/rstream & // @thi.ng/transducer constructs to create a basic cryptocurrency candle @@ -404,16 +401,7 @@ sync({ ] ), // perform hdom update / diffing - scan( - reducer( - () => [], - (prev, curr) => { - curr = normalizeTree(curr, {}); - diffElement(document.getElementById("app"), prev, curr); - return curr; - } - ) - ) + updateDOM() ) }); diff --git a/examples/dashboard/src/index.ts b/examples/dashboard/src/index.ts index b6aaac01bb..c024977d4d 100644 --- a/examples/dashboard/src/index.ts +++ b/examples/dashboard/src/index.ts @@ -31,4 +31,4 @@ const app = (() => { })(); // start update loop (RAF) -start("app", app); +start(app); diff --git a/examples/devcards/src/index.ts b/examples/devcards/src/index.ts index e169683e2f..0db3e0b898 100644 --- a/examples/devcards/src/index.ts +++ b/examples/devcards/src/index.ts @@ -48,7 +48,7 @@ function defcard(card: CardFn, state?: IAtom, title?: string, parent?: stri const root = card(state); // kick off hdom renderloop - start(parent, () => ["div.card", ["h3", title], ["div.body", root, ["pre", json]]]); + start(() => ["div.card", ["h3", title], ["div.body", root, ["pre", json]]], { root: parent }); } /** diff --git a/examples/hdom-basics/src/index.ts b/examples/hdom-basics/src/index.ts index 4733c606e2..d75a570397 100644 --- a/examples/hdom-basics/src/index.ts +++ b/examples/hdom-basics/src/index.ts @@ -18,7 +18,7 @@ const app = () => { }; // start update loop (browser only, see diagram below) -hdom.start(document.body, app()); +hdom.start(app()); // alternatively apply DOM tree only once // (stateful components won't update though) diff --git a/examples/hdom-benchmark/src/index.ts b/examples/hdom-benchmark/src/index.ts index ea124cdad6..e8bcc63db4 100644 --- a/examples/hdom-benchmark/src/index.ts +++ b/examples/hdom-benchmark/src/index.ts @@ -98,4 +98,4 @@ const app = () => { }; }; -start("app", app(), null, false); +start(app(), { span: false }); diff --git a/examples/hdom-dropdown-fuzzy/src/index.ts b/examples/hdom-dropdown-fuzzy/src/index.ts index 9298463047..890bac3535 100644 --- a/examples/hdom-dropdown-fuzzy/src/index.ts +++ b/examples/hdom-dropdown-fuzzy/src/index.ts @@ -23,7 +23,7 @@ const ctx = { const dd = dropdown("theme.dd"); const input = cancelableInput("theme.input"); -start("app", +start( (ctx) => { ctx.bus.processQueue(); return ["div", ctx.theme.root, @@ -39,6 +39,7 @@ start("app", ], ]; }, - ctx); + { ctx } +); // window["ctx"] = ctx; diff --git a/examples/hdom-dropdown/src/index.ts b/examples/hdom-dropdown/src/index.ts index cbb567aced..a33829c656 100644 --- a/examples/hdom-dropdown/src/index.ts +++ b/examples/hdom-dropdown/src/index.ts @@ -11,7 +11,7 @@ bus.instrumentWith([trace]); const dd = dropdown("theme.dd"); -start("app", +start( (ctx) => { bus.processQueue(); return ["div", ctx.theme.root, @@ -29,4 +29,5 @@ start("app", }]], ]; }, - { bus, theme }); + { ctx: { bus, theme } } +); diff --git a/examples/hdom-theme-adr-0003/src/index.ts b/examples/hdom-theme-adr-0003/src/index.ts index feaef66d73..17f9fada5f 100644 --- a/examples/hdom-theme-adr-0003/src/index.ts +++ b/examples/hdom-theme-adr-0003/src/index.ts @@ -144,4 +144,4 @@ const app = (ctx) => ["p", [link, "https://github.com/thi-ng/umbrella/blob/develop/examples/hdom-theme-adr-0003", "Source"]] ]; -start("app", app, ctx); +start(app, { ctx }); diff --git a/examples/hydrate-basics/.gitignore b/examples/hydrate-basics/.gitignore new file mode 100644 index 0000000000..9c418ce79f --- /dev/null +++ b/examples/hydrate-basics/.gitignore @@ -0,0 +1,3 @@ +node_modules +yarn.lock +*.js diff --git a/examples/hydrate-basics/README.md b/examples/hydrate-basics/README.md new file mode 100644 index 0000000000..4fcad46175 --- /dev/null +++ b/examples/hydrate-basics/README.md @@ -0,0 +1,21 @@ +# hydrate-basics + +[Live demo](http://demo.thi.ng/umbrella/hydrate-basics/) + +This example demonstrates how to hydrate a pre-created HTML DOM (e.g. +server-side rendering) client-side. + +```bash +git clone https://github.com/thi-ng/umbrella.git +cd umbrella/examples/hydrate-basics +yarn install +yarn start +``` + +## Authors + +- Karsten Schmidt + +## License + +© 2018 Karsten Schmidt // Apache Software License 2.0 diff --git a/examples/hydrate-basics/index.html b/examples/hydrate-basics/index.html new file mode 100644 index 0000000000..710f580a78 --- /dev/null +++ b/examples/hydrate-basics/index.html @@ -0,0 +1,19 @@ + + + + + + + + hydrate-basics + + + + + +
+ + + + \ No newline at end of file diff --git a/examples/hydrate-basics/package.json b/examples/hydrate-basics/package.json new file mode 100644 index 0000000000..9fefbe8437 --- /dev/null +++ b/examples/hydrate-basics/package.json @@ -0,0 +1,24 @@ +{ + "name": "hydrate-basics", + "version": "0.0.1", + "repository": "https://github.com/thi-ng/umbrella", + "author": "Karsten Schmidt ", + "license": "Apache-2.0", + "scripts": { + "build": "webpack --mode production --display-reasons --display-modules", + "start": "webpack-dev-server --open --mode development --devtool inline-source-map" + }, + "devDependencies": { + "ts-loader": "^4.4.2", + "typescript": "^3.0.1", + "webpack": "^4.16.3", + "webpack-cli": "^3.1.0", + "webpack-dev-server": "^3.1.5" + }, + "dependencies": { + "@thi.ng/api": "latest", + "@thi.ng/hdom": "latest", + "@thi.ng/rstream": "latest", + "@thi.ng/transducers": "latest" + } +} diff --git a/examples/hydrate-basics/src/index.ts b/examples/hydrate-basics/src/index.ts new file mode 100644 index 0000000000..0faa1b3e45 --- /dev/null +++ b/examples/hydrate-basics/src/index.ts @@ -0,0 +1,84 @@ +import { Atom } from "@thi.ng/atom"; +import { serialize } from "@thi.ng/hiccup/serialize"; +import { start } from "@thi.ng/hdom/start"; +import { canvas2D } from "@thi.ng/hdom-components/canvas"; +import { dropdown } from "@thi.ng/hdom-components/dropdown"; + +// basic state container +const state = new Atom({ + bg: "red", + freq: 0.01 +}); + +// state updates +const setBg = (x: string) => state.resetIn("bg", x); +const setFreq = (x: number) => state.resetIn("freq", x); + +// root component with different types of child components +const app = () => { + // HOF canvas component w/ life cycle methods see for further + // reference: + // https://github.com/thi-ng/umbrella/blob/master/packages/hdom-components/src/canvas.ts + // + // when serializing to html only the component's `render` method + // will be invoked. the component's `init` is invoked later when + // hydrating the DOM the `update` fn given here is canvas specific + const canvas = canvas2D({ + update: (el, ctx, _, time, __, ___, bg, freq) => { + const y = el.height / 2; + ctx.fillStyle = bg; + ctx.fillRect(0, 0, el.width, el.height); + ctx.strokeStyle = "white"; + ctx.lineWidth = 3; + ctx.beginPath(); + ctx.moveTo(0, y + y * Math.sin(time * freq)); + for (let x = 5; x < el.width; x += 5) { + ctx.lineTo(x, y + y * Math.sin((time + x) * freq)); + } + ctx.stroke(); + } + }); + // when serializing to HTML all event attributes w/ function values + // will be excluded, however the event listeners will be attached + // during hydration (1st frame of hdom update loop) + + // btw. the class names are for tachyons css + return (state) => { + state = state.deref(); + return ["div#root.w-50-ns.flex.ma2.sans-serif", + ["div.w-50-ns", + [canvas, { width: 200, height: 200 }, state.bg, state.freq]], + ["div.w-50-ns", + ["label.db.mb3", { for: "#bg" }, "Background color", + [dropdown, + { + id: "bg", + class: "w-100", + onchange: (e) => setBg(e.target.value) + }, + [["", "Choose..."], ["red", "Red"], ["green", "Green"], ["blue", "Blue"]], + state.bg + ]], + ["label.db.mb3", { for: "#freq" }, "Frequency", + ["input", { + id: "freq", + class: "w-100", + type: "range", + min: 0.001, + max: 0.02, + step: 0.001, + value: state.freq, + oninput: (e) => setFreq(parseFloat(e.target.value)) + }]] + ], + ]; + }; +}; + +// emulate SSR by serializing to HTML +const html = serialize(app()(state), null, false, true); +document.getElementById("app").innerHTML = html; +console.log(html); + +// ..then start hdom update loop w/ hydrate enabled +start(app(), { hydrate: true, ctx: state }); diff --git a/examples/hydrate-basics/tsconfig.json b/examples/hydrate-basics/tsconfig.json new file mode 100644 index 0000000000..bd6481a5a6 --- /dev/null +++ b/examples/hydrate-basics/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "." + }, + "include": [ + "./src/**/*.ts" + ] +} diff --git a/examples/hydrate-basics/webpack.config.js b/examples/hydrate-basics/webpack.config.js new file mode 100644 index 0000000000..e2bf1e8d3a --- /dev/null +++ b/examples/hydrate-basics/webpack.config.js @@ -0,0 +1,18 @@ +module.exports = { + entry: "./src/index.ts", + output: { + path: __dirname, + filename: "bundle.js" + }, + resolve: { + extensions: [".ts", ".js"] + }, + module: { + rules: [ + { test: /\.ts$/, use: "ts-loader" } + ] + }, + devServer: { + contentBase: "." + } +}; diff --git a/examples/interceptor-basics/src/index.ts b/examples/interceptor-basics/src/index.ts index 75a6ca2558..7027aeb40c 100644 --- a/examples/interceptor-basics/src/index.ts +++ b/examples/interceptor-basics/src/index.ts @@ -134,4 +134,4 @@ const app = () => { }; // kick off hdom render loop -start("app", app()); +start(app()); diff --git a/examples/json-components/src/index.ts b/examples/json-components/src/index.ts index 9bd9790bb4..728ae931aa 100644 --- a/examples/json-components/src/index.ts +++ b/examples/json-components/src/index.ts @@ -95,7 +95,6 @@ const editor = (() => { // start UI start( - document.getElementById("app"), () => ["div#container", ["div", editor], diff --git a/examples/login-form/src/index.ts b/examples/login-form/src/index.ts index cb1242cb48..089be68615 100644 --- a/examples/login-form/src/index.ts +++ b/examples/login-form/src/index.ts @@ -76,8 +76,8 @@ const currView = db.addView( // app root component const app = () => - ["div#app", + ["div", currView, ["footer", "Made with @thi.ng/atom and @thi.ng/hdom"]]; -start(document.body, app); +start(app); diff --git a/examples/router-basics/src/app.ts b/examples/router-basics/src/app.ts index 4969d52691..c046f68e70 100644 --- a/examples/router-basics/src/app.ts +++ b/examples/router-basics/src/app.ts @@ -110,11 +110,14 @@ export class App { */ start() { this.router.start(); - start(this.config.domRoot, () => { - if (this.ctx.bus.processQueue()) { - return this.rootComponent(); - } - }, this.ctx); + start( + () => { + if (this.ctx.bus.processQueue()) { + return this.rootComponent(); + } + }, + { root: this.config.domRoot, ctx: this.ctx } + ); } /** diff --git a/examples/rstream-dataflow/src/index.ts b/examples/rstream-dataflow/src/index.ts index 07cce78ace..d402f326d4 100644 --- a/examples/rstream-dataflow/src/index.ts +++ b/examples/rstream-dataflow/src/index.ts @@ -144,16 +144,18 @@ const graph = initGraph(db, { }); // start @thi.ng/hdom update loop -start("app", () => - ["div", - ["pre.absolute.top-1.left-1.pa0.ma0.z-2.f7", - JSON.stringify(db.deref(), null, 2)], - // note: direct embedding of result stream below. this works - // since all @thi.ng/rstream subscriptions implement the - // @thi.ng/api/IDeref interface (like several other types, e.g. - // @thi.ng/atom's Atom, Cursor, View etc.) - graph.circle.node - ]); +start( + () => + ["div", + ["pre.absolute.top-1.left-1.pa0.ma0.z-2.f7", + JSON.stringify(db.deref(), null, 2)], + // note: direct embedding of result stream below. this works + // since all @thi.ng/rstream subscriptions implement the + // @thi.ng/api/IDeref interface (like several other types, e.g. + // @thi.ng/atom's Atom, Cursor, View etc.) + graph.circle.node + ] +); // create a GraphViz DOT file of the entire dataflow graph // copy the output from the console into a new text file and then run: diff --git a/examples/rstream-grid/src/app.ts b/examples/rstream-grid/src/app.ts index b042e6d88e..8a8d10b1fe 100644 --- a/examples/rstream-grid/src/app.ts +++ b/examples/rstream-grid/src/app.ts @@ -82,14 +82,13 @@ export class App { const root = this.config.rootComponent(this.ctx); let firstFrame = true; start( - this.config.domRoot, () => { if (this.ctx.bus.processQueue({ history: this.history }) || firstFrame) { firstFrame = false; return root(); } }, - this.ctx + { root: this.config.domRoot, ctx: this.ctx } ); } diff --git a/examples/rstream-hdom/src/index.ts b/examples/rstream-hdom/src/index.ts index a0f45120af..ca7f079685 100644 --- a/examples/rstream-hdom/src/index.ts +++ b/examples/rstream-hdom/src/index.ts @@ -3,7 +3,7 @@ import { fromRAF } from "@thi.ng/rstream/from/raf"; import { sync } from "@thi.ng/rstream/stream-sync"; import { sidechainPartition } from "@thi.ng/rstream/subs/sidechain-partition"; import { Subscription, subscription } from "@thi.ng/rstream/subscription"; -import { updateUI } from "@thi.ng/transducers-hdom"; +import { updateDOM } from "@thi.ng/transducers-hdom"; import { peek } from "@thi.ng/transducers/func/peek"; import { vals } from "@thi.ng/transducers/iter/vals"; import { reducer } from "@thi.ng/transducers/reduce"; @@ -25,31 +25,33 @@ const ctx = { }; /** - * Takes a `parent` DOM element, a stream of `root` component values and - * an arbitrary user context object which will be implicitly passed to - * all component functions embedded in the root component. Subscribes to - * `root` stream & performs DOM diffs / updates using incoming values - * (i.e. UI components). Additionally, a RAF side chain stream is used - * here to synchronize DOM updates to be processed during RAF. + * Takes a `root` DOM element, a stream of `tree` component values and + * an (optional) arbitrary user context object which will be implicitly + * passed to all component functions embedded in the root component. + * Subscribes to `root` stream & performs DOM diffs / updates using + * incoming values (i.e. UI components). Additionally, a RAF side chain + * stream is used here to synchronize DOM update requests to be only + * processed during RAF. If multiple updates are triggered per frame, + * this also ensures that the DOM is only updated once per frame. * * Without RAF synchronization, the following would be sufficient: * * ``` - * root.transform(updateUI(parent, ctx)) + * root.transform(updateDOM({root, ctx})) * ``` * * Returns stream of hdom trees. * - * @param parent root DOM element - * @param root root hdom component stream + * @param root root DOM element + * @param tree hdom component stream * @param ctx user context object */ -const domUpdate = (parent: HTMLElement, root: ISubscribable, ctx?: any) => - root +const domUpdate = (root: HTMLElement, tree: ISubscribable, ctx?: any) => + tree .subscribe(sidechainPartition(fromRAF())) .transform( map(peek), - updateUI(parent, ctx) + updateDOM({ root, ctx }) ); /** diff --git a/examples/svg-particles/src/index.ts b/examples/svg-particles/src/index.ts index 98b644f188..0e6cf49940 100644 --- a/examples/svg-particles/src/index.ts +++ b/examples/svg-particles/src/index.ts @@ -40,4 +40,4 @@ const app = () => { return ["svg", { width, height }, particles]; }; -start(document.body, app); \ No newline at end of file +start(app); \ No newline at end of file diff --git a/examples/svg-waveform/src/app.ts b/examples/svg-waveform/src/app.ts index e5093fbf67..2e5bbf4c2a 100644 --- a/examples/svg-waveform/src/app.ts +++ b/examples/svg-waveform/src/app.ts @@ -71,14 +71,13 @@ export class App { const root = this.config.rootComponent(this.ctx); let firstFrame = true; start( - this.config.domRoot, () => { if (this.ctx.bus.processQueue({ history: this.history }) || firstFrame) { firstFrame = false; return root(); } }, - this.ctx + { root: this.config.domRoot, ctx: this.ctx } ); } diff --git a/examples/todo-list/src/index.ts b/examples/todo-list/src/index.ts index 246b06df30..f539dd9e96 100644 --- a/examples/todo-list/src/index.ts +++ b/examples/todo-list/src/index.ts @@ -83,4 +83,4 @@ const app = () => { }; // kick off UI w/ root component function -start("app", app()); +start(app()); diff --git a/examples/transducers-hdom/src/index.ts b/examples/transducers-hdom/src/index.ts index 399b5c8776..8ae55763e1 100644 --- a/examples/transducers-hdom/src/index.ts +++ b/examples/transducers-hdom/src/index.ts @@ -1,7 +1,7 @@ import { fromInterval } from "@thi.ng/rstream/from/interval"; import { stream } from "@thi.ng/rstream/stream"; import { sync } from "@thi.ng/rstream/stream-sync"; -import { updateUI } from "@thi.ng/transducers-hdom"; +import { updateDOM } from "@thi.ng/transducers-hdom"; import { count } from "@thi.ng/transducers/rfn/count"; import { map } from "@thi.ng/transducers/xform/map"; import { scan } from "@thi.ng/transducers/xform/scan"; @@ -41,8 +41,8 @@ sync({ // transform into hdom component map(app), // apply hdom tree to real DOM - updateUI("app") + updateDOM() ); // kick off -clickStream.next(0); \ No newline at end of file +clickStream.next(0); diff --git a/examples/triple-query/src/app.ts b/examples/triple-query/src/app.ts index 4fc6785f9c..06a9aba1e5 100644 --- a/examples/triple-query/src/app.ts +++ b/examples/triple-query/src/app.ts @@ -76,14 +76,13 @@ export class App { const root = this.config.rootComponent(this.ctx); let firstFrame = true; start( - this.config.domRoot, () => { if (this.ctx.bus.processQueue({ store: this.ctx.store }) || firstFrame) { firstFrame = false; return root(); } }, - this.ctx + { root: this.config.domRoot, ctx: this.ctx } ); } diff --git a/examples/webgl/src/index.ts b/examples/webgl/src/index.ts index 0288559c74..3899b07843 100644 --- a/examples/webgl/src/index.ts +++ b/examples/webgl/src/index.ts @@ -41,4 +41,4 @@ const app = () => { [c3, attribs, 400, 0.05]]; }; -start("app", app()); +start(app()); diff --git a/packages/associative/CHANGELOG.md b/packages/associative/CHANGELOG.md index 4c7a724220..9bc65c3407 100644 --- a/packages/associative/CHANGELOG.md +++ b/packages/associative/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.6.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/associative@0.6.1...@thi.ng/associative@0.6.2) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/associative + ## [0.6.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/associative@0.6.0...@thi.ng/associative@0.6.1) (2018-08-24) diff --git a/packages/associative/README.md b/packages/associative/README.md index da444655cd..1a1af64ef6 100644 --- a/packages/associative/README.md +++ b/packages/associative/README.md @@ -129,7 +129,7 @@ yarn add @thi.ng/associative - [@thi.ng/dcons](https://github.com/thi-ng/umbrella/tree/master/packages/dcons) - [@thi.ng/equiv](https://github.com/thi-ng/umbrella/tree/master/packages/equiv) - [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/master/packages/errors) -- [@thi.ng/iterators](https://github.com/thi-ng/umbrella/tree/master/packages/iterators) +- [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/master/packages/transducers) ## Types diff --git a/packages/associative/package.json b/packages/associative/package.json index 419951e996..b69378787d 100644 --- a/packages/associative/package.json +++ b/packages/associative/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/associative", - "version": "0.6.1", + "version": "0.6.2", "description": "Alternative Set & Map data type implementations with customizable equality semantics & supporting operations", "main": "./index.js", "typings": "./index.d.ts", @@ -31,10 +31,10 @@ "@thi.ng/api": "^4.1.0", "@thi.ng/checks": "^1.5.7", "@thi.ng/compare": "^0.1.6", - "@thi.ng/dcons": "^1.1.1", + "@thi.ng/dcons": "^1.1.2", "@thi.ng/equiv": "^0.1.7", "@thi.ng/errors": "^0.1.6", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "data structures", diff --git a/packages/atom/CHANGELOG.md b/packages/atom/CHANGELOG.md index 5f933d0779..95d8d3780b 100644 --- a/packages/atom/CHANGELOG.md +++ b/packages/atom/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [1.5.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/atom@1.4.7...@thi.ng/atom@1.5.0) (2018-08-27) + + +### Features + +* **atom:** add .value accessor aliases (for deref()/reset()) ([a0cbd2b](https://github.com/thi-ng/umbrella/commit/a0cbd2b)) + + + + ## [1.4.7](https://github.com/thi-ng/umbrella/compare/@thi.ng/atom@1.4.6...@thi.ng/atom@1.4.7) (2018-08-24) diff --git a/packages/atom/package.json b/packages/atom/package.json index 38788878d4..43bb6a58ef 100644 --- a/packages/atom/package.json +++ b/packages/atom/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/atom", - "version": "1.4.7", + "version": "1.5.0", "description": "Mutable wrapper for immutable values", "main": "./index.js", "typings": "./index.d.ts", diff --git a/packages/atom/src/atom.ts b/packages/atom/src/atom.ts index 90e7549118..888d2226cc 100644 --- a/packages/atom/src/atom.ts +++ b/packages/atom/src/atom.ts @@ -20,7 +20,7 @@ export class Atom implements IAtom, IEquiv { - protected value: T; + protected _value: T; protected valid: Predicate; protected _watches: any; @@ -28,12 +28,20 @@ export class Atom implements if (valid && !valid(val)) { illegalState("initial state value did not validate"); } - this.value = val; + this._value = val; this.valid = valid; } + get value() { + return this._value; + } + + set value(val: T) { + this.reset(val); + } + deref() { - return this.value; + return this._value; } equiv(o: any) { @@ -41,25 +49,25 @@ export class Atom implements } reset(val: T) { - const old = this.value; + const old = this._value; if (this.valid && !this.valid(val)) { return old; } - this.value = val; + this._value = val; this.notifyWatches(old, val); return val; } resetIn(path: Path, val: V) { - return this.reset(setIn(this.value, path, val)); + return this.reset(setIn(this._value, path, val)); } swap(fn: SwapFn, ...args: any[]) { - return this.reset(fn.apply(null, [this.value, ...args])); + return this.reset(fn.apply(null, [this._value, ...args])); } swapIn(path: Path, fn: SwapFn, ...args: any[]) { - return this.reset(updateIn(this.value, path, fn, ...args)); + return this.reset(updateIn(this._value, path, fn, ...args)); } // mixin stub @@ -84,7 +92,7 @@ export class Atom implements release() { delete this._watches; - delete this.value; + delete this._value; return true; } } diff --git a/packages/atom/src/cursor.ts b/packages/atom/src/cursor.ts index e873b7d3b4..04e74535d7 100644 --- a/packages/atom/src/cursor.ts +++ b/packages/atom/src/cursor.ts @@ -112,6 +112,14 @@ export class Cursor implements }); } + get value() { + return this.deref(); + } + + set value(val: T) { + this.reset(val); + } + deref() { return this.local.deref(); } diff --git a/packages/atom/src/history.ts b/packages/atom/src/history.ts index 97a1b9a52e..fe473c38ce 100644 --- a/packages/atom/src/history.ts +++ b/packages/atom/src/history.ts @@ -53,6 +53,14 @@ export class History implements this.clear(); } + get value() { + return this.deref(); + } + + set value(val: T) { + this.reset(val); + } + canUndo() { return this.history.length > 0; } diff --git a/packages/atom/src/view.ts b/packages/atom/src/view.ts index 20bfee3485..d0ae30f58c 100644 --- a/packages/atom/src/view.ts +++ b/packages/atom/src/view.ts @@ -88,6 +88,10 @@ export class View implements }); } + get value() { + return this.deref(); + } + /** * Returns view's value. If the view has a transformer, the * transformed value is returned. The transformer is only run once diff --git a/packages/bench/CHANGELOG.md b/packages/bench/CHANGELOG.md index 78a5a0e3e0..6b30f759bc 100644 --- a/packages/bench/CHANGELOG.md +++ b/packages/bench/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [0.2.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/bench@0.1.5...@thi.ng/bench@0.2.0) (2018-08-28) + + +### Features + +* **bench:** add opt prefix arg, update docs ([4a37367](https://github.com/thi-ng/umbrella/commit/4a37367)) + + + + ## [0.1.5](https://github.com/thi-ng/umbrella/compare/@thi.ng/bench@0.1.4...@thi.ng/bench@0.1.5) (2018-08-01) diff --git a/packages/bench/package.json b/packages/bench/package.json index cfb9dabf21..63ede8a722 100644 --- a/packages/bench/package.json +++ b/packages/bench/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/bench", - "version": "0.1.5", + "version": "0.2.0", "description": "Basic benchmarking helpers", "main": "./index.js", "typings": "./index.d.ts", diff --git a/packages/bench/src/index.ts b/packages/bench/src/index.ts index d1dfe22447..370d9109f4 100644 --- a/packages/bench/src/index.ts +++ b/packages/bench/src/index.ts @@ -1,29 +1,35 @@ /** * Calls function `fn` without args, prints elapsed time and returns - * fn's result. + * fn's result. The optional `prefix` will be displayed with the output, + * allowing to label different measurements. * * @param fn + * @param prefix */ -export function timed(fn: () => T) { +export function timed(fn: () => T, prefix = "") { const t0 = Date.now(); const res = fn(); - console.log(Date.now() - t0); + console.log(prefix + (Date.now() - t0) + "ms"); return res; } /** * Executes given function `n` times, prints elapsed time to console and - * returns last result from fn. + * returns last result from fn. The optional `prefix` will be displayed + * with the output, allowing to label different measurements. * * @param fn * @param n */ -export function bench(fn: () => T, n = 1e6) { +export function bench(fn: () => T, n = 1e6, prefix = "") { let res: T; - return timed(() => { - while (n-- > 0) { - res = fn(); - } - return res; - }); + return timed( + () => { + while (n-- > 0) { + res = fn(); + } + return res; + }, + prefix + ); } diff --git a/packages/cache/CHANGELOG.md b/packages/cache/CHANGELOG.md index f71c28c2f2..e0b09622f6 100644 --- a/packages/cache/CHANGELOG.md +++ b/packages/cache/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.2.19](https://github.com/thi-ng/umbrella/compare/@thi.ng/cache@0.2.18...@thi.ng/cache@0.2.19) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/cache + + +## [0.2.18](https://github.com/thi-ng/umbrella/compare/@thi.ng/cache@0.2.17...@thi.ng/cache@0.2.18) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/cache + ## [0.2.17](https://github.com/thi-ng/umbrella/compare/@thi.ng/cache@0.2.16...@thi.ng/cache@0.2.17) (2018-08-24) diff --git a/packages/cache/README.md b/packages/cache/README.md index c63796d511..bf2d9cc442 100644 --- a/packages/cache/README.md +++ b/packages/cache/README.md @@ -34,8 +34,7 @@ yarn add @thi.ng/cache - [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/master/packages/api) - [@thi.ng/dcons](https://github.com/thi-ng/umbrella/tree/master/packages/dcons) -- [@thi.ng/equiv](https://github.com/thi-ng/umbrella/tree/master/packages/equiv) -- [@thi.ng/iterators](https://github.com/thi-ng/umbrella/tree/master/packages/iterators) +- [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/master/packages/transducers) ## Usage examples diff --git a/packages/cache/package.json b/packages/cache/package.json index f0381899a5..a6becfdc31 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/cache", - "version": "0.2.17", + "version": "0.2.19", "description": "In-memory cache implementations with ES6 Map-like API and different eviction strategies", "main": "./index.js", "typings": "./index.d.ts", @@ -29,8 +29,8 @@ }, "dependencies": { "@thi.ng/api": "^4.1.0", - "@thi.ng/dcons": "^1.1.1", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/dcons": "^1.1.2", + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "cache", diff --git a/packages/cache/src/lru.ts b/packages/cache/src/lru.ts index f937a2d199..4c03175270 100644 --- a/packages/cache/src/lru.ts +++ b/packages/cache/src/lru.ts @@ -39,16 +39,16 @@ export class LRUCache implements ICache { return this.entries(); } - *entries(): IterableIterator]>> { - yield* map((e) => <[K, CacheEntry]>[e.k, e], this.items); + entries(): IterableIterator]>> { + return map((e) => <[K, CacheEntry]>[e.k, e], this.items); } - *keys(): IterableIterator> { - yield* map((e) => e.k, this.items); + keys(): IterableIterator> { + return map((e) => e.k, this.items); } - *values(): IterableIterator> { - yield* map((e) => e.v, this.items); + values(): IterableIterator> { + return map((e) => e.v, this.items); } copy(): ICache { diff --git a/packages/compose/CHANGELOG.md b/packages/compose/CHANGELOG.md index f99a8b6b78..9ed8d6839d 100644 --- a/packages/compose/CHANGELOG.md +++ b/packages/compose/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.1.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/compose@0.1.0...@thi.ng/compose@0.1.1) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/compose + # 0.1.0 (2018-08-24) diff --git a/packages/compose/README.md b/packages/compose/README.md index bd0a07b06f..08bcb9efc7 100644 --- a/packages/compose/README.md +++ b/packages/compose/README.md @@ -9,6 +9,7 @@ This project is part of the - [About](#about) - [Installation](#installation) +- [Dependencies](#dependencies) - [Usage examples](#usage-examples) - [Authors](#authors) - [License](#license) @@ -29,6 +30,11 @@ Functional composition helpers: yarn add @thi.ng/compose ``` +## Dependencies + +- [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/master/packages/api) +- [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/master/packages/errors) + ## Usage examples ```ts diff --git a/packages/compose/package.json b/packages/compose/package.json index abaa8daa6f..0c15ad06c0 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/compose", - "version": "0.1.0", + "version": "0.1.1", "description": "TODO", "main": "./index.js", "typings": "./index.d.ts", diff --git a/packages/csp/CHANGELOG.md b/packages/csp/CHANGELOG.md index 15be8c8c70..21670205a1 100644 --- a/packages/csp/CHANGELOG.md +++ b/packages/csp/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.3.58](https://github.com/thi-ng/umbrella/compare/@thi.ng/csp@0.3.57...@thi.ng/csp@0.3.58) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/csp + ## [0.3.57](https://github.com/thi-ng/umbrella/compare/@thi.ng/csp@0.3.56...@thi.ng/csp@0.3.57) (2018-08-24) diff --git a/packages/csp/package.json b/packages/csp/package.json index 3c5373942c..fd76b161eb 100644 --- a/packages/csp/package.json +++ b/packages/csp/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/csp", - "version": "0.3.57", + "version": "0.3.58", "description": "ES6 promise based CSP implementation", "main": "./index.js", "typings": "./index.d.ts", @@ -34,9 +34,9 @@ "dependencies": { "@thi.ng/api": "^4.1.0", "@thi.ng/checks": "^1.5.7", - "@thi.ng/dcons": "^1.1.1", + "@thi.ng/dcons": "^1.1.2", "@thi.ng/errors": "^0.1.6", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "async", diff --git a/packages/dcons/CHANGELOG.md b/packages/dcons/CHANGELOG.md index 6c23b1c57d..a86da46ee7 100644 --- a/packages/dcons/CHANGELOG.md +++ b/packages/dcons/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.1.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/dcons@1.1.1...@thi.ng/dcons@1.1.2) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/dcons + ## [1.1.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/dcons@1.1.0...@thi.ng/dcons@1.1.1) (2018-08-24) diff --git a/packages/dcons/README.md b/packages/dcons/README.md index 1d72e193ea..7f28690af9 100644 --- a/packages/dcons/README.md +++ b/packages/dcons/README.md @@ -39,6 +39,7 @@ yarn add @thi.ng/dcons - [@thi.ng/compare](https://github.com/thi-ng/umbrella/tree/master/packages/compare) - [@thi.ng/equiv](https://github.com/thi-ng/umbrella/tree/master/packages/equiv) - [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/master/packages/errors) +- [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/master/packages/transducers) ## Usage diff --git a/packages/dcons/package.json b/packages/dcons/package.json index da46f84b78..957fda4413 100644 --- a/packages/dcons/package.json +++ b/packages/dcons/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/dcons", - "version": "1.1.1", + "version": "1.1.2", "description": "Comprehensive doubly linked list structure w/ iterator support", "main": "./index.js", "typings": "./index.d.ts", @@ -33,7 +33,7 @@ "@thi.ng/compare": "^0.1.6", "@thi.ng/equiv": "^0.1.7", "@thi.ng/errors": "^0.1.6", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "datastructure", diff --git a/packages/dgraph/CHANGELOG.md b/packages/dgraph/CHANGELOG.md index 1c3f7bb74e..6aea233e59 100644 --- a/packages/dgraph/CHANGELOG.md +++ b/packages/dgraph/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.2.14](https://github.com/thi-ng/umbrella/compare/@thi.ng/dgraph@0.2.13...@thi.ng/dgraph@0.2.14) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/dgraph + ## [0.2.13](https://github.com/thi-ng/umbrella/compare/@thi.ng/dgraph@0.2.12...@thi.ng/dgraph@0.2.13) (2018-08-24) diff --git a/packages/dgraph/README.md b/packages/dgraph/README.md index a5ea007034..2a7aa01601 100644 --- a/packages/dgraph/README.md +++ b/packages/dgraph/README.md @@ -27,7 +27,7 @@ yarn add @thi.ng/dgraph - [@thi.ng/associative](https://github.com/thi-ng/umbrella/tree/master/packages/associative) - [@thi.ng/equiv](https://github.com/thi-ng/umbrella/tree/master/packages/equiv) - [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/master/packages/errors) -- [@thi.ng/iterators](https://github.com/thi-ng/umbrella/tree/master/packages/iterators) +- [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/master/packages/transducers) ## Usage examples diff --git a/packages/dgraph/package.json b/packages/dgraph/package.json index 5407b22c52..28b217eeb3 100644 --- a/packages/dgraph/package.json +++ b/packages/dgraph/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/dgraph", - "version": "0.2.13", + "version": "0.2.14", "description": "Type-agnostic directed acyclic graph (DAG) & graph operations", "main": "./index.js", "typings": "./index.d.ts", @@ -29,10 +29,10 @@ }, "dependencies": { "@thi.ng/api": "^4.1.0", - "@thi.ng/associative": "^0.6.1", + "@thi.ng/associative": "^0.6.2", "@thi.ng/equiv": "^0.1.7", "@thi.ng/errors": "^0.1.6", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "data structure", diff --git a/packages/hdom-components/CHANGELOG.md b/packages/hdom-components/CHANGELOG.md index 37b53729fb..6101bdc11d 100644 --- a/packages/hdom-components/CHANGELOG.md +++ b/packages/hdom-components/CHANGELOG.md @@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [2.2.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/hdom-components@2.2.0...@thi.ng/hdom-components@2.2.1) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/hdom-components + + +# [2.2.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/hdom-components@2.1.13...@thi.ng/hdom-components@2.2.0) (2018-08-27) + + +### Bug Fixes + +* **hdom-components:** call canvas update from init() ([b25edbe](https://github.com/thi-ng/umbrella/commit/b25edbe)) + + +### Features + +* **hdom-components:** add HDPI adaptation helper for canvas comps ([135d6f1](https://github.com/thi-ng/umbrella/commit/135d6f1)) + + + + ## [2.1.13](https://github.com/thi-ng/umbrella/compare/@thi.ng/hdom-components@2.1.12...@thi.ng/hdom-components@2.1.13) (2018-08-24) diff --git a/packages/hdom-components/README.md b/packages/hdom-components/README.md index 16c08e8d0c..115ac685f9 100644 --- a/packages/hdom-components/README.md +++ b/packages/hdom-components/README.md @@ -35,7 +35,7 @@ yarn add @thi.ng/hdom-components - [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/master/packages/api) - [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/master/packages/checks) -- [@thi.ng/iterators](https://github.com/thi-ng/umbrella/tree/master/packages/iterators) +- [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/master/packages/transducers) ## Usage examples diff --git a/packages/hdom-components/package.json b/packages/hdom-components/package.json index c6ebd07c01..e5a0fe915b 100644 --- a/packages/hdom-components/package.json +++ b/packages/hdom-components/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/hdom-components", - "version": "2.1.13", + "version": "2.2.1", "description": "Raw, skinnable UI & SVG components for @thi.ng/hdom", "main": "./index.js", "typings": "./index.d.ts", @@ -30,7 +30,7 @@ "dependencies": { "@thi.ng/api": "^4.1.0", "@thi.ng/checks": "^1.5.7", - "@thi.ng/transducers": "^2.0.1", + "@thi.ng/transducers": "^2.0.2", "@types/webgl2": "^0.0.4" }, "keywords": [ diff --git a/packages/hdom-components/src/canvas.ts b/packages/hdom-components/src/canvas.ts index b836455f5c..8e5adca876 100644 --- a/packages/hdom-components/src/canvas.ts +++ b/packages/hdom-components/src/canvas.ts @@ -1,23 +1,28 @@ +export type CanvasContext = + CanvasRenderingContext2D | + WebGLRenderingContext | + WebGL2RenderingContext; + /** * User provided canvas life cycle methods. These differ from the usual * @thi.ng/hdom life cycle methods and are always passed at least the * canvas DOM element, canvas context and hdom user context. Not all * handlers need to be implemented. */ -export interface CanvasHandlers { +export interface CanvasHandlers { /** * user init handler (called only once when canvas first) */ - init: (el: HTMLCanvasElement, gl: T, hctx?: any, ...args: any[]) => void; + init: (el: HTMLCanvasElement, ctx: T, hctx?: any, ...args: any[]) => void; /** * update handler (called for each hdom update iteration) */ - update: (el: HTMLCanvasElement, gl: T, hctx?: any, time?: number, frame?: number, ...args: any[]) => void; + update: (el: HTMLCanvasElement, ctx: T, hctx?: any, time?: number, frame?: number, ...args: any[]) => void; /** * release handler (called only once when canvas element is removed * from DOM) */ - release: (el: HTMLCanvasElement, gl: T, hctx?: any, ...args: any[]) => void; + release: (el: HTMLCanvasElement, ctx: T, hctx?: any, ...args: any[]) => void; } /** @@ -35,9 +40,11 @@ const _canvas = (type, { init, update, release }: Partial>, return { init(_el: HTMLCanvasElement, hctx: any, ...args: any[]) { el = _el; + adaptDPI(el, el.width, el.height); ctx = el.getContext(type, opts); time = Date.now(); init && init(el, ctx, hctx, ...args); + update && update(el, ctx, hctx, time, frame++, ...args); }, render(hctx: any, ...args: any[]) { ctx && update && update(el, ctx, hctx, Date.now() - time, frame++, ...args); @@ -98,3 +105,23 @@ export const canvas2D = ( handlers: Partial>, opts?: Canvas2DContextAttributes) => _canvas("2d", handlers, opts); + +/** + * Sets the canvas size to given `width` & `height` and adjusts style to + * compensate for HDPI devices. Note: For 2D canvases, this will + * automatically clear any prior canvas content. + * + * @param canvas + * @param width uncompensated pixel width + * @param height uncompensated pixel height + */ +export const adaptDPI = (canvas: HTMLCanvasElement, width: number, height: number) => { + const dpr = window.devicePixelRatio || 1; + if (dpr != 1) { + canvas.style.width = `${width}px`; + canvas.style.height = `${height}px`; + } + canvas.width = width * dpr; + canvas.height = height * dpr; + return dpr; +}; diff --git a/packages/hdom/CHANGELOG.md b/packages/hdom/CHANGELOG.md index 204b12d3e3..fa944eff34 100644 --- a/packages/hdom/CHANGELOG.md +++ b/packages/hdom/CHANGELOG.md @@ -3,6 +3,39 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [4.0.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/hdom@4.0.0...@thi.ng/hdom@4.0.1) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/hdom + + +# [4.0.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/hdom@3.0.35...@thi.ng/hdom@4.0.0) (2018-08-31) + + +### Features + +* **hdom:** add DOM hydration support (SSR), update start() ([#39](https://github.com/thi-ng/umbrella/issues/39)) ([9f8010d](https://github.com/thi-ng/umbrella/commit/9f8010d)) +* **hdom:** update HDOMOpts & start() ([5e74a9c](https://github.com/thi-ng/umbrella/commit/5e74a9c)) + + +### BREAKING CHANGES + +* **hdom:** start() args now as options object + + + + + +## [3.0.35](https://github.com/thi-ng/umbrella/compare/@thi.ng/hdom@3.0.34...@thi.ng/hdom@3.0.35) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/hdom + ## [3.0.34](https://github.com/thi-ng/umbrella/compare/@thi.ng/hdom@3.0.33...@thi.ng/hdom@3.0.34) (2018-08-24) diff --git a/packages/hdom/README.md b/packages/hdom/README.md index 85f7a5ba30..b3056471cb 100644 --- a/packages/hdom/README.md +++ b/packages/hdom/README.md @@ -8,7 +8,20 @@ This project is part of the - [About](#about) - - [Minimal example](#minimal-example) + - [Minimal example #1: Local state, RAF update](#minimal-example-1-local-state-raf-update) + - [Minimal example #2 (reactive state & transducer update)](#minimal-example-2-reactive-state--transducer-update) +- [Example projects](#example-projects) + - [Realtime crypto candle chart](#realtime-crypto-candle-chart) + - [Git commit log table](#git-commit-log-table) + - [Interactive SVG grid generator](#interactive-svg-grid-generator) + - [Interactive additive waveform visualization & SVG visualization](#interactive-additive-waveform-visualization--svg-visualization) + - [Dataflow graph SVG components](#dataflow-graph-svg-components) + - [Canvas based radial dial input widget](#canvas-based-radial-dial-input-widget) + - [SPA with router and event bus](#spa-with-router-and-event-bus) + - [Multiple apps with & without shared state](#multiple-apps-with--without-shared-state) + - [Interceptor based event handling](#interceptor-based-event-handling) + - [Todo list (w/ undo/redo)](#todo-list-w-undoredo) + - [SVG particles](#svg-particles) - [Component tree translation](#component-tree-translation) - [Event & state handling options](#event--state-handling-options) - [Reusable components](#reusable-components) @@ -18,20 +31,6 @@ This project is part of the - [Usage](#usage) - [User context injection](#user-context-injection) - [Component objects & life cycle methods](#component-objects--life-cycle-methods) -- [Example projects](#example-projects) - - [Interactive SVG grid generator](#interactive-svg-grid-generator) - - [Interactive additive waveform visualization](#interactive-additive-waveform-visualization) - - [Dataflow graph SVG components](#dataflow-graph-svg-components) - - [SPA with router and event bus](#spa-with-router-and-event-bus) - - [Additive waveform synthesis & SVG visualization](#additive-waveform-synthesis--svg-visualization) - - [Multiple apps with & without shared state](#multiple-apps-with--without-shared-state) - - [Interceptor based event handling](#interceptor-based-event-handling) - - [Todo list](#todo-list) - - [Cellular automata](#cellular-automata) - - [SVG particles](#svg-particles) - - [JSON based components](#json-based-components) - - [@thi.ng/rstream dataflow graph](#thingrstream-dataflow-graph) - - [Basic usage patterns](#basic-usage-patterns) - [Benchmark](#benchmark) - [Authors](#authors) - [License](#license) @@ -40,10 +39,12 @@ This project is part of the ## About -Lightweight reactive DOM components & VDOM implementation using only +Lightweight reactive DOM components & VDOM-ish implementation using only vanilla JS data structures (arrays, objects with life cycle functions, closures, iterators), based on [@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/master/packages/hiccup). +hdom is *very* flexible and supports many different workflows and means +to perform DOM updates... - Use the full expressiveness of ES6 / TypeScript to define, annotate & document components @@ -52,19 +53,22 @@ closures, iterators), based on - [Supports SVG](https://github.com/thi-ng/umbrella/tree/master/packages/hiccup-svg), arbitrary elements, attributes, events +- Suitable for server side rendering (by passing the same data structure + to @thi.ng/hiccup's `serialize()`) and then "hydrating" listeners and + components with life cycle methods - Less verbose than HTML / JSX, resulting in smaller file sizes - Static components can be distributed as JSON (or [transform JSON into components](https://github.com/thi-ng/umbrella/tree/master/examples/json-components)) - Optional user context injection (an arbitrary object passed to all component functions) -- auto-deref of embedded value wrappers which implement the - [@thi.ng/api/IDeref](https://github.com/thi-ng/umbrella/tree/master/packages/api/api) +- CSS conversion from JS objects for `style` attribs (for full + hiccup-based CSS-in-JS generation also see: + [@thi.ng/hiccup-css](https://github.com/thi-ng/umbrella/tree/master/packages/hiccup-css)) +- Auto-deref of embedded value wrappers which implement the + [`IDeref`](https://github.com/thi-ng/umbrella/tree/master/packages/api/api) interface (e.g. atoms, cursors, derived views, streams etc.) -- CSS conversion from JS objects for `style` attribs -- Suitable for server side rendering (by passing the same data structure - to @thi.ng/hiccup's `serialize()`) - Fairly fast (see benchmark example below) -- Only ~4.4KB gzipped +- Only ~5KB gzipped In addition to the descriptions in this file, [further information and examples are available in the @@ -74,7 +78,7 @@ Also see the [work-in-progress ADRs](https://github.com/thi-ng/umbrella/tree/master/packages/hdom-components/adr/) for component configuration. -### Minimal example +### Minimal example #1: Local state, RAF update ```ts import * as hdom from "@thi.ng/hdom"; @@ -97,7 +101,7 @@ const app = () => { }; // start update loop (browser only, see diagram below) -hdom.start(document.body, app()); +hdom.start(app(), { root: document.body }); // alternatively apply DOM tree only once // (stateful components won't update though) @@ -118,11 +122,139 @@ console.log(serialize(app())); //

hello world

``` -No template engine & no pre-compilation steps needed, just use the full -expressiveness of ES6/TypeScript to define your DOM tree. Using -TypeScript gives the additional benefit of making UI components strongly -typed, and since they're just normal functions, can use generics, -overrides, varargs etc. +### Minimal example #2 (reactive state & transducer update) + +This example uses the +[@thi.ng/transducers-hdom](https://github.com/thi-ng/umbrella/tree/master/packages/transducers-hdom) +support library to perform reactive DOM updates (instead of regular +diffing via RAF). + +```ts +import * as rs from "@thi.ng/rstream/stream"; +import * as tx from "@thi.ng/rstream/transducers"; +import { updateDOM } from "@thi.ng/rstream/transducers-hdom"; + +// root component function +const app = ({ ticks, clicks }) => + ["div", + `${ticks} ticks & `, + ["a", + { href: "#", onclick: () => clickStream.next(0)}, + `${clicks} clicks`] + ]; + +// click stream (click counter) +const clickStream = rs.stream().transform(tx.scan(tx.count(-1))); + +// stream combinator +// waits until all inputs have produced at least one value, +// then updates whenever any input has changed +rs.sync({ + // streams to synchronize + src: { + ticks: rs.fromInterval(1000), + clicks: clickStream, + }, +}).transform( + // transform tuple into hdom component + tx.map(app), + // apply hdom tree to real DOM + updateDOM({ root: document.body }) +); + +// kick off +clickStream.next(0); +``` + +[Live demo](https://demo.thi.ng/umbrella/transducers-hdom/) | +[standalone example](https://github.com/thi-ng/umbrella/tree/master/examples/transducers-hdom) + +## Example projects + +Most of the 25 +[examples](https://github.com/thi-ng/umbrella/tree/master/examples) +included in this repo are using this package in one way or another. +Please check them out to learn more. Each is heavily commented, incl. +best practice notes. + +Non-exhaustive list: + +### Realtime crypto candle chart + +![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/screenshots/crypto-chart.png) + +[Source](https://github.com/thi-ng/umbrella/tree/master/examples/crypto-chart) | +[Live version](https://demo.thi.ng/umbrella/crypto-chart/) + +### Git commit log table + +![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/screenshots/commit-table-ssr.png) + +[Source](https://github.com/thi-ng/umbrella/tree/master/examples/commit-table-ssr) | +[Live version](https://demo.thi.ng/umbrella/commit-table-ssr/) + +### Interactive SVG grid generator + +![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/screenshots/rstream-grid.png) + +[Source](https://github.com/thi-ng/umbrella/tree/master/examples/rstream-grid) | +[Live version](https://demo.thi.ng/umbrella/rstream-grid/) + +### Interactive additive waveform visualization & SVG visualization + +![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/screenshots/svg-waveform.png) + +[Source](https://github.com/thi-ng/umbrella/tree/master/examples/svg-waveform) | +[Live version](https://demo.thi.ng/umbrella/svg-waveform/) + +### Dataflow graph SVG components + +![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/screenshots/estuary.png) + +This is a preview of the WIP +[@thi.ng/estuary](https://github.com/thi-ng/umbrella/tree/feature/estuary/packages/estuary) +package: + +[Source](https://github.com/thi-ng/umbrella/tree/feature/estuary/packages/estuary) +| [Live version](https://demo.thi.ng/umbrella/estuary/) + +### Canvas based radial dial input widget + +![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/screenshots/canvas-dial.png) + +[Source](https://github.com/thi-ng/umbrella/tree/master/examples/canvas-dial) | +[Live version](https://demo.thi.ng/umbrella/canvas-dial/) + +### SPA with router and event bus + +Based on the `create-hdom-app` project scaffolding, this is one of the +more advanced demos, combining functionality of several other @thi.ng +packages. + +[Source](https://github.com/thi-ng/umbrella/tree/master/examples/router-basics) +| [Live version](https://demo.thi.ng/umbrella/router-basics/) + +### Multiple apps with & without shared state + +Devcards style BMI calculator(s) with basic SVG viz. + +[Source](https://github.com/thi-ng/umbrella/tree/master/examples/devcards) +| [Live version](https://demo.thi.ng/umbrella/devcards/) + +### Interceptor based event handling + +[Source](https://github.com/thi-ng/umbrella/tree/master/examples/interceptor-basics) +| [Live version](https://demo.thi.ng/umbrella/interceptor-basics/) + +### Todo list (w/ undo/redo) + +[Source](https://github.com/thi-ng/umbrella/tree/master/examples/todo-list) +| [Live version](https://demo.thi.ng/umbrella/todo-list/) + +### SVG particles + +[Source](https://github.com/thi-ng/umbrella/tree/master/examples/svg-particles) +| [Live version](https://demo.thi.ng/umbrella/svg-particles/) ### Component tree translation @@ -132,6 +264,9 @@ JS arrays). Components can be defined as static arrays, closures or objects with [life cycle methods](#lifecycle-methods) (init, render, release). +**Note: hdom uses a RAF render loop only by default, but is absolutely +no way tied to this.** + ![hdom dataflow](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/hdom-dataflow.png) The syntax is inspired by Clojure's @@ -164,9 +299,8 @@ provided by these packages: ## Status The overall "API" is stable, but there's further work planned on -generalizing the approach beyond standard browser DOM use cases (planned -for v4.0.0). The project has been used for several projects in -production since 2016. +generalizing the approach beyond standard browser DOM use cases. The +project has been used for several projects in production since 2016. ## Installation @@ -194,7 +328,6 @@ yarn start - [@thi.ng/diff](https://github.com/thi-ng/umbrella/tree/master/packages/diff) - [@thi.ng/equiv](https://github.com/thi-ng/umbrella/tree/master/packages/equiv) - [@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/master/packages/hiccup) -- [@thi.ng/iterators](https://github.com/thi-ng/umbrella/tree/master/packages/iterators) ## Usage @@ -205,7 +338,7 @@ reference to learn about the basics of the approach and syntax used. Both projects started in early 2016, have somewhat evolved independently, however should be considered complementary. -#### `start(parent: Element | string, tree: any, ctx?: any, path?: number[], keys?: boolean, span?: boolean): () => boolean` +#### `start(tree: any, opts?: Partial): () => any` Main user function of this package. For most use cases, this function should be the only one required in user code. It takes a parent DOM @@ -229,10 +362,14 @@ previous DOM tree is kept around until the root function returns a tree again, which then is diffed and applied against the previous tree kept as usual. Any number of frames may be skipped this way. -**Important:** The parent element given is assumed to have NO children at -the time when `start()` is called. Since hdom does NOT track the real -DOM, the resulting changes will result in potentially undefined behavior -if the parent element wasn't empty. +**Important:** Unless the `hydrate` option is enabled, the parent +element given is assumed to have NO children at the time when `start()` +is called. Since hdom does NOT track the real DOM, the resulting changes +will result in potentially undefined behavior if the parent element +wasn't empty. Likewise, if `hydrate` is enabled, it is assumed that an +equivalent DOM (minus listeners) already exists (i.e. generated via SSR) +when `start()` is called. Any other discrepancies between the +pre-existing DOM and the hdom trees will cause undefined behavior. Returns a function, which when called, immediately cancels the update loop. @@ -290,6 +427,14 @@ of elements, if the provided tree is an iterable. Creates DOM text nodes for non-component values. Returns `parent` if tree is `null` or `undefined`. +#### `hydrateDOM(parent: Element, tag: any)` + +Takes a DOM root element and normalized hdom tree, then walks tree and +initializes any event listeners and components with lifecycle init +methods. Assumes that an equivalent DOM (minus listeners) already exists +(e.g. generated via SSR) when called. Any other discrepancies between +the pre-existing DOM and the hdom tree will cause undefined behavior. + ### User context injection Since v3.0.0 hdom offers support for an arbitrary "context" object @@ -349,7 +494,7 @@ const root = [ ]; // start hdom update loop -start("app", root, ctx); +start(root, { ctx }); ``` ### Component objects & life cycle methods @@ -421,8 +566,8 @@ const canvas = () => { // usage scenario #1: static component // inline initialization is okay here... start( - document.body, - [canvas(), { width: 100, height: 100 }, "Hello world"] + [canvas(), { width: 100, height: 100 }, "Hello world"], + { root: document.body } ); @@ -445,89 +590,9 @@ const app = () => { ]; }; -start(document.body, app()); +start(app(), { root: document.body }); ``` -## Example projects - -Most of the -[examples](https://github.com/thi-ng/umbrella/tree/master/examples) -included in this repo are using this package in one way or another. -Please check them out to learn more. Each is heavily commented, incl. -best practice notes. - -Non-exhaustive list: - -### Interactive SVG grid generator - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/rstream-grid) | -[Live version](https://demo.thi.ng/umbrella/rstream-grid/) - -### Interactive additive waveform visualization - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/svg-waveform) | -[Live version](https://demo.thi.ng/umbrella/svg-waveform/) - -### Dataflow graph SVG components - -This is a preview of the upcoming -[@thi.ng/estuary](https://github.com/thi-ng/umbrella/tree/feature/estuary/packages/estuary) -package: - -[Source](https://github.com/thi-ng/umbrella/tree/feature/estuary/packages/estuary) | [Live version](https://demo.thi.ng/umbrella/estuary/) - -### SPA with router and event bus - -Based on the `create-hdom-app` project scaffolding, this is one of the -more advanced demos, combining functionality of several other @thi.ng -packages. - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/router-basics) | [Live version](https://demo.thi.ng/umbrella/router-basics/) - -### Additive waveform synthesis & SVG visualization - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/svg-waveform) | [Live version](https://demo.thi.ng/umbrella/svg-waveform/) - -### Multiple apps with & without shared state - -Devcards style BMI calculator(s) with basic SVG viz. - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/devcards) | [Live version](https://demo.thi.ng/umbrella/devcards/) - -### Interceptor based event handling - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/interceptor-basics) | [Live version](https://demo.thi.ng/umbrella/interceptor-basics/) - -### Todo list - -A fully documented, obligatory todo list app with undo / redo. - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/todo-list) | [Live version](https://demo.thi.ng/umbrella/todo-list/) - -### Cellular automata - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/cellular-automata) | [Live version](https://demo.thi.ng/umbrella/cellular-automata/) - -### SVG particles - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/svg-particles) | [Live version](https://demo.thi.ng/umbrella/svg-particles/) - -### JSON based components - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/json-components) | [Live version](https://demo.thi.ng/umbrella/json-components/) - -### @thi.ng/rstream dataflow graph - -A small, interactive dataflow graph example: - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/rstream-dataflow) | [Live version](https://demo.thi.ng/umbrella/rstream-dataflow) - -### Basic usage patterns - -The code below is also available as standalone project in: [/examples/dashboard](https://github.com/thi-ng/umbrella/tree/master/examples/dashboard) - -[Source](https://github.com/thi-ng/umbrella/tree/master/examples/dashboard) | [Live version](https://demo.thi.ng/umbrella/dashboard/) - ### Benchmark A stress test benchmark is here: @@ -540,8 +605,8 @@ Twitter](https://twitter.com/toxi/status/959246871339454464), performance should be more than acceptable for even quite demanding UIs. In the 192 / 256 cells configurations **this stress test causes approx. 600 / 800 DOM every single frame**, very unlikely for a typical web app. -In Chrome 64 on a MBP2016 this still runs at a stable 60fps (192 cells) -/ 32fps (256 cells). Both FPS readings based the 50 frame +In Chrome 68 on a MBP2016 this still runs at a stable 60fps (192 cells) +/ 37fps (256 cells). Both FPS readings based the 50 frame [SMA](https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average). ## Authors diff --git a/packages/hdom/package.json b/packages/hdom/package.json index de3a3af50c..caa7da861a 100644 --- a/packages/hdom/package.json +++ b/packages/hdom/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/hdom", - "version": "3.0.34", + "version": "4.0.1", "description": "Lightweight vanilla ES6 UI component & virtual DOM system", "main": "./index.js", "typings": "./index.d.ts", @@ -20,7 +20,7 @@ "test": "rm -rf build && tsc -p test && nyc mocha build/test/*.js" }, "devDependencies": { - "@thi.ng/atom": "^1.4.7", + "@thi.ng/atom": "^1.5.0", "@types/mocha": "^5.2.5", "@types/node": "^10.5.5", "mocha": "^5.2.0", @@ -33,7 +33,7 @@ "@thi.ng/checks": "^1.5.7", "@thi.ng/diff": "^1.0.22", "@thi.ng/equiv": "^0.1.7", - "@thi.ng/hiccup": "^2.0.10" + "@thi.ng/hiccup": "^2.1.0" }, "keywords": [ "browser", diff --git a/packages/hdom/src/api.ts b/packages/hdom/src/api.ts index 7d32a1e5fd..461285df3d 100644 --- a/packages/hdom/src/api.ts +++ b/packages/hdom/src/api.ts @@ -16,4 +16,37 @@ export interface ComponentAttribs { [_: string]: any; } +export interface HDOMOpts { + /** + * Root element or ID (default: "app"). + */ + root: Element | string; + /** + * Arbitrary user context object, passed to all component functions + * embedded in the tree. + */ + ctx?: any; + /** + * If true (default), text content will be wrapped in `` + */ + span?: boolean; + /** + * If true (default false), the first frame will only be used to + * inject event listeners. + * + * *Important:* Enabling this option assumes that an equivalent DOM + * (minus listeners) already exists (i.e. generated via SSR) when + * hdom's `start()` function is called. Any other discrepancies + * between the pre-existing DOM and the hdom trees will cause + * undefined behavior. + */ + hydrate?: boolean; + /** + * If true (default), the hdom component tree will be first + * normalized before diffing (using `normalizeTree()`). Unless you + * know what you're doing, it's best to leave this enabled. + */ + normalize?: boolean; +} + export const DEBUG = false; diff --git a/packages/hdom/src/diff.ts b/packages/hdom/src/diff.ts index 9195b9fa2e..50f4df0dab 100644 --- a/packages/hdom/src/diff.ts +++ b/packages/hdom/src/diff.ts @@ -34,11 +34,10 @@ const isString = iss.isString; * @param prev previous tree * @param curr current tree */ -export function diffElement(root: Element, prev: any, curr: any) { +export const diffElement = (root: Element, prev: any, curr: any) => _diffElement(root, prev, curr, 0); -} -function _diffElement(parent: Element, prev: any, curr: any, child: number) { +const _diffElement = (parent: Element, prev: any, curr: any, child: number) => { const delta = diffArray(prev, curr, equiv, true); if (delta.distance === 0) { return; @@ -109,9 +108,9 @@ function _diffElement(parent: Element, prev: any, curr: any, child: number) { // DEBUG && console.log("call __init", curr); i.apply(curr, [el, ...(curr.__args)]); } -} +}; -function releaseDeep(tag: any) { +const releaseDeep = (tag: any) => { if (isArray(tag)) { if ((tag).__release) { // DEBUG && console.log("call __release", tag); @@ -122,9 +121,9 @@ function releaseDeep(tag: any) { releaseDeep(tag[i]); } } -} +}; -function diffAttributes(el: Element, prev: any, curr: any) { +const diffAttributes = (el: Element, prev: any, curr: any) => { let i, e, edits; const delta = diffObject(prev, curr); removeAttribs(el, delta.dels, prev); @@ -152,9 +151,9 @@ function diffAttributes(el: Element, prev: any, curr: any) { if (value !== SEMAPHORE) { setAttrib(el, "value", value, curr); } -} +}; -function extractEquivElements(edits: DiffLogEntry[]) { +const extractEquivElements = (edits: DiffLogEntry[]) => { let k, v, e, ek; const equiv = {}; for (let i = edits.length; --i >= 0;) { @@ -167,4 +166,4 @@ function extractEquivElements(edits: DiffLogEntry[]) { } } return equiv; -} +}; diff --git a/packages/hdom/src/dom.ts b/packages/hdom/src/dom.ts index d9706d9d20..bc06b5b1ea 100644 --- a/packages/hdom/src/dom.ts +++ b/packages/hdom/src/dom.ts @@ -23,7 +23,7 @@ const isString = iss.isString; * @param tag * @param insert */ -export function createDOM(parent: Element, tag: any, insert?: number) { +export const createDOM = (parent: Element, tag: any, insert?: number) => { if (isArray(tag)) { const t = tag[0]; if (isFunction(t)) { @@ -52,9 +52,47 @@ export function createDOM(parent: Element, tag: any, insert?: number) { return parent; } return createTextElement(parent, tag); -} +}; -export function createElement(parent: Element, tag: string, attribs?: any, insert?: number) { +/** + * Takes a DOM root element and normalized hdom tree, then walks tree + * and initializes any event listeners and components with lifecycle + * `init` methods. Assumes that an equivalent DOM (minus listeners) + * already exists (e.g. generated via SSR) when called. Any other + * discrepancies between the pre-existing DOM and the hdom tree will + * cause undefined behavior. + * + * @param parent + * @param tree + * @param i + */ +export const hydrateDOM = (parent: Element, tree: any, i = 0) => { + if (isArray(tree)) { + const el = parent.children[i]; + if (isFunction(tree[0])) { + return hydrateDOM(parent, tree[0].apply(null, tree.slice(1)), i); + } + if ((tree).__init) { + (tree).__init.apply((tree).__this, [el, ...(tree).__args]); + } + const attr = tree[1]; + for (let a in attr) { + if (a.indexOf("on") === 0) { + el.addEventListener(a.substr(2), attr[a]); + } + } + for (let n = tree.length, i = 2; i < n; i++) { + hydrateDOM(el, tree[i], i - 2); + } + } else if (!isString(tree) && isIterable(tree)) { + for (let t of tree) { + hydrateDOM(parent, t, i); + i++; + } + } +}; + +export const createElement = (parent: Element, tag: string, attribs?: any, insert?: number) => { const el = SVG_TAGS[tag] ? document.createElementNS(SVG_NS, tag) : document.createElement(tag); @@ -69,9 +107,9 @@ export function createElement(parent: Element, tag: string, attribs?: any, inser setAttribs(el, attribs); } return el; -} +}; -export function createTextElement(parent: Element, content: string, insert?: number) { +export const createTextElement = (parent: Element, content: string, insert?: number) => { const el = document.createTextNode(content); if (parent) { if (insert === undefined) { @@ -81,21 +119,21 @@ export function createTextElement(parent: Element, content: string, insert?: num } } return el; -} +}; -export function cloneWithNewAttribs(el: Element, attribs: any) { +export const cloneWithNewAttribs = (el: Element, attribs: any) => { const res = el.cloneNode(true); setAttribs(res, attribs); el.parentNode.replaceChild(res, el); return res; -} +}; -export function setAttribs(el: Element, attribs: any) { +export const setAttribs = (el: Element, attribs: any) => { for (let k in attribs) { setAttrib(el, k, attribs[k], attribs); } return el; -} +}; /** * Sets a single attribute on given element. If attrib name is NOT @@ -118,7 +156,7 @@ export function setAttribs(el: Element, attribs: any) { * @param val * @param attribs */ -export function setAttrib(el: Element, id: string, val: any, attribs?: any) { +export const setAttrib = (el: Element, id: string, val: any, attribs?: any) => { const isListener = id.indexOf("on") === 0; if (!isListener && isFunction(val)) { val = val(attribs); @@ -146,9 +184,9 @@ export function setAttrib(el: Element, id: string, val: any, attribs?: any) { el[id] != null ? (el[id] = null) : el.removeAttribute(id); } return el; -} +}; -export function updateValueAttrib(el: HTMLInputElement, v: any) { +export const updateValueAttrib = (el: HTMLInputElement, v: any) => { switch (el.type) { case "text": case "textarea": @@ -166,9 +204,9 @@ export function updateValueAttrib(el: HTMLInputElement, v: any) { default: el.value = v; } -} +}; -export function removeAttribs(el: Element, attribs: string[], prev: any) { +export const removeAttribs = (el: Element, attribs: string[], prev: any) => { for (let i = attribs.length; --i >= 0;) { const a = attribs[i]; if (a.indexOf("on") === 0) { @@ -177,20 +215,17 @@ export function removeAttribs(el: Element, attribs: string[], prev: any) { el[a] ? (el[a] = null) : el.removeAttribute(a); } } -} +}; -export function setStyle(el: Element, styles: any) { - el.setAttribute("style", css(styles)); - return el; -} +export const setStyle = (el: Element, styles: any) => + (el.setAttribute("style", css(styles)), el); -export function clearDOM(el: Element) { +export const clearDOM = (el: Element) => el.innerHTML = ""; -} -export function removeChild(parent: Element, childIdx: number) { +export const removeChild = (parent: Element, childIdx: number) => { const n = parent.children[childIdx]; if (n !== undefined) { n.remove(); } -} +}; diff --git a/packages/hdom/src/normalize.ts b/packages/hdom/src/normalize.ts index cbda166bb5..725ce30e00 100644 --- a/packages/hdom/src/normalize.ts +++ b/packages/hdom/src/normalize.ts @@ -5,7 +5,7 @@ import * as isi from "@thi.ng/checks/is-iterable"; import * as iso from "@thi.ng/checks/is-plain-object"; import * as iss from "@thi.ng/checks/is-string"; import { illegalArgs } from "@thi.ng/errors/illegal-arguments"; -import { TAG_REGEXP } from "@thi.ng/hiccup/api"; +import { NO_SPANS, TAG_REGEXP } from "@thi.ng/hiccup/api"; const isArray = isa.isArray; const isFunction = isf.isFunction; @@ -38,7 +38,7 @@ const isString = iss.isString; * @param spec * @param keys */ -export function normalizeElement(spec: any[], keys: boolean) { +export const normalizeElement = (spec: any[], keys: boolean) => { let tag = spec[0], hasAttribs = isPlainObject(spec[1]), match, id, clazz, attribs; if (!isString(tag) || !(match = TAG_REGEXP.exec(tag))) { illegalArgs(`${tag} is not a valid tag name`); @@ -62,12 +62,6 @@ export function normalizeElement(spec: any[], keys: boolean) { } } return [match[1], attribs, ...spec.slice(hasAttribs ? 2 : 1)]; -} - -const NO_SPANS = { - option: 1, - text: 1, - textarea: 1, }; /** @@ -111,7 +105,7 @@ const NO_SPANS = { * @param keys * @param span */ -export function normalizeTree(tree: any, ctx?: any, path = [0], keys = true, span = true) { +export const normalizeTree = (tree: any, ctx?: any, path = [0], keys = true, span = true) => { if (tree == null) { return; } @@ -182,4 +176,4 @@ export function normalizeTree(tree: any, ctx?: any, path = [0], keys = true, spa return span ? ["span", keys ? { key: path.join("-") } : {}, tree.toString()] : tree.toString(); -} +}; diff --git a/packages/hdom/src/start.ts b/packages/hdom/src/start.ts index b513daf36d..d40f7ce45b 100644 --- a/packages/hdom/src/start.ts +++ b/packages/hdom/src/start.ts @@ -1,6 +1,9 @@ import { isString } from "@thi.ng/checks/is-string"; + +import { HDOMOpts } from "./api"; import { diffElement } from "./diff"; import { normalizeTree } from "./normalize"; +import { hydrateDOM } from "@thi.ng/hdom/src/dom"; /** * Takes a parent DOM element (or ID), hiccup tree (array, function or @@ -23,36 +26,47 @@ import { normalizeTree } from "./normalize"; * which then is diffed and applied against the previous tree kept as * usual. Any number of frames may be skipped this way. * - * Important: The parent element given is assumed to have NO children at - * the time when `start()` is called. Since hdom does NOT track the real - * DOM, the resulting changes will result in potentially undefined - * behavior if the parent element wasn't empty. + * **Important:** Unless the `hydrate` option is enabled, the parent + * element given is assumed to have NO children at the time when + * `start()` is called. Since hdom does NOT track the real DOM, the + * resulting changes will result in potentially undefined behavior if + * the parent element wasn't empty. Likewise, if `hydrate` is enabled, + * it is assumed that an equivalent DOM (minus listeners) already exists + * (i.e. generated via SSR) when `start()` is called. Any other + * discrepancies between the pre-existing DOM and the hdom trees will + * cause undefined behavior. * * Returns a function, which when called, immediately cancels the update * loop. * - * @param parent root element or ID * @param tree hiccup DOM tree - * @param ctx arbitrary user context object - * @param spans true (default), if text should be wrapped in `` + * @param opts options */ -export function start(parent: Element | string, tree: any, ctx?: any, spans = true) { +export const start = (tree: any, opts?: Partial) => { + opts = { root: "app", span: true, normalize: true, ...opts }; let prev = []; let isActive = true; - parent = isString(parent) ? - document.getElementById(parent) : - parent; - function update() { + const root = isString(opts.root) ? + document.getElementById(opts.root) : + opts.root; + const update = () => { if (isActive) { - const curr = normalizeTree(tree, ctx, [0], true, spans); + const curr = opts.normalize ? + normalizeTree(tree, opts.ctx, [0], true, opts.span) : + tree; if (curr != null) { - diffElement(parent, prev, curr); + if (opts.hydrate) { + hydrateDOM(root, curr); + opts.hydrate = false; + } else { + diffElement(root, prev, curr); + } prev = curr; } // check again in case one of the components called cancel isActive && requestAnimationFrame(update); } - } + }; requestAnimationFrame(update); return () => (isActive = false); -} +}; diff --git a/packages/hiccup-css/CHANGELOG.md b/packages/hiccup-css/CHANGELOG.md index f2a0f5e5b1..adbccdea69 100644 --- a/packages/hiccup-css/CHANGELOG.md +++ b/packages/hiccup-css/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.2.17](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup-css@0.2.16...@thi.ng/hiccup-css@0.2.17) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/hiccup-css + ## [0.2.16](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup-css@0.2.15...@thi.ng/hiccup-css@0.2.16) (2018-08-24) diff --git a/packages/hiccup-css/package.json b/packages/hiccup-css/package.json index 751631f270..a5c5348e3c 100644 --- a/packages/hiccup-css/package.json +++ b/packages/hiccup-css/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/hiccup-css", - "version": "0.2.16", + "version": "0.2.17", "description": "CSS from nested JS data structures", "main": "./index.js", "typings": "./index.d.ts", @@ -31,7 +31,7 @@ "@thi.ng/api": "^4.1.0", "@thi.ng/checks": "^1.5.7", "@thi.ng/errors": "^0.1.6", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "clojure", diff --git a/packages/hiccup-svg/CHANGELOG.md b/packages/hiccup-svg/CHANGELOG.md index 6395ebf2d5..8c6264826e 100644 --- a/packages/hiccup-svg/CHANGELOG.md +++ b/packages/hiccup-svg/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.12](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup-svg@1.0.11...@thi.ng/hiccup-svg@1.0.12) (2018-08-31) + + + + +**Note:** Version bump only for package @thi.ng/hiccup-svg + + +## [1.0.11](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup-svg@1.0.10...@thi.ng/hiccup-svg@1.0.11) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/hiccup-svg + ## [1.0.10](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup-svg@1.0.9...@thi.ng/hiccup-svg@1.0.10) (2018-08-24) diff --git a/packages/hiccup-svg/package.json b/packages/hiccup-svg/package.json index e7b8d23648..b31197f38b 100644 --- a/packages/hiccup-svg/package.json +++ b/packages/hiccup-svg/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/hiccup-svg", - "version": "1.0.10", + "version": "1.0.12", "description": "SVG element functions for @thi.ng/hiccup & @thi.ng/hdom", "main": "./index.js", "typings": "./index.d.ts", @@ -28,7 +28,7 @@ "typescript": "^3.0.1" }, "dependencies": { - "@thi.ng/hiccup": "^2.0.10" + "@thi.ng/hiccup": "^2.1.0" }, "keywords": [ "components", diff --git a/packages/hiccup/CHANGELOG.md b/packages/hiccup/CHANGELOG.md index 72b40acabe..fdded8e71e 100644 --- a/packages/hiccup/CHANGELOG.md +++ b/packages/hiccup/CHANGELOG.md @@ -3,6 +3,31 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.1.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup@2.0.11...@thi.ng/hiccup@2.1.0) (2018-08-31) + + +### Bug Fixes + +* **hiccup:** disable spans for certain element types ([1b97a25](https://github.com/thi-ng/umbrella/commit/1b97a25)) +* **hiccup:** serialize() args ([1e8b4ef](https://github.com/thi-ng/umbrella/commit/1e8b4ef)) + + +### Features + +* **hiccup:** add optional support for spans & auto keying ([#39](https://github.com/thi-ng/umbrella/issues/39)) ([1b0deb2](https://github.com/thi-ng/umbrella/commit/1b0deb2)) + + + + + +## [2.0.11](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup@2.0.10...@thi.ng/hiccup@2.0.11) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/hiccup + ## [2.0.10](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup@2.0.9...@thi.ng/hiccup@2.0.10) (2018-08-24) diff --git a/packages/hiccup/package.json b/packages/hiccup/package.json index 38636e955e..bafe767581 100644 --- a/packages/hiccup/package.json +++ b/packages/hiccup/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/hiccup", - "version": "2.0.10", + "version": "2.1.0", "description": "HTML/SVG/XML serialization of nested data structures, iterables & closures", "main": "./index.js", "typings": "./index.d.ts", @@ -20,7 +20,7 @@ "test": "rm -rf build && tsc -p test && nyc mocha build/test/*.js" }, "devDependencies": { - "@thi.ng/atom": "^1.4.7", + "@thi.ng/atom": "^1.5.0", "@types/mocha": "^5.2.5", "@types/node": "^10.5.5", "mocha": "^5.2.0", diff --git a/packages/hiccup/src/api.ts b/packages/hiccup/src/api.ts index f6b68d41f9..22d3c1c47c 100644 --- a/packages/hiccup/src/api.ts +++ b/packages/hiccup/src/api.ts @@ -21,4 +21,10 @@ export const ENTITIES = { "'": "'", }; +export const NO_SPANS = { + option: 1, + text: 1, + textarea: 1, +}; + export const ENTITY_RE = new RegExp(`[${Object.keys(ENTITIES)}]`, "g"); diff --git a/packages/hiccup/src/serialize.ts b/packages/hiccup/src/serialize.ts index 501d223177..9d8aeffe9a 100644 --- a/packages/hiccup/src/serialize.ts +++ b/packages/hiccup/src/serialize.ts @@ -1,11 +1,12 @@ -import { illegalArgs } from "@thi.ng/errors/illegal-arguments"; import { implementsFunction } from "@thi.ng/checks/implements-function"; import { isFunction } from "@thi.ng/checks/is-function"; import { isPlainObject } from "@thi.ng/checks/is-plain-object"; import { isString } from "@thi.ng/checks/is-string"; +import { illegalArgs } from "@thi.ng/errors/illegal-arguments"; -import { TAG_REGEXP, VOID_TAGS } from "./api"; +import { NO_SPANS, TAG_REGEXP, VOID_TAGS } from "./api"; import { css } from "./css"; +import { escape } from "./escape"; /** * Recursively normalizes and serializes given tree as HTML/SVG/XML @@ -74,12 +75,24 @@ import { css } from "./css"; * strings, numbers, iterables or any type with a suitable `.toString()` * implementation). * - * @param tree elements / component tree + * If the optional `span` flag is true (default: false), all text + * content will be wrapped in elements (this is to ensure DOM + * compatibility with hdom). + * + * If the optional `keys` flag is true (default: false), all elements + * will have an autogenerated `key` attribute injected. If `span` is + * enabled, `keys` will be enabled by default too. + * + * @param tree hiccup elements / component tree + * @param ctx arbitrary user context object * @param escape auto-escape entities + * @param span use spans for text content + * @param keys attach key attribs */ -export const serialize = (tree: any[], ctx?: any, escape = false) => _serialize(tree, ctx, escape); +export const serialize = (tree: any, ctx?: any, escape = false, span = false, keys = span, path = [0]) => + _serialize(tree, ctx, escape, span, keys, path); -const _serialize = (tree: any, ctx: any, esc: boolean) => { +const _serialize = (tree: any, ctx: any, esc: boolean, span: boolean, keys: boolean, path: any[]) => { if (tree == null) { return ""; } @@ -89,10 +102,10 @@ const _serialize = (tree: any, ctx: any, esc: boolean) => { } let tag = tree[0]; if (isFunction(tag)) { - return _serialize(tag.apply(null, [ctx, ...tree.slice(1)]), ctx, esc); + return _serialize(tag.apply(null, [ctx, ...tree.slice(1)]), ctx, esc, span, keys, path); } if (implementsFunction(tag, "render")) { - return _serialize(tag.render.apply(null, [ctx, ...tree.slice(1)]), ctx, esc); + return _serialize(tag.render.apply(null, [ctx, ...tree.slice(1)]), ctx, esc, span, keys, path); } if (isString(tag)) { tree = normalize(tree); @@ -100,6 +113,9 @@ const _serialize = (tree: any, ctx: any, esc: boolean) => { let attribs = tree[1]; let body = tree[2]; let res = `<${tag}`; + if (keys && attribs.key === undefined) { + attribs.key = path.join("-"); + } for (let a in attribs) { if (attribs.hasOwnProperty(a)) { let v = attribs[a]; @@ -125,8 +141,9 @@ const _serialize = (tree: any, ctx: any, esc: boolean) => { illegalArgs(`No body allowed in tag: ${tag}`); } res += ">"; + span = span && !NO_SPANS[tag]; for (let i = 0, n = body.length; i < n; i++) { - res += _serialize(body[i], ctx, esc); + res += _serialize(body[i], ctx, esc, span, keys, [...path, i]); } return res += ``; } else if (!VOID_TAGS[tag]) { @@ -135,26 +152,31 @@ const _serialize = (tree: any, ctx: any, esc: boolean) => { return res += "/>"; } if (iter(tree)) { - return _serializeIter(tree, ctx, esc); + return _serializeIter(tree, ctx, esc, span, keys, path); } illegalArgs(`invalid tree node: ${tree}`); } if (isFunction(tree)) { - return _serialize(tree(ctx), ctx, esc); + return _serialize(tree(ctx), ctx, esc, span, keys, path); } if (implementsFunction(tree, "deref")) { - return _serialize(tree.deref(), ctx, esc); + return _serialize(tree.deref(), ctx, esc, span, keys, path); } if (iter(tree)) { - return _serializeIter(tree, ctx, esc); + return _serializeIter(tree, ctx, esc, span, keys, path); } - return esc ? escape(tree.toString()) : tree; + tree = esc ? escape(tree.toString()) : tree; + return span ? + `${tree}` : + tree; }; -const _serializeIter = (iter: Iterable, ctx: any, esc: boolean) => { +const _serializeIter = (iter: Iterable, ctx: any, esc: boolean, span: boolean, keys: boolean, path: any[]) => { const res = []; + const p = path.slice(0, path.length - 1); + let k = 0; for (let i of iter) { - res.push(_serialize(i, ctx, esc)); + res.push(_serialize(i, ctx, esc, span, keys, [...p, k++])); } return res.join(""); } diff --git a/packages/iges/CHANGELOG.md b/packages/iges/CHANGELOG.md index 03e4c5923f..73c3f930a0 100644 --- a/packages/iges/CHANGELOG.md +++ b/packages/iges/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.2.7](https://github.com/thi-ng/umbrella/compare/@thi.ng/iges@0.2.6...@thi.ng/iges@0.2.7) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/iges + ## [0.2.6](https://github.com/thi-ng/umbrella/compare/@thi.ng/iges@0.2.5...@thi.ng/iges@0.2.6) (2018-08-24) diff --git a/packages/iges/README.md b/packages/iges/README.md index 693513e4ee..81e4f5e694 100644 --- a/packages/iges/README.md +++ b/packages/iges/README.md @@ -11,7 +11,7 @@ Bare-bones IGES 5.3 serializer for (currently only) polygonal geometry, both open & closed, for use in various CAD applications (e.g. Rhino, Houdini, Fusion 360) -![houdini](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/iges.png) +![houdini](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/screenshots/iges.png) ## Installation @@ -19,6 +19,13 @@ Houdini, Fusion 360) yarn add @thi.ng/iges ``` +## Dependencies + +- [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/master/packages/api) +- [@thi.ng/defmulti](https://github.com/thi-ng/umbrella/tree/master/packages/defmulti) +- [@thi.ng/strings](https://github.com/thi-ng/umbrella/tree/master/packages/strings) +- [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/master/packages/transducers) + ## Usage examples ```ts diff --git a/packages/iges/package.json b/packages/iges/package.json index 75069725b3..e8a3a43ce9 100644 --- a/packages/iges/package.json +++ b/packages/iges/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/iges", - "version": "0.2.6", + "version": "0.2.7", "description": "IGES 5.3 serializer for (currently only) polygonal geometry, both open & closed", "main": "./index.js", "typings": "./index.d.ts", @@ -30,8 +30,8 @@ "dependencies": { "@thi.ng/api": "^4.1.0", "@thi.ng/defmulti": "^0.3.8", - "@thi.ng/strings": "^0.3.0", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/strings": "^0.3.1", + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "CAD", diff --git a/packages/interceptors/CHANGELOG.md b/packages/interceptors/CHANGELOG.md index 634c7cb218..1fd6e192ea 100644 --- a/packages/interceptors/CHANGELOG.md +++ b/packages/interceptors/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.8.11](https://github.com/thi-ng/umbrella/compare/@thi.ng/interceptors@1.8.10...@thi.ng/interceptors@1.8.11) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/interceptors + ## [1.8.10](https://github.com/thi-ng/umbrella/compare/@thi.ng/interceptors@1.8.9...@thi.ng/interceptors@1.8.10) (2018-08-24) diff --git a/packages/interceptors/package.json b/packages/interceptors/package.json index f7494db1b6..5feb96f74d 100644 --- a/packages/interceptors/package.json +++ b/packages/interceptors/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/interceptors", - "version": "1.8.10", + "version": "1.8.11", "description": "Interceptor based event bus, side effect & immutable state handling", "main": "./index.js", "typings": "./index.d.ts", @@ -29,7 +29,7 @@ }, "dependencies": { "@thi.ng/api": "^4.1.0", - "@thi.ng/atom": "^1.4.7", + "@thi.ng/atom": "^1.5.0", "@thi.ng/checks": "^1.5.7", "@thi.ng/errors": "^0.1.6", "@thi.ng/paths": "^1.5.2" diff --git a/packages/iterators/CHANGELOG.md b/packages/iterators/CHANGELOG.md index fa3eb52510..6c5f13d6bb 100644 --- a/packages/iterators/CHANGELOG.md +++ b/packages/iterators/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [4.1.23](https://github.com/thi-ng/umbrella/compare/@thi.ng/iterators@4.1.22...@thi.ng/iterators@4.1.23) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/iterators + ## [4.1.22](https://github.com/thi-ng/umbrella/compare/@thi.ng/iterators@4.1.21...@thi.ng/iterators@4.1.22) (2018-08-24) diff --git a/packages/iterators/package.json b/packages/iterators/package.json index 3da316fd33..a8b1f0b317 100644 --- a/packages/iterators/package.json +++ b/packages/iterators/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/iterators", - "version": "4.1.22", + "version": "4.1.23", "description": "clojure.core inspired, composable ES6 iterators & generators", "main": "./index.js", "typings": "./index.d.ts", @@ -29,7 +29,7 @@ }, "dependencies": { "@thi.ng/api": "^4.1.0", - "@thi.ng/dcons": "^1.1.1", + "@thi.ng/dcons": "^1.1.2", "@thi.ng/errors": "^0.1.6" }, "keywords": [ diff --git a/packages/memoize/CHANGELOG.md b/packages/memoize/CHANGELOG.md index 82c0488b65..432ab995d3 100644 --- a/packages/memoize/CHANGELOG.md +++ b/packages/memoize/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.1.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/memoize@0.1.1...@thi.ng/memoize@0.1.2) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/memoize + ## [0.1.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/memoize@0.1.0...@thi.ng/memoize@0.1.1) (2018-08-24) diff --git a/packages/memoize/README.md b/packages/memoize/README.md index c989125cb9..4967214b63 100644 --- a/packages/memoize/README.md +++ b/packages/memoize/README.md @@ -37,6 +37,10 @@ based on different strategies. See doc strings for further details. yarn add @thi.ng/memoize ``` +## Dependencies + +- [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/master/packages/api) + ## Usage examples ```ts diff --git a/packages/memoize/package.json b/packages/memoize/package.json index 5c6af0065d..a2bbbbd396 100644 --- a/packages/memoize/package.json +++ b/packages/memoize/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/memoize", - "version": "0.1.1", + "version": "0.1.2", "description": "Function memoization with configurable caches", "main": "./index.js", "typings": "./index.d.ts", diff --git a/packages/range-coder/CHANGELOG.md b/packages/range-coder/CHANGELOG.md index 077df4fc79..9f65b6df1a 100644 --- a/packages/range-coder/CHANGELOG.md +++ b/packages/range-coder/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.1.7](https://github.com/thi-ng/umbrella/compare/@thi.ng/range-coder@0.1.6...@thi.ng/range-coder@0.1.7) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/range-coder + ## [0.1.6](https://github.com/thi-ng/umbrella/compare/@thi.ng/range-coder@0.1.5...@thi.ng/range-coder@0.1.6) (2018-08-24) diff --git a/packages/range-coder/package.json b/packages/range-coder/package.json index e274eb5bf7..0ea7873a01 100644 --- a/packages/range-coder/package.json +++ b/packages/range-coder/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/range-coder", - "version": "0.1.6", + "version": "0.1.7", "description": "Binary data range encoder / decoder", "main": "./index.js", "typings": "./index.d.ts", @@ -20,7 +20,7 @@ "test": "rm -rf build && tsc -p test && nyc mocha build/test/*.js" }, "devDependencies": { - "@thi.ng/transducers": "^2.0.1", + "@thi.ng/transducers": "^2.0.2", "@types/mocha": "^5.2.5", "@types/node": "^10.5.5", "mocha": "^5.2.0", diff --git a/packages/resolve-map/CHANGELOG.md b/packages/resolve-map/CHANGELOG.md index c9961caf93..017c1809a0 100644 --- a/packages/resolve-map/CHANGELOG.md +++ b/packages/resolve-map/CHANGELOG.md @@ -3,7 +3,15 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - + +## [3.0.9](https://github.com/thi-ng/umbrella/compare/@thi.ng/resolve-map@3.0.8...@thi.ng/resolve-map@3.0.9) (2018-08-30) + + + + +**Note:** Version bump only for package @thi.ng/resolve-map + + ## [3.0.8](https://github.com/thi-ng/umbrella/compare/@thi.ng/resolve-map@3.0.7...@thi.ng/resolve-map@3.0.8) (2018-08-24) @@ -11,7 +19,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline **Note:** Version bump only for package @thi.ng/resolve-map - + ## [3.0.7](https://github.com/thi-ng/umbrella/compare/@thi.ng/resolve-map@3.0.6...@thi.ng/resolve-map@3.0.7) (2018-08-01) diff --git a/packages/resolve-map/README.md b/packages/resolve-map/README.md index a2b03a4751..7e1da6f9f2 100644 --- a/packages/resolve-map/README.md +++ b/packages/resolve-map/README.md @@ -58,9 +58,11 @@ Any function values are called using two possible conventions: 2) If no de-structure form is found in the function's arguments, the function is only called with `resolve` as argument. -**Important:** Since ES6 var names can't contain special characters, -destructured keys can ALWAYS only be looked up as siblings of the -currently processed key. +**Important:** ES6 destructuring can *only* be used for ES6 compile +targets and *will fail when transpiling to ES5*. If you're not sure, use +the 2nd (legacy) form. Also, since ES6 var names can't contain special +characters, destructured keys can ALWAYS only be looked up as siblings +of the currently processed key. The `resolve` function provided as arg to the user function accepts a path (**without `@` prefix**) to look up any other values in the root @@ -135,22 +137,22 @@ import * as tx from "@thi.ng/transducers"; // will be injected later as well const stats = { // sequence average - mean: ({src}) => tx.reduce(tx.mean(), src), + mean: ({ src }) => tx.mean(src), // sequence range - range: ({min,max}) => max - min, + range: ({ min, max }) => max - min, // computes sequence min val - min: ({src}) => tx.reduce(tx.min(), src), + min: ({ src }) => tx.min(src), // computes sequence max val - max: ({src}) => tx.reduce(tx.max(), src), + max: ({ src }) => tx.max(src), // sorted copy - sorted: ({src}) => [...src].sort((a, b) => a - b), + sorted: ({ src }) => [...src].sort((a, b) => a - b), // standard deviation - sd: ({src, mean})=> + sd: ({ src, mean })=> Math.sqrt( tx.transduce(tx.map((x) => Math.pow(x - mean, 2)), tx.add(), src) / (src.length - 1)), // compute 10th - 90th percentiles - percentiles: ({sorted}) => { + percentiles: ({ sorted }) => { return tx.transduce( tx.map((x) => sorted[Math.floor(x / 100 * sorted.length)]), tx.push(), diff --git a/packages/resolve-map/package.json b/packages/resolve-map/package.json index e9328786a9..74f27f99ba 100644 --- a/packages/resolve-map/package.json +++ b/packages/resolve-map/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/resolve-map", - "version": "3.0.8", + "version": "3.0.9", "description": "DAG resolution of vanilla objects & arrays with internally linked values", "main": "./index.js", "typings": "./index.d.ts", diff --git a/packages/resolve-map/test/index.ts b/packages/resolve-map/test/index.ts index 94b02ab9a8..98ca8b5adb 100644 --- a/packages/resolve-map/test/index.ts +++ b/packages/resolve-map/test/index.ts @@ -73,13 +73,13 @@ describe("resolve-map", () => { it("destructure", () => { const stats = { // sequence average - mean: ({ src: a }) => tx.reduce(tx.mean(), a), + mean: ({ src: a }) => tx.mean(a), // sequence range range: ({ min, max }) => max - min, // computes sequence min val - min: ({ src }) => tx.reduce(tx.min(), src), + min: ({ src }) => tx.min(src), // computes sequence max val - max: ({ src }) => tx.reduce(tx.max(), src), + max: ({ src }) => tx.max(src), // sorted copy sorted: ({ src }) => [...src].sort((a, b) => a - b), // standard deviation diff --git a/packages/rle-pack/CHANGELOG.md b/packages/rle-pack/CHANGELOG.md index 536f3e6998..85f13c422d 100644 --- a/packages/rle-pack/CHANGELOG.md +++ b/packages/rle-pack/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/rle-pack@1.0.1...@thi.ng/rle-pack@1.0.2) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/rle-pack + ## [1.0.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/rle-pack@1.0.0...@thi.ng/rle-pack@1.0.1) (2018-08-24) diff --git a/packages/rle-pack/README.md b/packages/rle-pack/README.md index ff3e2f9d6e..71d8febc30 100644 --- a/packages/rle-pack/README.md +++ b/packages/rle-pack/README.md @@ -42,6 +42,7 @@ yarn add @thi.ng/rle-pack ## Dependencies - [@thi.ng/bitstream](https://github.com/thi-ng/umbrella/tree/master/packages/bitstream) +- [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/master/packages/errors) ## API diff --git a/packages/rle-pack/package.json b/packages/rle-pack/package.json index 873d0b513f..3863a544e7 100644 --- a/packages/rle-pack/package.json +++ b/packages/rle-pack/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rle-pack", - "version": "1.0.1", + "version": "1.0.2", "description": "Binary run-length encoding packer w/ flexible repeat bit widths", "main": "./index.js", "typings": "./index.d.ts", diff --git a/packages/router/CHANGELOG.md b/packages/router/CHANGELOG.md index f86c98d1f4..cb47af8d72 100644 --- a/packages/router/CHANGELOG.md +++ b/packages/router/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.1.22](https://github.com/thi-ng/umbrella/compare/@thi.ng/router@0.1.21...@thi.ng/router@0.1.22) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/router + ## [0.1.21](https://github.com/thi-ng/umbrella/compare/@thi.ng/router@0.1.20...@thi.ng/router@0.1.21) (2018-08-24) diff --git a/packages/router/README.md b/packages/router/README.md index f3a5aab90f..386cfb359d 100644 --- a/packages/router/README.md +++ b/packages/router/README.md @@ -50,7 +50,8 @@ yarn start A complete, full commented demo app is here: -[Source](https://github.com/thi-ng/umbrella/blob/master/examples/router-basics/) | [Live demo](https://demo.thi.ng/umbrella/router-basics/) +[Source](https://github.com/thi-ng/umbrella/blob/master/examples/router-basics/) | +[Live demo](https://demo.thi.ng/umbrella/router-basics/) ```ts import * as r from "@thi.ng/router"; @@ -142,7 +143,8 @@ router.addListener(r.EV_ROUTE_CHANGED, console.log); router.start(); ``` -See [further comments in source code](https://github.com/thi-ng/umbrella/blob/master/packages/router/src/api.ts) +See [further comments in source +code](https://github.com/thi-ng/umbrella/blob/master/packages/router/src/api.ts) ## Authors diff --git a/packages/router/package.json b/packages/router/package.json index 69145ff74f..b98018bc49 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/router", - "version": "0.1.21", + "version": "0.1.22", "description": "Generic router for browser & non-browser based applications", "main": "./index.js", "typings": "./index.d.ts", diff --git a/packages/rstream-csp/CHANGELOG.md b/packages/rstream-csp/CHANGELOG.md index 541a716723..1ade10e636 100644 --- a/packages/rstream-csp/CHANGELOG.md +++ b/packages/rstream-csp/CHANGELOG.md @@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.1.101](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-csp@0.1.100...@thi.ng/rstream-csp@0.1.101) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/rstream-csp + + +## [0.1.100](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-csp@0.1.99...@thi.ng/rstream-csp@0.1.100) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream-csp + + +## [0.1.99](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-csp@0.1.98...@thi.ng/rstream-csp@0.1.99) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream-csp + ## [0.1.98](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-csp@0.1.97...@thi.ng/rstream-csp@0.1.98) (2018-08-24) diff --git a/packages/rstream-csp/package.json b/packages/rstream-csp/package.json index fc4c509944..41bcd29af1 100644 --- a/packages/rstream-csp/package.json +++ b/packages/rstream-csp/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rstream-csp", - "version": "0.1.98", + "version": "0.1.101", "description": "@thi.ng/csp bridge module for @thi.ng/rstream", "main": "./index.js", "typings": "./index.d.ts", @@ -28,8 +28,8 @@ "typescript": "^3.0.1" }, "dependencies": { - "@thi.ng/csp": "^0.3.57", - "@thi.ng/rstream": "^1.11.4" + "@thi.ng/csp": "^0.3.58", + "@thi.ng/rstream": "^1.11.7" }, "keywords": [ "bridge", diff --git a/packages/rstream-dot/CHANGELOG.md b/packages/rstream-dot/CHANGELOG.md index efbb648b0b..fc39c52367 100644 --- a/packages/rstream-dot/CHANGELOG.md +++ b/packages/rstream-dot/CHANGELOG.md @@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.2.40](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-dot@0.2.39...@thi.ng/rstream-dot@0.2.40) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/rstream-dot + + +## [0.2.39](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-dot@0.2.38...@thi.ng/rstream-dot@0.2.39) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream-dot + + +## [0.2.38](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-dot@0.2.37...@thi.ng/rstream-dot@0.2.38) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream-dot + ## [0.2.37](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-dot@0.2.36...@thi.ng/rstream-dot@0.2.37) (2018-08-24) diff --git a/packages/rstream-dot/package.json b/packages/rstream-dot/package.json index 1a3a57f88b..6503f6fead 100644 --- a/packages/rstream-dot/package.json +++ b/packages/rstream-dot/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rstream-dot", - "version": "0.2.37", + "version": "0.2.40", "description": "Graphviz DOT conversion of @thi.ng/rstream dataflow graph topologies", "main": "./index.js", "typings": "./index.d.ts", @@ -28,7 +28,7 @@ "typescript": "^3.0.1" }, "dependencies": { - "@thi.ng/rstream": "^1.11.4" + "@thi.ng/rstream": "^1.11.7" }, "keywords": [ "conversion", diff --git a/packages/rstream-gestures/CHANGELOG.md b/packages/rstream-gestures/CHANGELOG.md index d6c1f5cbf3..e9a07b7501 100644 --- a/packages/rstream-gestures/CHANGELOG.md +++ b/packages/rstream-gestures/CHANGELOG.md @@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.5.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-gestures@0.5.1...@thi.ng/rstream-gestures@0.5.2) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/rstream-gestures + + +## [0.5.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-gestures@0.5.0...@thi.ng/rstream-gestures@0.5.1) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream-gestures + + +# [0.5.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-gestures@0.4.18...@thi.ng/rstream-gestures@0.5.0) (2018-08-27) + + +### Features + +* **rstream-gestures:** add options for local & scaled positions ([ccc40a9](https://github.com/thi-ng/umbrella/commit/ccc40a9)) + + + + ## [0.4.18](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-gestures@0.4.17...@thi.ng/rstream-gestures@0.4.18) (2018-08-24) diff --git a/packages/rstream-gestures/README.md b/packages/rstream-gestures/README.md index 4c9e4214fa..9b8af9e627 100644 --- a/packages/rstream-gestures/README.md +++ b/packages/rstream-gestures/README.md @@ -22,6 +22,10 @@ The `zoom` value is always present, but is only updated with wheel events. The value will be constrained to `minZoom` ... `maxZoom` interval (provided via options object). +Also see the +[`GestureStreamOpts`](https://github.com/thi-ng/umbrella/tree/master/packages/rstream-gestures/src/index.ts#L26) +config options for further details. + ## Installation ```bash @@ -36,7 +40,12 @@ yarn add @thi.ng/rstream-gestures ## Usage examples -A small, fully commented project can be found in the `/examples` folder: +Several small, fully commented projects can be found in the `/examples` folder: + +![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/screenshots/canvas-dial.png) + +[Source](https://github.com/thi-ng/umbrella/tree/master/examples/canvas-dial) | +[Live version](https://demo.thi.ng/umbrella/canvas-dial) [Source](https://github.com/thi-ng/umbrella/tree/master/examples/rstream-dataflow) | [Live version](https://demo.thi.ng/umbrella/rstream-dataflow) diff --git a/packages/rstream-gestures/package.json b/packages/rstream-gestures/package.json index 06e8283f1a..4341cd22a4 100644 --- a/packages/rstream-gestures/package.json +++ b/packages/rstream-gestures/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rstream-gestures", - "version": "0.4.18", + "version": "0.5.2", "description": "Unified mouse, mouse wheel & single-touch event stream abstraction", "main": "./index.js", "typings": "./index.d.ts", @@ -29,8 +29,8 @@ }, "dependencies": { "@thi.ng/api": "^4.1.0", - "@thi.ng/rstream": "^1.11.4", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/rstream": "^1.11.7", + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "dataflow", diff --git a/packages/rstream-gestures/src/index.ts b/packages/rstream-gestures/src/index.ts index 606be865e8..c7c2be9195 100644 --- a/packages/rstream-gestures/src/index.ts +++ b/packages/rstream-gestures/src/index.ts @@ -50,6 +50,17 @@ export interface GestureStreamOpts extends IID { * Scaling factor for zoom changes. Default: 1 */ smooth: number; + /** + * Local coordinate flag. If true (default), the elements position + * offset is subtracted. + */ + local: boolean; + /** + * If true (default: false), all positions and delta values are + * scaled by `window.devicePixelRatio`. Note: Only enable if `local` + * is true. + */ + scale: boolean; } /** @@ -80,7 +91,7 @@ export interface GestureStreamOpts extends IID { * @param el * @param opts */ -export function gestureStream(el: Element, opts?: Partial): StreamMerge { +export function gestureStream(el: HTMLElement, opts?: Partial): StreamMerge { let isDown = false, clickPos: number[]; opts = Object.assign({ @@ -91,8 +102,11 @@ export function gestureStream(el: Element, opts?: Partial): S smooth: 1, eventOpts: { capture: true }, preventDefault: true, + local: true, + scale: false, }, opts); let zoom = Math.min(Math.max(opts.zoom, opts.minZoom), opts.maxZoom); + const dpr = window.devicePixelRatio || 1; return merge({ id: opts.id, src: [ @@ -120,12 +134,18 @@ export function gestureStream(el: Element, opts?: Partial): S }[e.type]; evt = e; } - const pos = [evt.clientX | 0, evt.clientY | 0]; + const pos = opts.local ? + [(evt.clientX - el.offsetLeft) | 0, (evt.clientY - el.offsetTop) | 0] : + [evt.clientX | 0, evt.clientY | 0]; + if (opts.scale) { + pos[0] *= dpr; + pos[1] *= dpr; + } const body = { pos, zoom }; switch (type) { case GestureType.START: isDown = true; - clickPos = pos; + clickPos = [...pos]; break; case GestureType.END: isDown = false; diff --git a/packages/rstream-graph/CHANGELOG.md b/packages/rstream-graph/CHANGELOG.md index 68bc412dad..d25181ef6e 100644 --- a/packages/rstream-graph/CHANGELOG.md +++ b/packages/rstream-graph/CHANGELOG.md @@ -3,6 +3,38 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [2.1.26](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-graph@2.1.25...@thi.ng/rstream-graph@2.1.26) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/rstream-graph + + +## [2.1.25](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-graph@2.1.24...@thi.ng/rstream-graph@2.1.25) (2018-08-30) + + + + +**Note:** Version bump only for package @thi.ng/rstream-graph + + +## [2.1.24](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-graph@2.1.23...@thi.ng/rstream-graph@2.1.24) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream-graph + + +## [2.1.23](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-graph@2.1.22...@thi.ng/rstream-graph@2.1.23) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream-graph + ## [2.1.22](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-graph@2.1.21...@thi.ng/rstream-graph@2.1.22) (2018-08-24) diff --git a/packages/rstream-graph/package.json b/packages/rstream-graph/package.json index 7b0185b116..384cc421af 100644 --- a/packages/rstream-graph/package.json +++ b/packages/rstream-graph/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rstream-graph", - "version": "2.1.22", + "version": "2.1.26", "description": "Declarative dataflow graph construction for @thi.ng/rstream", "main": "./index.js", "typings": "./index.d.ts", @@ -32,9 +32,9 @@ "@thi.ng/checks": "^1.5.7", "@thi.ng/errors": "^0.1.6", "@thi.ng/paths": "^1.5.2", - "@thi.ng/resolve-map": "^3.0.8", - "@thi.ng/rstream": "^1.11.4", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/resolve-map": "^3.0.9", + "@thi.ng/rstream": "^1.11.7", + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "compute", diff --git a/packages/rstream-log/CHANGELOG.md b/packages/rstream-log/CHANGELOG.md index f58fd3b18c..0ecc92b62c 100644 --- a/packages/rstream-log/CHANGELOG.md +++ b/packages/rstream-log/CHANGELOG.md @@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.52](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-log@1.0.51...@thi.ng/rstream-log@1.0.52) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/rstream-log + + +## [1.0.51](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-log@1.0.50...@thi.ng/rstream-log@1.0.51) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream-log + + +## [1.0.50](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-log@1.0.49...@thi.ng/rstream-log@1.0.50) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream-log + ## [1.0.49](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-log@1.0.48...@thi.ng/rstream-log@1.0.49) (2018-08-24) diff --git a/packages/rstream-log/package.json b/packages/rstream-log/package.json index 08e5afef24..e67b35abbb 100644 --- a/packages/rstream-log/package.json +++ b/packages/rstream-log/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rstream-log", - "version": "1.0.49", + "version": "1.0.52", "description": "Structured, multilevel & hierarchical loggers based on @thi.ng/rstream", "main": "./index.js", "typings": "./index.d.ts", @@ -31,8 +31,8 @@ "@thi.ng/api": "^4.1.0", "@thi.ng/checks": "^1.5.7", "@thi.ng/errors": "^0.1.6", - "@thi.ng/rstream": "^1.11.4", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/rstream": "^1.11.7", + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "ES6", diff --git a/packages/rstream-query/CHANGELOG.md b/packages/rstream-query/CHANGELOG.md index 3b7f15928c..a365a0c4ea 100644 --- a/packages/rstream-query/CHANGELOG.md +++ b/packages/rstream-query/CHANGELOG.md @@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.3.39](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-query@0.3.38...@thi.ng/rstream-query@0.3.39) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/rstream-query + + +## [0.3.38](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-query@0.3.37...@thi.ng/rstream-query@0.3.38) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream-query + + +## [0.3.37](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-query@0.3.36...@thi.ng/rstream-query@0.3.37) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream-query + ## [0.3.36](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-query@0.3.35...@thi.ng/rstream-query@0.3.36) (2018-08-24) diff --git a/packages/rstream-query/package.json b/packages/rstream-query/package.json index 1fed827ca6..0d58dcee4a 100644 --- a/packages/rstream-query/package.json +++ b/packages/rstream-query/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rstream-query", - "version": "0.3.36", + "version": "0.3.39", "description": "@thi.ng/rstream based triple store & reactive query engine", "main": "./index.js", "typings": "./index.d.ts", @@ -29,13 +29,13 @@ }, "dependencies": { "@thi.ng/api": "^4.1.0", - "@thi.ng/associative": "^0.6.1", + "@thi.ng/associative": "^0.6.2", "@thi.ng/checks": "^1.5.7", "@thi.ng/equiv": "^0.1.7", "@thi.ng/errors": "^0.1.6", - "@thi.ng/rstream": "^1.11.4", - "@thi.ng/rstream-dot": "^0.2.37", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/rstream": "^1.11.7", + "@thi.ng/rstream-dot": "^0.2.40", + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "dataflow", diff --git a/packages/rstream/CHANGELOG.md b/packages/rstream/CHANGELOG.md index 53e626be94..288e185ed5 100644 --- a/packages/rstream/CHANGELOG.md +++ b/packages/rstream/CHANGELOG.md @@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.11.7](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream@1.11.6...@thi.ng/rstream@1.11.7) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/rstream + + +## [1.11.6](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream@1.11.5...@thi.ng/rstream@1.11.6) (2018-08-27) + + +### Bug Fixes + +* **rstream:** Fix unbound this in method call expression ([34a97b4](https://github.com/thi-ng/umbrella/commit/34a97b4)) + + + + + +## [1.11.5](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream@1.11.4...@thi.ng/rstream@1.11.5) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/rstream + ## [1.11.4](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream@1.11.3...@thi.ng/rstream@1.11.4) (2018-08-24) diff --git a/packages/rstream/package.json b/packages/rstream/package.json index 07214d4910..e115ba34b3 100644 --- a/packages/rstream/package.json +++ b/packages/rstream/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rstream", - "version": "1.11.4", + "version": "1.11.7", "description": "Reactive multi-tap streams, dataflow & transformation pipeline constructs", "main": "./index.js", "typings": "./index.d.ts", @@ -29,12 +29,12 @@ }, "dependencies": { "@thi.ng/api": "^4.1.0", - "@thi.ng/associative": "^0.6.1", - "@thi.ng/atom": "^1.4.7", + "@thi.ng/associative": "^0.6.2", + "@thi.ng/atom": "^1.5.0", "@thi.ng/checks": "^1.5.7", "@thi.ng/errors": "^0.1.6", "@thi.ng/paths": "^1.5.2", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "datastructure", diff --git a/packages/rstream/src/stream-merge.ts b/packages/rstream/src/stream-merge.ts index 3e5f7a24d8..feb2415c2a 100644 --- a/packages/rstream/src/stream-merge.ts +++ b/packages/rstream/src/stream-merge.ts @@ -47,6 +47,24 @@ export interface StreamMergeOpts extends IID { * // 20 * // 30 * ``` + * + * Use the `labeled()` transducer for each input to create a stream of + * labeled values and track their provenance: + * + * ``` + * merge({ + * src: [ + * fromIterable([1,2,3]).transform(labeled("a")), + * fromIterable([10,20,30]).transform(labeled("b")), + * ] + * }).subscribe(trace()); + * // ["a", 1] + * // ["b", 10] + * // ["a", 2] + * // ["b", 20] + * // ["a", 3] + * // ["b", 30] + * ``` */ export function merge(opts?: Partial>) { return new StreamMerge(opts); diff --git a/packages/rstream/src/subs/resolve.ts b/packages/rstream/src/subs/resolve.ts index ee1feac494..ee86cced37 100644 --- a/packages/rstream/src/subs/resolve.ts +++ b/packages/rstream/src/subs/resolve.ts @@ -57,7 +57,14 @@ export class Resolver extends Subscription, T> { DEBUG && console.log(`resolved value in ${State[this.state]} state (${x})`); } }, - (e) => (this.fail || this.error)(e) + (e) => { + if (this.fail) { + this.fail(e); + } + else { + this.error(e); + } + } ); } diff --git a/packages/sax/CHANGELOG.md b/packages/sax/CHANGELOG.md index b77a2251b6..d5e9a28f54 100644 --- a/packages/sax/CHANGELOG.md +++ b/packages/sax/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.3.16](https://github.com/thi-ng/umbrella/compare/@thi.ng/sax@0.3.15...@thi.ng/sax@0.3.16) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/sax + ## [0.3.15](https://github.com/thi-ng/umbrella/compare/@thi.ng/sax@0.3.14...@thi.ng/sax@0.3.15) (2018-08-24) diff --git a/packages/sax/README.md b/packages/sax/README.md index 91c3423991..5b35c8b726 100644 --- a/packages/sax/README.md +++ b/packages/sax/README.md @@ -32,6 +32,7 @@ yarn add @thi.ng/sax ## Dependencies +- [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/master/packages/api) - [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/master/packages/transducers) - [@thi.ng/transducers-fsm](https://github.com/thi-ng/umbrella/tree/master/packages/transducers-fsm) diff --git a/packages/sax/package.json b/packages/sax/package.json index 52d927ee86..20736419b5 100644 --- a/packages/sax/package.json +++ b/packages/sax/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/sax", - "version": "0.3.15", + "version": "0.3.16", "description": "Transducer-based, SAX-like, non-validating, speedy & tiny XML parser", "main": "./index.js", "typings": "./index.d.ts", @@ -29,8 +29,8 @@ }, "dependencies": { "@thi.ng/api": "^4.1.0", - "@thi.ng/transducers": "^2.0.1", - "@thi.ng/transducers-fsm": "^0.2.14" + "@thi.ng/transducers": "^2.0.2", + "@thi.ng/transducers-fsm": "^0.2.15" }, "keywords": [ "ES6", diff --git a/packages/strings/CHANGELOG.md b/packages/strings/CHANGELOG.md index 898574ca0c..ff8ce77449 100644 --- a/packages/strings/CHANGELOG.md +++ b/packages/strings/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.3.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/strings@0.3.0...@thi.ng/strings@0.3.1) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/strings + # [0.3.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/strings@0.2.0...@thi.ng/strings@0.3.0) (2018-08-24) diff --git a/packages/strings/README.md b/packages/strings/README.md index 689e9735cc..ebd814a649 100644 --- a/packages/strings/README.md +++ b/packages/strings/README.md @@ -9,6 +9,7 @@ This project is part of the - [About](#about) - [Installation](#installation) +- [Dependencies](#dependencies) - [Usage examples](#usage-examples) - [Authors](#authors) - [License](#license) @@ -26,6 +27,10 @@ functions, some memoized. WIP / Alpha. yarn add @thi.ng/strings ``` +## Dependencies + +- [@thi.ng/memoize](https://github.com/thi-ng/umbrella/tree/master/packages/memoize) + ## Usage examples ```ts diff --git a/packages/strings/package.json b/packages/strings/package.json index 8b051efd01..93b848ff0d 100644 --- a/packages/strings/package.json +++ b/packages/strings/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/strings", - "version": "0.3.0", + "version": "0.3.1", "description": "Various string formatting & utility functions", "main": "./index.js", "typings": "./index.d.ts", @@ -28,7 +28,7 @@ "typescript": "^3.0.1" }, "dependencies": { - "@thi.ng/memoize": "^0.1.1" + "@thi.ng/memoize": "^0.1.2" }, "keywords": [ "ES6", diff --git a/packages/transducers-fsm/CHANGELOG.md b/packages/transducers-fsm/CHANGELOG.md index 7e2fcde8ef..ac0a5adae6 100644 --- a/packages/transducers-fsm/CHANGELOG.md +++ b/packages/transducers-fsm/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.2.15](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers-fsm@0.2.14...@thi.ng/transducers-fsm@0.2.15) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/transducers-fsm + ## [0.2.14](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers-fsm@0.2.13...@thi.ng/transducers-fsm@0.2.14) (2018-08-24) diff --git a/packages/transducers-fsm/README.md b/packages/transducers-fsm/README.md index adc1ae8c94..070be1b205 100644 --- a/packages/transducers-fsm/README.md +++ b/packages/transducers-fsm/README.md @@ -21,6 +21,7 @@ yarn add @thi.ng/transducers-fsm ## Dependencies +- [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/master/packages/api) - [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/master/packages/transducers) ## Usage examples diff --git a/packages/transducers-fsm/package.json b/packages/transducers-fsm/package.json index d676a73f32..e7d15606c6 100644 --- a/packages/transducers-fsm/package.json +++ b/packages/transducers-fsm/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/transducers-fsm", - "version": "0.2.14", + "version": "0.2.15", "description": "Transducer-based Finite State Machine transformer", "main": "./index.js", "typings": "./index.d.ts", @@ -29,7 +29,7 @@ }, "dependencies": { "@thi.ng/api": "^4.1.0", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "ES6", diff --git a/packages/transducers-hdom/CHANGELOG.md b/packages/transducers-hdom/CHANGELOG.md index 07ff96fd9b..aa418b9fd8 100644 --- a/packages/transducers-hdom/CHANGELOG.md +++ b/packages/transducers-hdom/CHANGELOG.md @@ -3,6 +3,38 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers-hdom@1.0.0...@thi.ng/transducers-hdom@1.0.1) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/transducers-hdom + + +# [1.0.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers-hdom@0.1.5...@thi.ng/transducers-hdom@1.0.0) (2018-08-31) + + +### Features + +* **transducers-hdom:** add DOM hydration support, rename ([#39](https://github.com/thi-ng/umbrella/issues/39)) ([0f39694](https://github.com/thi-ng/umbrella/commit/0f39694)) + + +### BREAKING CHANGES + +* **transducers-hdom:** rename transducer: `updateUI` => `updateDOM`, new API + + + + + +## [0.1.5](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers-hdom@0.1.4...@thi.ng/transducers-hdom@0.1.5) (2018-08-27) + + + + +**Note:** Version bump only for package @thi.ng/transducers-hdom + ## [0.1.4](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers-hdom@0.1.3...@thi.ng/transducers-hdom@0.1.4) (2018-08-24) diff --git a/packages/transducers-hdom/README.md b/packages/transducers-hdom/README.md index b555ccc4c5..d3464cd8cc 100644 --- a/packages/transducers-hdom/README.md +++ b/packages/transducers-hdom/README.md @@ -17,7 +17,7 @@ This project is part of the ## About -This package provides a single `updateUI` function, a side-effecting & +This package provides a single `updateDOM` function, a side-effecting & stateful transducer which receives [@thi.ng/hdom](https://github.com/thi-ng/umbrella/tree/master/packages/hdom) component trees, diffs each against the previous value and applies any @@ -25,14 +25,23 @@ required changes to the browser DOM, starting at given root element. By default, incoming values are first normalized using @thi.ng/hdom's `normalizeTree()` function. +If the `hydrate` option is given, the first received tree is only used +to inject event listeners and initialize components with lifecycle +`init()` methods and expects an otherwise identical pre-existing DOM. +All succeeding trees are diffed then as usual. + This transducer is primarily intended for [@thi.ng/rstream](https://github.com/thi-ng/umbrella/tree/master/packages/rstream)-based -dataflow graphs, where this transducer can be used as final leaf -subscription / transformer to reflect UI changes back to the user, -without using the standard RAF update loop used by @thi.ng/hdom by -default. In this setup, UI updates will only be performed if the stream -this transducer is attached to receives new values (i.e. hdom component -trees). +dataflow graphs, where it can be used as final leaf subscription / +stream transformer to reflect UI changes back to the user, without using +the usual RAF update loop used by @thi.ng/hdom by default. In this +setup, DOM updates will only be performed if the stream this transducer +is attached to receives new values (i.e. hdom component trees). + +Please also see the following hdom references for further details: + +- [start()](https://github.com/thi-ng/umbrella/tree/master/packages/hdom/src/start.ts) +- [HDOMOpts](https://github.com/thi-ng/umbrella/tree/master/packages/hdom/src/api.ts#L19) ## Installation @@ -40,6 +49,12 @@ trees). yarn add @thi.ng/transducers-hdom ``` +## Dependencies + +- [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/master/packages/checks) +- [@thi.ng/hdom](https://github.com/thi-ng/umbrella/tree/master/packages/hdom) +- [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/master/packages/transducers) + ## Usage examples The below example is also available in the @@ -51,7 +66,7 @@ directory. ```ts import * as rs from "@thi.ng/rstream"; import * as tx from "@thi.ng/transducers"; -import { updateUI } from "@thi.ng/transducers-hdom"; +import { updateDOM } from "@thi.ng/transducers-hdom"; // root component function const app = ({ ticks, clicks }) => @@ -76,7 +91,7 @@ rs.sync({ // transform into hdom component tx.map(app), // apply as DOM - updateUI(document.body) + updateDOM({ root: document.body }) ); // kick off diff --git a/packages/transducers-hdom/package.json b/packages/transducers-hdom/package.json index 764ee11430..0b948dd59f 100644 --- a/packages/transducers-hdom/package.json +++ b/packages/transducers-hdom/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/transducers-hdom", - "version": "0.1.4", + "version": "1.0.1", "description": "Transducer based UI updater for @thi.ng/hdom", "main": "./index.js", "typings": "./index.d.ts", @@ -29,8 +29,8 @@ }, "dependencies": { "@thi.ng/checks": "^1.5.7", - "@thi.ng/hdom": "^3.0.34", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/hdom": "^4.0.1", + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "diff", diff --git a/packages/transducers-hdom/src/index.ts b/packages/transducers-hdom/src/index.ts index f2d49efb19..0d7aa6c5a3 100644 --- a/packages/transducers-hdom/src/index.ts +++ b/packages/transducers-hdom/src/index.ts @@ -1,5 +1,7 @@ import { isString } from "@thi.ng/checks/is-string"; +import { HDOMOpts } from "@thi.ng/hdom/api"; import { diffElement } from "@thi.ng/hdom/diff"; +import { hydrateDOM } from "@thi.ng/hdom/dom"; import { normalizeTree } from "@thi.ng/hdom/normalize"; import { Transducer } from "@thi.ng/transducers/api"; import { reducer } from "@thi.ng/transducers/reduce"; @@ -14,27 +16,44 @@ import { scan } from "@thi.ng/transducers/xform/scan"; * `normalizeTree()` function and the given (optional) `ctx` object is * provided to all embedded component functions in the tree. * + * If the `hydrate` option is given, the first received tree is only + * used to inject event listeners and initialize components with + * lifecycle `init()` methods and expects an otherwise identical, + * pre-existing DOM. All succeeding trees are diffed then as usual. + * * This transducer is primarily intended for @thi.ng/rstream dataflow - * graph based applications, where this transducer can be used as final - * leaf subscription to reactively reflect UI changes back to the user, - * without using the standard RAF update loop used by hdom by default. - * In this setup, UI updates will only be performed when the stream this + * graph based applications, where it can be used as final leaf + * subscription to reactively reflect UI changes back to the user, + * without using the usual RAF update loop used by hdom by default. In + * this setup, DOM updates will only be performed when the stream this * transducer is attached to emits new values (i.e. hdom component * trees). * - * @param root root element (or ID) - * @param ctx hdom user context - * @param normalize + * Please see here for further details: + * https://github.com/thi-ng/umbrella/blob/master/packages/hdom/src/start.ts + * + * @param opts hdom options */ -export const updateUI = (root: string | Element, ctx?: any, normalize = true): Transducer => { - root = isString(root) ? document.getElementById(root) : root; +export const updateDOM = (opts?: Partial): Transducer => { + opts = { root: "app", span: true, normalize: true, ...opts }; + const root = isString(opts.root) ? + document.getElementById(opts.root) : + opts.root; return scan( reducer( () => [], (prev, curr) => { - normalize && (curr = normalizeTree(curr, ctx)); - diffElement(root, prev, curr); - return curr; + opts.normalize && (curr = normalizeTree(curr, opts.ctx, [0], opts.span)); + if (curr != null) { + if (opts.hydrate) { + hydrateDOM(root, curr); + opts.hydrate = false; + } else { + diffElement(root, prev, curr); + } + return curr; + } + return prev; } ) ); diff --git a/packages/transducers-stats/CHANGELOG.md b/packages/transducers-stats/CHANGELOG.md index 1033c6651d..2762fae944 100644 --- a/packages/transducers-stats/CHANGELOG.md +++ b/packages/transducers-stats/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.4.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers-stats@0.4.1...@thi.ng/transducers-stats@0.4.2) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/transducers-stats + ## [0.4.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers-stats@0.4.0...@thi.ng/transducers-stats@0.4.1) (2018-08-24) diff --git a/packages/transducers-stats/README.md b/packages/transducers-stats/README.md index f2adeefb6a..041049b2d8 100644 --- a/packages/transducers-stats/README.md +++ b/packages/transducers-stats/README.md @@ -10,6 +10,7 @@ This project is part of the - [About](#about) - [Supported indicators](#supported-indicators) - [Installation](#installation) +- [Dependencies](#dependencies) - [Usage examples](#usage-examples) - [Authors](#authors) - [License](#license) @@ -53,13 +54,19 @@ transforming ES6 iterator (generator) instead of a transducer. yarn add @thi.ng/transducers-stats ``` +## Dependencies + +- [@thi.ng/dcons](https://github.com/thi-ng/umbrella/tree/master/packages/dcons) +- [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/master/packages/errors) +- [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/master/packages/transducers) + ## Usage examples For some realworld use, please see the [crypto chart](https://github.com/thi-ng/umbrella/tree/master/examples/crypto-chart) example. -![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/crypto-chart.png) +![screenshot](https://raw.githubusercontent.com/thi-ng/umbrella/master/assets/screenshots/crypto-chart.png) ```ts import * as tx from "@thi.ng/transducers"; diff --git a/packages/transducers-stats/package.json b/packages/transducers-stats/package.json index e3a5cb865a..f69a93f93f 100644 --- a/packages/transducers-stats/package.json +++ b/packages/transducers-stats/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/transducers-stats", - "version": "0.4.1", + "version": "0.4.2", "description": "Transducers for statistical / technical analysis", "main": "./index.js", "typings": "./index.d.ts", @@ -28,9 +28,9 @@ "typescript": "^3.0.1" }, "dependencies": { - "@thi.ng/dcons": "^1.1.1", + "@thi.ng/dcons": "^1.1.2", "@thi.ng/errors": "^0.1.6", - "@thi.ng/transducers": "^2.0.1" + "@thi.ng/transducers": "^2.0.2" }, "keywords": [ "ES6", diff --git a/packages/transducers/CHANGELOG.md b/packages/transducers/CHANGELOG.md index 7dc8879d0c..32890f7d31 100644 --- a/packages/transducers/CHANGELOG.md +++ b/packages/transducers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [2.0.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers@2.0.1...@thi.ng/transducers@2.0.2) (2018-09-01) + + + + +**Note:** Version bump only for package @thi.ng/transducers + ## [2.0.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers@2.0.0...@thi.ng/transducers@2.0.1) (2018-08-24) diff --git a/packages/transducers/README.md b/packages/transducers/README.md index cd070a5012..b21a389566 100644 --- a/packages/transducers/README.md +++ b/packages/transducers/README.md @@ -88,6 +88,8 @@ yarn add @thi.ng/transducers - [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/master/packages/api) - [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/master/packages/checks) - [@thi.ng/compare](https://github.com/thi-ng/umbrella/tree/master/packages/compare) +- [@thi.ng/compose](https://github.com/thi-ng/umbrella/tree/master/packages/compose) +- [@thi.ng/equiv](https://github.com/thi-ng/umbrella/tree/master/packages/equiv) - [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/master/packages/errors) - [@thi.ng/strings](https://github.com/thi-ng/umbrella/tree/master/packages/strings) diff --git a/packages/transducers/package.json b/packages/transducers/package.json index f517d30984..6d23bd371c 100644 --- a/packages/transducers/package.json +++ b/packages/transducers/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/transducers", - "version": "2.0.1", + "version": "2.0.2", "description": "Lightweight transducer implementations for ES6 / TypeScript", "main": "./index.js", "typings": "./index.d.ts", @@ -31,10 +31,10 @@ "@thi.ng/api": "^4.1.0", "@thi.ng/checks": "^1.5.7", "@thi.ng/compare": "^0.1.6", - "@thi.ng/compose": "^0.1.0", + "@thi.ng/compose": "^0.1.1", "@thi.ng/equiv": "^0.1.7", "@thi.ng/errors": "^0.1.6", - "@thi.ng/strings": "^0.3.0" + "@thi.ng/strings": "^0.3.1" }, "keywords": [ "ES6", diff --git a/packages/vectors/CHANGELOG.md b/packages/vectors/CHANGELOG.md index db644ea934..3106ffe1f7 100644 --- a/packages/vectors/CHANGELOG.md +++ b/packages/vectors/CHANGELOG.md @@ -3,6 +3,59 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.5.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/vectors@0.5.1...@thi.ng/vectors@0.5.2) (2018-09-01) + + +### Bug Fixes + +* **vectors:** add missing deps ([d2b4faf](https://github.com/thi-ng/umbrella/commit/d2b4faf)) + + + + + +## [0.5.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/vectors@0.5.0...@thi.ng/vectors@0.5.1) (2018-08-31) + + + + +**Note:** Version bump only for package @thi.ng/vectors + + +# [0.5.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/vectors@0.4.0...@thi.ng/vectors@0.5.0) (2018-08-30) + + +### Features + +* **vectors:** consolidate vector consts, add toJSON() impls ([bdb5d37](https://github.com/thi-ng/umbrella/commit/bdb5d37)) +* **vectors:** update types, update GVec, add maths fns, swap impls ([d5cec94](https://github.com/thi-ng/umbrella/commit/d5cec94)) + + + + + +# [0.4.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/vectors@0.3.0...@thi.ng/vectors@0.4.0) (2018-08-28) + + +### Features + +* **vectors:** add more vec2/3 ops ([cd834f8](https://github.com/thi-ng/umbrella/commit/cd834f8)) + + + + + +# [0.3.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/vectors@0.2.1...@thi.ng/vectors@0.3.0) (2018-08-27) + + +### Features + +* **vectors:** add mix1(), minor cleanups ([cfb2b74](https://github.com/thi-ng/umbrella/commit/cfb2b74)) + + + + ## [0.2.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/vectors@0.2.0...@thi.ng/vectors@0.2.1) (2018-08-24) diff --git a/packages/vectors/README.md b/packages/vectors/README.md index 152d9d19d4..d67d60bae7 100644 --- a/packages/vectors/README.md +++ b/packages/vectors/README.md @@ -11,6 +11,7 @@ This project is part of the - [Vectors](#vectors) - [Matrices](#matrices) - [Installation](#installation) +- [Dependencies](#dependencies) - [Usage examples](#usage-examples) - [Basics](#basics) - [Vector classes & interleaved vectors in large buffer](#vector-classes--interleaved-vectors-in-large-buffer) @@ -46,7 +47,8 @@ All of the vector operations listed below are also available via class wrappers of strided buffer views. These vector classes (`Vec2/3/4`) are array-like themselves and provide array index and `.x`, `.y`, `.z`, `.w` property accessors (including `.length`). The `GVec` class wrapper only -provides `.length` read access. All classes are also iterable. +provides `.length` read access and element access via `getAt()` and +`setAt()`. All classes are iterable. ```ts buf = [0, 1, 0, 2, 0, 3]; @@ -85,62 +87,67 @@ zero-copy vector operations on sections of larger buffers. The default start index is 0, default stride 1. See examples below and [tests](https://github.com/thi-ng/umbrella/tree/master/packages/vectors/test/). -| Operation | Generic | 2D | 3D | 4D | -|---------------------------------|--------------|------------------|--------------------|------------------| -| Get vector (dense copy) | `get` | `get2` | `get3` | `get4` | -| Set vector components (vector) | `set` | `set2` | `set3` | `set4` | -| Set vector components (uniform) | `setN` | `setN2` | `setN3` | `setN4` | -| Set vector components (scalars) | | `setS2` | `setS3` | `setS4` | -| Swizzle vector components | | `swizzle2` | `swizzle3` | `swizzle4` | -| Equality (w/ epsilon) | `eqDelta` | `eqDelta2` | `eqDelta3` | `eqDelta4` | -| Vector addition | `add` | `add2` | `add3` | `add4` | -| Vector subtraction | `sub` | `sub2` | `sub3` | `sub4` | -| Vector multiplication | `mul` | `mul2` | `mul3` | `mul4` | -| Vector division | `div` | `div2` | `div3` | `div4` | -| Uniform scalar addition | `addN` | `addN2` | `addN3` | `addN4` | -| Uniform scalar subtraction | `subN` | `subN2` | `subN3` | `subN4` | -| Uniform scalar multiply | `mulN` | `mulN2` | `mulN3` | `mulN4` | -| Uniform scalar multiply | `divN` | `divN2` | `divN3` | `divN4` | -| Vector negation | `neg` | `neg2` | `neg3` | `neg4` | -| Multiply-add vectors | `madd` | `madd2` | `madd3` | `madd4` | -| Multiply-add scalar | `maddN` | `maddN2` | `maddN3` | `maddN4` | -| Linear interpolation (vector) | `mix` | `mix2` | `mix3` | `mix4` | -| Linear interpolation (uniform) | `mixN` | `mixN2` | `mixN3` | `mixN4` | -| Dot product | `dot` | `dot2` | `dot3` | `dot4` | -| Cross product | | `cross2` | `cross3` | | -| Magnitude | `mag` | `mag2` | `mag3` | `mag4` | -| Magnitude (squared) | `magSq` | `magSq2` | `magSq3` | `magSq4` | -| Normalize (w/ opt length) | `normalize` | `normalize2` | `normalize3` | `normalize4` | -| Limit to length | | `limit2` | `limit3` | `limit4` | -| Distance | `dist` | `dist2` | `dist3` | `dist4` | -| Distance (squared) | `distSq` | `distSq2` | `distSq3` | `distSq4` | -| Manhattan distance | | `distManhattan2` | `distManhattan3` | `distManhattan4` | -| Chebyshev distance | | `distChebyshev2` | `distChebyshev3` | `distChebyshev4` | -| Reflection | | `reflect2` | `reflect3` | `reflect4` | -| RotationX | | | `rotateX3` | | -| RotationY | | | `rotateY3` | | -| RotationZ | | `rotate2` | `rotateZ3` | | -| Heading XY | | `heading2` | `headingXY3` | | -| Heading XZ | | | `headingXZ3` | | -| Heading YZ | | | `headingYZ3` | | -| Cartesian -> Polar | | `toPolar2` | `toSpherical3` | | -| Polar -> Cartesian | | `toCartesian2` | `toCartesian3` | | -| Cartesian -> Cylindrical | | | `toCylindrical3` | | -| Cylindrical -> Cartesian | | | `fromCylindrical3` | | -| Minor axis | | `minorAxis2` | `minorAxis3` | `minorAxis4` | -| Major axis | | `majorAxis2` | `majorAxis3` | `majorAxis4` | -| Minimum | `min` | `min2` | `min3` | `min4` | -| Maximum | `max` | `max2` | `max3` | `max4` | -| Range clamping | `clamp` | `clamp2` | `clamp3` | `clamp4` | -| Step (like GLSL) | `step` | `step2` | `step3` | `step4` | -| SmoothStep (like GLSL) | `smoothStep` | `smoothStep2` | `smoothStep3` | `smoothStep4` | -| Absolute value | `abs` | `abs2` | `abs3` | `abs4` | -| Sign (w/ opt epsilon) | `sign` | `sign2` | `sign3` | `sign4` | -| Round down | `floor` | `floor2` | `floor3` | `floor4` | -| Round up | `ceil` | `ceil2` | `ceil3` | `ceil4` | -| Square root | `sqrt` | `sqrt2` | `sqrt3` | `sqrt4` | -| Power (vector) | `pow` | `pow2` | `pow3` | `pow4` | -| Power (uniform) | `powN` | `powN2` | `powN3` | `powN4` | +| Operation | Generic | 2D | 3D | 4D | +|---------------------------------|--------------|----------------------|---------------------|------------------| +| Get vector (dense copy) | `get` | `get2` | `get3` | `get4` | +| Set vector components (vector) | `set` | `set2` | `set3` | `set4` | +| Set vector components (uniform) | `setN` | `setN2` | `setN3` | `setN4` | +| Set vector components (scalars) | | `setS2` | `setS3` | `setS4` | +| Swizzle vector components | | `swizzle2` | `swizzle3` | `swizzle4` | +| Swap vectors | | `swap2` | `swap3` | `swap4` | +| Equality (w/ epsilon) | `eqDelta` | `eqDelta2` | `eqDelta3` | `eqDelta4` | +| Vector addition | `add` | `add2` | `add3` | `add4` | +| Vector subtraction | `sub` | `sub2` | `sub3` | `sub4` | +| Vector multiplication | `mul` | `mul2` | `mul3` | `mul4` | +| Vector division | `div` | `div2` | `div3` | `div4` | +| Uniform scalar addition | `addN` | `addN2` | `addN3` | `addN4` | +| Uniform scalar subtraction | `subN` | `subN2` | `subN3` | `subN4` | +| Uniform scalar multiply | `mulN` | `mulN2` | `mulN3` | `mulN4` | +| Uniform scalar multiply | `divN` | `divN2` | `divN3` | `divN4` | +| Vector negation | `neg` | `neg2` | `neg3` | `neg4` | +| Multiply-add vectors | `madd` | `madd2` | `madd3` | `madd4` | +| Multiply-add scalar | `maddN` | `maddN2` | `maddN3` | `maddN4` | +| Linear interpolation (vector) | `mix` | `mix2` | `mix3` | `mix4` | +| Linear interpolation (uniform) | `mixN` | `mixN2` | `mixN3` | `mixN4` | +| Dot product | `dot` | `dot2` | `dot3` | `dot4` | +| Cross product | | `cross2` | `cross3` | | +| Magnitude | `mag` | `mag2` | `mag3` | `mag4` | +| Magnitude (squared) | `magSq` | `magSq2` | `magSq3` | `magSq4` | +| Normalize (w/ opt length) | `normalize` | `normalize2` | `normalize3` | `normalize4` | +| Limit to length | | `limit2` | `limit3` | `limit4` | +| Distance | `dist` | `dist2` | `dist3` | `dist4` | +| Distance (squared) | `distSq` | `distSq2` | `distSq3` | `distSq4` | +| Manhattan distance | | `distManhattan2` | `distManhattan3` | `distManhattan4` | +| Chebyshev distance | | `distChebyshev2` | `distChebyshev3` | `distChebyshev4` | +| Reflection | | `reflect2` | `reflect3` | `reflect4` | +| RotationX | | | `rotateX3` | | +| RotationY | | | `rotateY3` | | +| RotationZ | | `rotate2` | `rotateZ3` | | +| Rotation around point | | `rotateAroundPoint2` | | | +| Rotation around axis | | | `rotateAroundAxis3` | | +| Heading XY | | `heading2` | `headingXY3` | | +| Heading XZ | | | `headingXZ3` | | +| Heading YZ | | | `headingYZ3` | | +| Angle between vectors | | `angleBetween2` | `angleBetween3` | | +| Bisector angle | | `bisect2` | | | +| Cartesian -> Polar | | `toPolar2` | `toSpherical3` | | +| Polar -> Cartesian | | `toCartesian2` | `toCartesian3` | | +| Cartesian -> Cylindrical | | | `toCylindrical3` | | +| Cylindrical -> Cartesian | | | `fromCylindrical3` | | +| Minor axis | | `minorAxis2` | `minorAxis3` | `minorAxis4` | +| Major axis | | `majorAxis2` | `majorAxis3` | `majorAxis4` | +| Minimum | `min` | `min2` | `min3` | `min4` | +| Maximum | `max` | `max2` | `max3` | `max4` | +| Range clamping | `clamp` | `clamp2` | `clamp3` | `clamp4` | +| Step (like GLSL) | `step` | `step2` | `step3` | `step4` | +| SmoothStep (like GLSL) | `smoothStep` | `smoothStep2` | `smoothStep3` | `smoothStep4` | +| Absolute value | `abs` | `abs2` | `abs3` | `abs4` | +| Sign (w/ opt epsilon) | `sign` | `sign2` | `sign3` | `sign4` | +| Round down | `floor` | `floor2` | `floor3` | `floor4` | +| Round up | `ceil` | `ceil2` | `ceil3` | `ceil4` | +| Square root | `sqrt` | `sqrt2` | `sqrt3` | `sqrt4` | +| Power (vector) | `pow` | `pow2` | `pow3` | `pow4` | +| Power (uniform) | `powN` | `powN2` | `powN3` | `powN4` | ### Matrices @@ -188,6 +195,12 @@ non-suffixed naming.** yarn add @thi.ng/vectors ``` +## Dependencies + +- [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/master/packages/api) +- [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/master/packages/checks) +- [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/master/packages/errors) + ## Usage examples ### Basics diff --git a/packages/vectors/package.json b/packages/vectors/package.json index 6320e44fb6..1e75d5e307 100644 --- a/packages/vectors/package.json +++ b/packages/vectors/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/vectors", - "version": "0.2.1", + "version": "0.5.2", "description": "Vector algebra for fixed & variable sizes, memory mapped, flexible layouts", "main": "./index.js", "typings": "./index.d.ts", @@ -29,7 +29,8 @@ }, "dependencies": { "@thi.ng/api": "^4.1.0", - "@thi.ng/checks": "^1.5.7" + "@thi.ng/checks": "^1.5.7", + "@thi.ng/errors": "^0.1.6" }, "keywords": [ "ES6", diff --git a/packages/vectors/src/api.ts b/packages/vectors/src/api.ts index 81efc2408a..9b0f209618 100644 --- a/packages/vectors/src/api.ts +++ b/packages/vectors/src/api.ts @@ -1,15 +1,43 @@ import { NumericArray } from "@thi.ng/api/api"; export type Vec = NumericArray; -export type ReadonlyVec = ArrayLike; +export type ReadonlyVec = ArrayLike & Iterable; export type Mat = NumericArray; -export type ReadonlyMat = ArrayLike; +export type ReadonlyMat = ArrayLike & Iterable; -export type VecOp = (a: Vec, b: ReadonlyVec, ia: number, ib: number, sa: number, sb: number) => Vec; +/** + * A vector operation involving only a single vector. The vector might + * be modified. + */ +export type VecOp1 = (v: Vec, i: number, s?: number) => T; + +/** + * A vector operation involving two vectors. The first vector might be + * modified. + */ +export type VecOp2 = (a: Vec, b: ReadonlyVec, ia: number, ib: number, sa?: number, sb?: number) => T; + +/** + * A readonly vector operation involving only a single vector. + */ +export type ReadonlyVecOp1 = (v: ReadonlyVec, i: number, s?: number) => T; + +/** + * A readonly vector operation involving two vectors. + */ +export type ReadonlyVecOp2 = (a: ReadonlyVec, b: ReadonlyVec, ia: number, ib: number, sa?: number, sb?: number) => T; export interface IVec { buf: Vec; + length: number; i: number; s: number; } + +const min = Number.NEGATIVE_INFINITY; +const max = Number.POSITIVE_INFINITY; +export const MIN4 = Object.freeze([min, min, min, min]); +export const MAX4 = Object.freeze([max, max, max, max]); +export const ONE4 = Object.freeze([1, 1, 1, 1]); +export const ZERO4 = Object.freeze([0, 0, 0, 0]); diff --git a/packages/vectors/src/common.ts b/packages/vectors/src/common.ts index e393990774..c0cc5231a0 100644 --- a/packages/vectors/src/common.ts +++ b/packages/vectors/src/common.ts @@ -1,4 +1,4 @@ -import { ReadonlyVec, Vec, VecOp } from "./api"; +import { ReadonlyVec, Vec, VecOp2 } from "./api"; import { EPS, eqDelta1 } from "./math"; export const x = (v: ReadonlyVec, i = 0, _?: number) => v[i]; @@ -31,7 +31,7 @@ export const w = (v: ReadonlyVec, i = 0, s = 1) => v[i + 3 * s]; * @param csb component stride `b` * @param esa element stride `a` */ -export const transformVectors1 = (fn: VecOp, a: Vec, b: ReadonlyVec, num: number, ia: number, ib: number, csa: number, csb: number, esa: number) => { +export const transformVectors1 = (fn: VecOp2, a: Vec, b: ReadonlyVec, num: number, ia: number, ib: number, csa: number, csb: number, esa: number) => { for (; num > 0; num-- , ia += esa) { fn(a, b, ia, ib, csa, csb); } @@ -50,7 +50,7 @@ export const transformVectors1 = (fn: VecOp, a: Vec, b: ReadonlyVec, num: number * @param esa * @param esb */ -export const transformVectors2 = (fn: VecOp, a: Vec, b: ReadonlyVec, n: number, ia: number, ib: number, csa: number, csb: number, esa: number, esb: number) => { +export const transformVectors2 = (fn: VecOp2, a: Vec, b: ReadonlyVec, n: number, ia: number, ib: number, csa: number, csb: number, esa: number, esb: number) => { for (; n > 0; n-- , ia += esa, ib += esb) { fn(a, b, ia, ib, csa, csb); } @@ -75,11 +75,19 @@ export const eqDelta = (a: ReadonlyVec, b: ReadonlyVec, n: number, eps = EPS, ia return true; }; -export const declareIndices = (proto: any, indices: number[]) => +export const declareIndices = (proto: any, indices: number[]) => { + const get = (i: number) => function () { return this.buf[this.i + i * this.s]; }; + const set = (i: number) => function (n: number) { this.buf[this.i + i * this.s] = n; }; indices.forEach((i) => { Object.defineProperty(proto, i, { - get: function () { return this.buf[this.i + i * this.s]; }, - set: function (n: number) { this.buf[this.i + i * this.s] = n; }, + get: get(i), + set: set(i), + enumerable: true, + }); + Object.defineProperty(proto, "xyzw"[i], { + get: get(i), + set: set(i), enumerable: true, }); }); +}; diff --git a/packages/vectors/src/gvec.ts b/packages/vectors/src/gvec.ts index aa68389d83..653287f499 100644 --- a/packages/vectors/src/gvec.ts +++ b/packages/vectors/src/gvec.ts @@ -1,4 +1,9 @@ -import { ICopy, IEqualsDelta, IEquiv, ILength } from "@thi.ng/api/api"; +import { + ICopy, + IEqualsDelta, + IEquiv, + ILength +} from "@thi.ng/api/api"; import { isArrayLike } from "@thi.ng/checks/is-arraylike"; import { illegalArgs } from "@thi.ng/errors/illegal-arguments"; @@ -7,6 +12,7 @@ import { eqDelta, equiv } from "./common"; import { clamp1, EPS, + fract1, sign1, smoothStep1, step1 @@ -132,6 +138,9 @@ export const floor = (a: Vec, num = a.length, i = 0, s = 1) => export const ceil = (a: Vec, num = a.length, i = 0, s = 1) => opg1(Math.ceil, a, num, i, s); +export const fract = (a: Vec, num = a.length, i = 0, s = 1) => + opg1(fract1, a, num, i, s); + export const sin = (a: Vec, num = a.length, i = 0, s = 1) => opg1(Math.sin, a, num, i, s); @@ -195,15 +204,25 @@ export class GVec implements return this.n; } + getAt(i: number, safe = true) { + safe && this.ensureIndex(i); + return this.buf[this.i + i * this.s]; + } + + setAt(i: number, x: number, safe = true) { + safe && this.ensureIndex(i); + this.buf[this.i + i * this.s] = x; + } + copy() { - return new GVec(get(this.buf, this.n, this.i, this.s), this.n); + return new GVec(get(this.buf, this.n, this.i, this.s)); } equiv(v: any) { return v instanceof GVec && v.n === this.n ? equiv(this.buf, v.buf, this.n, this.i, v.i, this.s, v.s) : isArrayLike(v) && v.length === this.n ? - equiv(this.buf, v, this.n, this.i, 0, this.s, 1) : + equiv(this.buf, v, this.n, this.i, 0, this.s, 1) : false; } @@ -325,6 +344,11 @@ export class GVec implements return this; } + fract() { + fract(this.buf, this.n, this.i, this.s); + return this; + } + floor() { floor(this.buf, this.n, this.i, this.s); return this; @@ -392,7 +416,15 @@ export class GVec implements return `[${get(this.buf, this.n, this.i, this.s).join(", ")}]`; } + toJSON() { + return get(this.buf, this.n, this.i, this.s); + } + protected ensureSize(v: Readonly) { this.n !== v.n && illegalArgs(`vector size: ${v.n} (needed ${this.n})`); } + + protected ensureIndex(i: number) { + (i < 0 && i >= this.n) && illegalArgs(`index out of bounds: ${i}`); + } } diff --git a/packages/vectors/src/mat23.ts b/packages/vectors/src/mat23.ts index b8ea298e45..d9078af2f0 100644 --- a/packages/vectors/src/mat23.ts +++ b/packages/vectors/src/mat23.ts @@ -1,6 +1,6 @@ import { ICopy, IEqualsDelta } from "@thi.ng/api/api"; import { isArrayLike } from "@thi.ng/checks/is-arraylike"; -import { Mat, ReadonlyMat, Vec } from "./api"; +import { Mat, ReadonlyMat, Vec, ReadonlyVec } from "./api"; import { eqDelta } from "./common"; import { EPS } from "./math"; import { @@ -11,9 +11,9 @@ import { } from "./vec2"; export const get23 = (a: Mat, i = 0) => - set23(new ((a.constructor))(6), a, 0, i); + a.slice(i, i + 6); -export const set23 = (a: Mat, b: Mat, ia = 0, ib = 0) => ( +export const set23 = (a: Mat, b: ReadonlyMat, ia = 0, ib = 0) => ( a[ia] = b[ib], a[ia + 1] = b[ib + 1], a[ia + 2] = b[ib + 2], @@ -38,15 +38,20 @@ export const set23 = (a: Mat, b: Mat, ia = 0, ib = 0) => ( * @param m21 * @param i */ -export const setS23 = (m: Mat, m00: number, m01: number, m10: number, m11: number, m20: number, m21: number, i = 0) => ( - m[i] = m00, - m[i + 1] = m01, - m[i + 2] = m10, - m[i + 3] = m11, - m[i + 4] = m20, - m[i + 5] = m21, - m -); +export const setS23 = ( + m: Mat, + m00: number, m01: number, + m10: number, m11: number, + m20: number, m21: number, + i = 0) => ( + m[i] = m00, + m[i + 1] = m01, + m[i + 2] = m10, + m[i + 3] = m11, + m[i + 4] = m20, + m[i + 5] = m21, + m + ); export const identity23 = (m?: Mat, i = 0) => setS23(m || [], 1, 0, 0, 1, 0, 0, i); @@ -57,7 +62,7 @@ export const rotation23 = (m: Mat, theta: number, i = 0) => { return setS23(m || [], c, s, -s, c, 0, 0, i); }; -export const rotationAroundPoint23 = (m: Mat, p: Vec, theta: number, im = 0, iv = 0, sv = 1) => +export const rotationAroundPoint23 = (m: Mat, p: ReadonlyVec, theta: number, im = 0, iv = 0, sv = 1) => concat23( translationV23(m || [], p, im, iv, sv), im, rotation23([], theta), @@ -73,14 +78,14 @@ export const scaleN23 = (m: Mat, n: number, i = 0) => export const scaleS23 = (m: Mat, sx: number, sy: number, i = 0) => setS23(m || [], sx, 0, 0, sy, 0, 0, i); -export const scaleWithCenter23 = (m: Mat, p: Vec, sx: number, sy: number, im = 0, iv = 0, sv = 1) => +export const scaleWithCenter23 = (m: Mat, p: ReadonlyVec, sx: number, sy: number, im = 0, iv = 0, sv = 1) => concat23( translationV23(m || [], p, im, iv, sv), im, scaleS23([], sx, sy), translationS23([], -p[iv], -p[iv + sv]) ); -export const translationV23 = (m: Mat, v: Vec, i = 0, iv = 0, sv = 1) => +export const translationV23 = (m: Mat, v: ReadonlyVec, i = 0, iv = 0, sv = 1) => translationS23(m, v[iv], v[iv + sv], i); export const translationS23 = (m: Mat, x: number, y: number, i = 0) => @@ -118,7 +123,7 @@ export const concat23 = (a: Mat, ia: number, ...xs: (ReadonlyMat | [ReadonlyMat, a ); -export const mulV23 = (m: ReadonlyMat, v: Vec, im = 0, iv = 0, sv = 1) => +export const mulV23 = (v: Vec, m: ReadonlyMat, iv = 0, im = 0, sv = 1) => setS2( v, dot2(m, v, im, iv, 2, sv) + m[im + 4], @@ -161,7 +166,7 @@ export class Mat23 implements return new Mat23(rotation23([], theta)); } - static rotationAroundPoint(p: Vec2, theta: number) { + static rotationAroundPoint(p: Readonly, theta: number) { return new Mat23(rotationAroundPoint23([], p.buf, theta, 0, p.i, p.s)); } @@ -176,11 +181,11 @@ export class Mat23 implements ); } - static scaleWithCenter(p: Vec2, sx: number, sy = sx) { + static scaleWithCenter(p: Readonly, sx: number, sy = sx) { return new Mat23(scaleWithCenter23([], p.buf, sx, sy, p.i, p.s)); } - static translation(v: Vec2): Mat23; + static translation(v: Readonly): Mat23; static translation(x: number, y: number): Mat23; static translation(x: any, y?: any) { return new Mat23( @@ -214,8 +219,8 @@ export class Mat23 implements buf: Mat; i: number; - constructor(buf: Mat, i = 0) { - this.buf = buf; + constructor(buf?: Mat, i = 0) { + this.buf = buf || (new Array(6).fill(0)); this.i = i; } @@ -248,7 +253,7 @@ export class Mat23 implements } mulV(v: Vec2) { - mulV23(this.buf, v.buf, this.i, v.i, v.s); + mulV23(v.buf, this.buf, v.i, this.i, v.s); return v; } @@ -266,4 +271,8 @@ export class Mat23 implements const i = this.i; return `${b[i]} ${b[i + 2]} ${b[i + 4]}\n${b[i + 1]} ${b[i + 3]} ${b[i + 5]}`; } + + toJSON() { + return get23(this.buf, this.i); + } } diff --git a/packages/vectors/src/mat33.ts b/packages/vectors/src/mat33.ts index 75fe93863e..3cf65e3488 100644 --- a/packages/vectors/src/mat33.ts +++ b/packages/vectors/src/mat33.ts @@ -1,6 +1,6 @@ import { ICopy, IEqualsDelta } from "@thi.ng/api/api"; import { isArrayLike } from "@thi.ng/checks/is-arraylike"; -import { Mat, ReadonlyMat, Vec } from "./api"; +import { Mat, ReadonlyMat, Vec, ReadonlyVec } from "./api"; import { eqDelta } from "./common"; import { EPS } from "./math"; import { @@ -12,9 +12,9 @@ import { import { setS4 } from "./vec4"; export const get33 = (a: Mat, i = 0) => - set33(new ((a.constructor))(9), a, 0, i); + a.slice(i, i + 9); -export const set33 = (a: Mat, b: Mat, ia = 0, ib = 0) => ( +export const set33 = (a: Mat, b: ReadonlyMat, ia = 0, ib = 0) => ( a[ia] = b[ib], a[ia + 1] = b[ib + 1], a[ia + 2] = b[ib + 2], @@ -46,18 +46,23 @@ export const set33 = (a: Mat, b: Mat, ia = 0, ib = 0) => ( * @param m22 * @param i */ -export const setS33 = (m: Mat, m00: number, m01: number, m02: number, m10: number, m11: number, m12: number, m20: number, m21: number, m22: number, i = 0) => ( - m[i] = m00, - m[i + 1] = m01, - m[i + 2] = m02, - m[i + 3] = m10, - m[i + 4] = m11, - m[i + 5] = m12, - m[i + 6] = m20, - m[i + 7] = m21, - m[i + 8] = m22, - m -); +export const setS33 = ( + m: Mat, + m00: number, m01: number, m02: number, + m10: number, m11: number, m12: number, + m20: number, m21: number, m22: number, + i = 0) => ( + m[i] = m00, + m[i + 1] = m01, + m[i + 2] = m02, + m[i + 3] = m10, + m[i + 4] = m11, + m[i + 5] = m12, + m[i + 6] = m20, + m[i + 7] = m21, + m[i + 8] = m22, + m + ); export const identity33 = (m?: Mat, i = 0) => setS33(m || [], @@ -100,7 +105,7 @@ export const rotationZ33 = (m: Mat, theta: number, i = 0) => { ); }; -export const scaleV33 = (m: Mat, v: Vec, i = 0, iv = 0, sv = 1) => +export const scaleV33 = (m: Mat, v: ReadonlyVec, i = 0, iv = 0, sv = 1) => scaleS33(m, v[iv], v[iv + sv], v[iv + 2 * sv], i); export const scaleN33 = (m: Mat, n: number, i = 0) => @@ -137,7 +142,7 @@ export const concat33 = (a: Mat, ia: number, ...xs: (ReadonlyMat | [ReadonlyMat, a ); -export const mulV33 = (m: ReadonlyMat, v: Vec, im = 0, iv = 0, sv = 1) => +export const mulV33 = (v: Vec, m: ReadonlyMat, iv = 0, im = 0, sv = 1) => setS3( v, dot3(m, v, im, iv, 3, sv), @@ -204,7 +209,7 @@ export const transpose33 = (m: Mat, i = 0) => i ); -export const mat33to44 = (m44: Mat, m33: Mat, ia = 0, ib = 0) => ( +export const mat33to44 = (m44: Mat, m33: ReadonlyMat, ia = 0, ib = 0) => ( set3(m44, m33, ia, ib), set3(m44, m33, ia + 4, ib + 3), set3(m44, m33, ia + 8, ib + 6), @@ -229,7 +234,7 @@ export class Mat33 implements return new Mat33(rotationZ33([], theta)); } - static scale(v: Vec3): Mat33; + static scale(v: Readonly): Mat33; static scale(n: number): Mat33; static scale(x: number, y: number, z: number): Mat33; static scale(x: any, y = x, z = x) { @@ -248,8 +253,8 @@ export class Mat33 implements buf: Mat; i: number; - constructor(buf: Mat, i = 0) { - this.buf = buf; + constructor(buf?: Mat, i = 0) { + this.buf = buf || (new Array(9).fill(0)); this.i = i; } @@ -271,7 +276,9 @@ export class Mat33 implements return this; } - setS(m00: number, m01: number, m02: number, m10: number, m11: number, m12: number, m20: number, m21: number, m22: number) { + setS(m00: number, m01: number, m02: number, + m10: number, m11: number, m12: number, + m20: number, m21: number, m22: number) { setS33(this.buf, m00, m01, m02, m10, m11, m12, m20, m21, m22, this.i); return this; } @@ -282,7 +289,7 @@ export class Mat33 implements } mulV(v: Vec3) { - mulV33(this.buf, v.buf, this.i, v.i, v.s); + mulV33(v.buf, this.buf, v.i, this.i, v.s); return v; } @@ -305,4 +312,8 @@ export class Mat33 implements const i = this.i; return `${b[i]} ${b[i + 3]} ${b[i + 6]}\n${b[i + 1]} ${b[i + 4]} ${b[i + 7]}\n${b[i + 2]} ${b[i + 5]} ${b[i + 8]}`; } + + toJSON() { + return get33(this.buf, this.i); + } } diff --git a/packages/vectors/src/mat44.ts b/packages/vectors/src/mat44.ts index 0ef1aa0a31..23cfee8ed7 100644 --- a/packages/vectors/src/mat44.ts +++ b/packages/vectors/src/mat44.ts @@ -1,6 +1,6 @@ import { ICopy, IEqualsDelta } from "@thi.ng/api/api"; import { isArrayLike } from "@thi.ng/checks/is-arraylike"; -import { Mat, ReadonlyMat, Vec } from "./api"; +import { Mat, ReadonlyMat, Vec, ReadonlyVec } from "./api"; import { eqDelta } from "./common"; import { Mat33 } from "./mat33"; import { EPS, rad } from "./math"; @@ -17,9 +17,9 @@ import { import { dot4, setS4, Vec4 } from "./vec4"; export const get44 = (a: Mat, i = 0) => - set44(new ((a.constructor))(16), a, 0, i); + a.slice(i, i + 16); -export const set44 = (a: Mat, b: Mat, ia = 0, ib = 0) => { +export const set44 = (a: Mat, b: ReadonlyMat, ia = 0, ib = 0) => { for (let i = 0; i < 16; i++) { a[ia + i] = b[ib + i]; } @@ -34,7 +34,9 @@ export const set44 = (a: Mat, b: Mat, ia = 0, ib = 0) => { * m03 m13 m23 m33 * ``` */ -export const setS44 = (m: Mat, m00: number, m01: number, m02: number, m03: number, +export const setS44 = ( + m: Mat, + m00: number, m01: number, m02: number, m03: number, m10: number, m11: number, m12: number, m13: number, m20: number, m21: number, m22: number, m23: number, m30: number, m31: number, m32: number, m33: number, @@ -103,7 +105,7 @@ export const rotationZ44 = (m: Mat, theta: number, i = 0) => { ); }; -export const scaleV44 = (m: Mat, v: Vec, i = 0, iv = 0, sv = 1) => +export const scaleV44 = (m: Mat, v: ReadonlyVec, i = 0, iv = 0, sv = 1) => scaleS44(m, v[iv], v[iv + sv], v[iv + 2 * sv], i); export const scaleN44 = (m: Mat, n: number, i = 0) => @@ -118,14 +120,14 @@ export const scaleS44 = (m: Mat, sx: number, sy: number, sz: number, i = 0) => i ); -export const scaleWithCenter44 = (m: Mat, p: Vec, sx: number, sy: number, sz: number, im = 0, iv = 0, sv = 1) => +export const scaleWithCenter44 = (m: Mat, p: ReadonlyVec, sx: number, sy: number, sz: number, im = 0, iv = 0, sv = 1) => concat44( translationV44(m || [], p, im, iv, sv), im, scaleS44([], sx, sy, sz), translationS44([], -p[iv], -p[iv + sv], -p[iv + 2 * sv]) ); -export const translationV44 = (m: Mat, v: Vec, i = 0, iv = 0, sv = 1) => +export const translationV44 = (m: Mat, v: ReadonlyVec, i = 0, iv = 0, sv = 1) => translationS44(m, v[iv], v[iv + sv], v[iv + 2 * sv], i); export const translationS44 = (m: Mat, x: number, y: number, z: number, i = 0) => @@ -181,18 +183,15 @@ export const ortho = (m: Mat, left: number, right: number, bottom: number, top: ); }; -export const lookAt = (m: Mat, eye: Vec, target: Vec, up: Vec, im = 0, ie = 0, it = 0, iu = 0, se = 1, st = 1, su = 1) => { - eye = get3(eye, ie, se); - target = get3(target, it, st); - up = get3(up, iu, su); - const z = normalize3(sub3([...eye], target)); - const x = normalize3(cross3(up, z)); +export const lookAt = (m: Mat, eye: ReadonlyVec, target: ReadonlyVec, up: ReadonlyVec, im = 0, ie = 0, it = 0, iu = 0, se = 1, st = 1, su = 1) => { + const z = normalize3(sub3(get3(eye, ie, se), get3(target, it, st))); + const x = normalize3(cross3(get3(up, iu, su), z)); const y = normalize3(cross3([...z], x)); return setS44(m || [], x[0], y[0], z[0], 0, x[1], y[1], z[1], 0, x[2], y[2], z[2], 0, - -dot3(eye, x), -dot3(eye, y), -dot3(eye, z), 1, + -dot3(eye, x, ie, 0, se), -dot3(eye, y, ie, 0, se), -dot3(eye, z, ie, 0, se), 1, im ); } @@ -227,7 +226,7 @@ export const concat44 = (a: Mat, ia: number, ...xs: (ReadonlyMat | [ReadonlyMat, a ); -export const mulV344 = (m: ReadonlyMat, v: Vec, im = 0, iv = 0, sv = 1) => +export const mulV344 = (v: Vec, m: ReadonlyMat, iv = 0, im = 0, sv = 1) => setS3( v, dot3(m, v, im, iv, 4, sv) + m[12], @@ -236,7 +235,7 @@ export const mulV344 = (m: ReadonlyMat, v: Vec, im = 0, iv = 0, sv = 1) => iv, sv ); -export const mulV44 = (m: ReadonlyMat, v: Vec, im = 0, iv = 0, sv = 1) => +export const mulV44 = (v: Vec, m: ReadonlyMat, iv = 0, im = 0, sv = 1) => setS4( v, dot4(m, v, im, iv, 4, sv), @@ -279,7 +278,7 @@ const detCoeffs44 = (m: ReadonlyMat, i = 0) => { ]; }; -export const det44 = (m: Mat, i = 0) => { +export const det44 = (m: ReadonlyMat, i = 0) => { const d = detCoeffs44(m, i); return d[0] * d[11] - d[1] * d[10] + d[2] * d[9] + d[3] * d[8] - d[4] * d[7] + d[5] * d[6]; @@ -352,7 +351,7 @@ export const transpose44 = (m: Mat, i = 0) => i ); -export const normal44 = (a: Mat, b: Mat, ia = 0, ib = 0) => { +export const normal44 = (a: Mat, b: ReadonlyMat, ia = 0, ib = 0) => { const m00 = b[ib]; const m01 = b[ib + 1]; const m02 = b[ib + 2]; @@ -382,7 +381,7 @@ export const normal44 = (a: Mat, b: Mat, ia = 0, ib = 0) => { return a; }; -export const mat44to33 = (m33: Mat, m44: Mat, ia = 0, ib = 0) => ( +export const mat44to33 = (m33: Mat, m44: ReadonlyMat, ia = 0, ib = 0) => ( set3(m33, m44, ia, ib), set3(m33, m44, ia + 3, ib + 4), set3(m33, m44, ia + 6, ib + 8), @@ -405,7 +404,7 @@ export class Mat44 implements return new Mat44(rotationZ44([], theta)); } - static scale(v: Vec3): Mat44; + static scale(v: Readonly): Mat44; static scale(n: number): Mat44; static scale(x: number, y: number, z: number): Mat44; static scale(x: any, y = x, z = x) { @@ -416,11 +415,11 @@ export class Mat44 implements ); } - static scaleWithCenter(p: Vec3, sx: number, sy = sx, sz = sy) { + static scaleWithCenter(p: Readonly, sx: number, sy = sx, sz = sy) { return new Mat44(scaleWithCenter44([], p.buf, sx, sy, sz, p.i, p.s)); } - static translation(v: Vec3): Mat44; + static translation(v: Readonly): Mat44; static translation(x: number, y: number, z: number): Mat44; static translation(x: any, y?: any, z?: any) { return new Mat44( @@ -430,6 +429,27 @@ export class Mat44 implements ); } + static perspective(fov: number, aspect: number, near: number, far: number) { + return new Mat44(perspective([], fov, aspect, near, far)); + } + + static ortho(left: number, right: number, bottom: number, top: number, near: number, far: number) { + return new Mat44(ortho([], left, right, bottom, top, near, far)); + } + + static frustum(left: number, right: number, bottom: number, top: number, near: number, far: number) { + return new Mat44(frustum([], left, right, bottom, top, near, far)); + } + + static lookAt(eye: Readonly, target: Readonly, up: Readonly) { + return new Mat44(lookAt( + [], + eye.buf, target.buf, up.buf, + 0, eye.i, target.i, up.i, + eye.s, target.s, up.s + )); + } + static concat(m: Mat44, ...xs: Readonly[]) { concat44.apply(null, [m.buf, m.i, ...<[ReadonlyMat, number][]>xs.map((x) => [x.buf, x.i])]); return m; @@ -438,8 +458,8 @@ export class Mat44 implements buf: Mat; i: number; - constructor(buf: Mat, i = 0) { - this.buf = buf; + constructor(buf?: Mat, i = 0) { + this.buf = buf || (new Array(16).fill(0)); this.i = i; } @@ -475,12 +495,12 @@ export class Mat44 implements } mulV3(v: Vec3) { - mulV344(this.buf, v.buf, this.i, v.i, v.s); + mulV344(v.buf, this.buf, v.i, this.i, v.s); return v; } mulV(v: Vec4) { - mulV44(this.buf, v.buf, this.i, v.i, v.s); + mulV44(v.buf, this.buf, v.i, this.i, v.s); return v; } @@ -511,8 +531,12 @@ export class Mat44 implements } toString() { - const b = this.buf; const i = this.i; + const b = [...this.buf.slice(i, i+16)].map((x)=>x.toFixed(4)); return `${b[i]} ${b[i + 4]} ${b[i + 8]} ${b[i + 12]}\n${b[i + 1]} ${b[i + 5]} ${b[i + 9]} ${b[i + 13]}\n${b[i + 2]} ${b[i + 6]} ${b[i + 10]} ${b[i + 14]}\n${b[i + 3]} ${b[i + 7]} ${b[i + 11]} ${b[i + 15]}`; } + + toJSON() { + return get44(this.buf, this.i); + } } diff --git a/packages/vectors/src/math.ts b/packages/vectors/src/math.ts index 9f8d9fb182..282ba4df39 100644 --- a/packages/vectors/src/math.ts +++ b/packages/vectors/src/math.ts @@ -8,10 +8,10 @@ export const RAD2DEG = 180 / PI; export let EPS = 1e-6; -export const absDiff = (x: number, y: number) => +export const absDiff1 = (x: number, y: number) => Math.abs(x - y); -export const atan2Abs = (y: number, x: number) => { +export const atan2Abs1 = (y: number, x: number) => { const theta = Math.atan2(y, x); return theta < 0 ? TAU + theta : theta; }; @@ -48,7 +48,8 @@ export const eqDelta1 = (a: number, b: number, eps = EPS) => { * @param a * @param b */ -export const fmod1 = (a: number, b: number) => a - b * Math.floor(a / b); +export const fmod1 = (a: number, b: number) => + a - b * Math.floor(a / b); /** * Step/threshold function. @@ -57,7 +58,8 @@ export const fmod1 = (a: number, b: number) => a - b * Math.floor(a / b); * @param x test value * @returns 0, if `x < e`, else 1 */ -export const step1 = (edge: number, x: number) => x < edge ? 0 : 1; +export const step1 = (edge: number, x: number) => + x < edge ? 0 : 1; /** * GLSL-style smoothStep threshold function. @@ -72,7 +74,16 @@ export const smoothStep1 = (edge: number, edge2: number, x: number) => { return (3 - 2 * t) * t * t; }; -export const min2id = (a, b) => a <= b ? 0 : 1; +export const expStep1 = (x: number, k: number, n: number) => + Math.exp(-k * Math.pow(x, n)); + +export const gain1 = (x: number, k: number) => + x < 0.5 ? + 0.5 * Math.pow(2 * x, k) : + 1 - 0.5 * Math.pow(2 - 2 * x, k); + +export const min2id = (a, b) => + a <= b ? 0 : 1; export const min3id = (a, b, c) => (a <= b) ? @@ -88,7 +99,8 @@ export const min4id = (a, b, c, d) => (b <= d ? 1 : 3) : (c <= d ? 2 : 3)); -export const max2id = (a, b) => a >= b ? 0 : 1; +export const max2id = (a, b) => + a >= b ? 0 : 1; export const max3id = (a, b, c) => (a >= b) ? @@ -104,16 +116,28 @@ export const max4id = (a, b, c, d) => (b >= d ? 1 : 3) : (c >= d ? 2 : 3)); +export const smin1 = (a: number, b: number, k: number) => + -Math.log(Math.exp(-k * a) + Math.exp(-k * b)) / k; + +export const smax1 = (a: number, b: number, k: number) => + Math.log(Math.exp(a) + Math.exp(b)) / k; + /** - * Clamps value `x` to given closed interval. - * - * @param x value to clamp - * @param min lower bound - * @param max upper bound - */ +* Clamps value `x` to given closed interval. +* +* @param x value to clamp +* @param min lower bound +* @param max upper bound +*/ export const clamp1 = (x: number, min: number, max: number) => x < min ? min : x > max ? max : x; +export const mix1 = (a: number, b: number, t: number) => + a + (b - a) * t; + +export const norm1 = (x: number, a: number, b: number) => + (x - a) / (b - a); + export const fit1 = (x: number, a: number, b: number, c: number, d: number) => c + (d - c) * (x - a) / (b - a); @@ -123,6 +147,9 @@ export const fitClamped1 = (x: number, a: number, b: number, c: number, d: numbe export const sign1 = (x: number, eps = EPS) => x > eps ? 1 : x < -eps ? -1 : 0; +export const fract1 = (x: number) => + x - Math.floor(x); + export const trunc1 = (x: number) => x < 0 ? Math.ceil(x) : Math.floor(x); @@ -136,7 +163,7 @@ export const roundTo1 = (x: number, prec = 1) => * @param min * @param max */ -export const inRange = (x: number, min: number, max: number) => +export const inRange1 = (x: number, min: number, max: number) => x >= min && x <= max; /** @@ -146,5 +173,8 @@ export const inRange = (x: number, min: number, max: number) => * @param min * @param max */ -export const inOpenRange = (x: number, min: number, max: number) => +export const inOpenRange1 = (x: number, min: number, max: number) => x > min && x < max; + +export const hash1 = (x: number) => + fract1(Math.sin(x) * 758.5453); diff --git a/packages/vectors/src/vec2.ts b/packages/vectors/src/vec2.ts index bcb13cafb4..26d117b03e 100644 --- a/packages/vectors/src/vec2.ts +++ b/packages/vectors/src/vec2.ts @@ -6,21 +6,29 @@ import { } from "@thi.ng/api/api"; import { isArrayLike } from "@thi.ng/checks/is-arraylike"; -import { IVec, ReadonlyVec, Vec } from "./api"; +import { + IVec, + MAX4, + MIN4, + ONE4, + ReadonlyVec, + Vec, + ZERO4 +} from "./api"; import { declareIndices } from "./common"; import { - atan2Abs, + atan2Abs1, EPS, eqDelta1, + fract1, + HALF_PI, max2id, min2id, + PI, smoothStep1, step1 } from "./math"; -export const ZERO2 = Object.freeze([0, 0]); -export const ONE2 = Object.freeze([1, 1]); - export const op2 = (fn: (x: number) => number, a: Vec, ia = 0, sa = 1) => (a[ia] = fn(a[ia]), a[ia + sa] = fn(a[ia + sa]), a); @@ -42,7 +50,7 @@ export const setN2 = (a: Vec, n: number, ia = 0, sa = 1) => export const setS2 = (a: Vec, x: number, y: number, ia = 0, sa = 1) => (a[ia] = x, a[ia + sa] = y, a); -export const swizzle2 = (a: Vec, b: Vec, x: number, y: number, ia = 0, ib = 0, sa = 1, sb = 1) => { +export const swizzle2 = (a: Vec, b: ReadonlyVec, x: number, y: number, ia = 0, ib = 0, sa = 1, sb = 1) => { const xx = b[ib + x * sb]; const yy = b[ib + y * sb]; a[ia] = xx; @@ -50,6 +58,13 @@ export const swizzle2 = (a: Vec, b: Vec, x: number, y: number, ia = 0, ib = 0, s return a; }; +export const swap2 = (a: Vec, b: Vec, ia = 0, ib = 0, sa = 1, sb = 1) => { + let t = a[ia]; a[ia] = b[ib]; b[ib] = t; + ia += sa; ib += sb; + t = a[ia]; a[ia] = b[ib]; b[ib] = t; + return a; +}; + export const equiv2 = (a: ReadonlyVec, b: ReadonlyVec, ia = 0, ib = 0, sa = 1, sb = 1) => a[ia] === b[ib] && a[ia + sa] === b[ib + sb]; @@ -97,6 +112,9 @@ export const floor2 = (a: Vec, ia = 0, sa = 1) => export const ceil2 = (a: Vec, ia = 0, sa = 1) => op2(Math.ceil, a, ia, sa); +export const fract2 = (a: Vec, ia = 0, sa = 1) => + op2(fract1, a, ia, sa); + export const sin2 = (a: Vec, ia = 0, sa = 1) => op2(Math.sin, a, ia, sa); @@ -208,15 +226,38 @@ export const rotate2 = (a: Vec, theta: number, ia = 0, sa = 1) => { return setS2(a, x * c - y * s, x * s + y * c, ia, sa); }; +export const rotateAroundPoint2 = (a: Vec, b: Vec, theta: number, ia = 0, ib = 0, sa = 1, sb = 1) => { + const x = a[ia] - b[ib]; + const y = a[ia + sa] - b[ib + sb]; + const s = Math.sin(theta); + const c = Math.cos(theta); + return setS2( + a, + x * c - y * s + b[ib], + x * s + y * c + b[ib + sb], + ia, sa + ); +}; + export const heading2 = (a: ReadonlyVec, ia = 0, sa = 1) => - atan2Abs(a[ia + sa], a[ia]); + atan2Abs1(a[ia + sa], a[ia]); + +export const angleBetween2 = (a: ReadonlyVec, b: ReadonlyVec, normalize = false, ia = 0, ib = 0, sa = 1, sb = 1): number => + normalize ? + angleBetween2(get2(a, ia, sa), get2(b, ib, sb)) : + Math.acos(dot2(a, b, ia, ib, sa, sb)); + +export const bisect2 = (a: ReadonlyVec, b: ReadonlyVec, ia = 0, ib = 0, sa = 1, sb = 1) => { + const theta = (heading2(a, ia, sa) + heading2(b, ib, sb)) / 2; + return theta <= HALF_PI ? theta : PI - theta; +} export const toPolar2 = (a: Vec, ia = 0, sa = 1) => { const x = a[ia], y = a[ia + sa]; - return setS2(a, Math.sqrt(x * x + y * y), atan2Abs(y, x), ia, sa); + return setS2(a, Math.sqrt(x * x + y * y), atan2Abs1(y, x), ia, sa); }; -export const toCartesian2 = (a: Vec, b: ReadonlyVec = ZERO2, ia = 0, ib = 0, sa = 1, sb = 1) => { +export const toCartesian2 = (a: Vec, b: ReadonlyVec = ZERO4, ia = 0, ib = 0, sa = 1, sb = 1) => { const r = a[ia], theta = a[ia + sa]; return setS2( a, @@ -266,12 +307,16 @@ export class Vec2 implements return res; } - static ZERO = Object.freeze(new Vec2(ZERO2)); - static ONE = Object.freeze(new Vec2(ONE2)); + static readonly ZERO = Object.freeze(new Vec2(ZERO4)); + static readonly ONE = Object.freeze(new Vec2(ONE4)); + static readonly MIN = Object.freeze(new Vec2(MIN4)); + static readonly MAX = Object.freeze(new Vec2(MAX4)); buf: Vec; i: number; s: number; + x: number; + y: number; [0]: number; [1]: number; @@ -282,28 +327,15 @@ export class Vec2 implements } *[Symbol.iterator]() { - yield this.x; - yield this.y; - } - - get length() { - return 2; - } - - get x() { - return this.buf[this.i]; + yield* this.array(); } - set x(x: number) { - this.buf[this.i] = x; + array() { + return get2(this.buf, this.i, this.s); } - get y() { - return this.buf[this.i + this.s]; - } - - set y(y: number) { - this.buf[this.i + this.s] = y; + get length() { + return 2; } copy() { @@ -314,7 +346,7 @@ export class Vec2 implements return v instanceof Vec2 ? equiv2(this.buf, v.buf, this.i, v.i, this.s, v.s) : isArrayLike(v) && v.length === 2 ? - equiv2(this.buf, v, this.i, 0, this.s, 1) : + equiv2(this.buf, v, this.i, 0, this.s, 1) : false; } @@ -342,6 +374,11 @@ export class Vec2 implements return this; } + swap(v: Vec2) { + swap2(this.buf, v.buf, this.i, v.i, this.s, v.s); + return this; + } + add(v: Readonly) { add2(this.buf, v.buf, this.i, v.i, this.s, v.s); return this; @@ -407,6 +444,11 @@ export class Vec2 implements return this; } + fract() { + fract2(this.buf, this.i, this.s); + return this; + } + sqrt() { sqrt2(this.buf, this.i, this.s); return this; @@ -537,10 +579,23 @@ export class Vec2 implements return this; } + rotateAroundPoint(v: Readonly, theta: number) { + rotateAroundPoint2(this.buf, v.buf, theta, this.i, v.i, this.s, v.s); + return this; + } + heading() { return heading2(this.buf, this.i, this.s); } + angleBetween(v: Readonly, normalize = false) { + return angleBetween2(this.buf, v.buf, normalize, this.i, v.i, this.s, v.s); + } + + bisect(v: Readonly) { + return bisect2(this.buf, v.buf, this.i, v.i, this.s, v.s); + } + toPolar() { toPolar2(this.buf, this.i, this.s); return this; @@ -554,6 +609,10 @@ export class Vec2 implements toString() { return `[${this.buf[this.i]}, ${this.buf[this.i + this.s]}]`; } + + toJSON() { + return this.array(); + } } declareIndices(Vec2.prototype, [0, 1]); diff --git a/packages/vectors/src/vec3.ts b/packages/vectors/src/vec3.ts index b430a866d5..48415fe0af 100644 --- a/packages/vectors/src/vec3.ts +++ b/packages/vectors/src/vec3.ts @@ -6,12 +6,21 @@ import { } from "@thi.ng/api/api"; import { isArrayLike } from "@thi.ng/checks/is-arraylike"; -import { IVec, ReadonlyVec, Vec } from "./api"; +import { + IVec, + MAX4, + MIN4, + ONE4, + ReadonlyVec, + Vec, + ZERO4 +} from "./api"; import { declareIndices } from "./common"; import { - atan2Abs, + atan2Abs1, EPS, eqDelta1, + fract1, max3id, min3id, sign1, @@ -25,9 +34,6 @@ import { toPolar2 } from "./vec2"; -export const ZERO3 = Object.freeze([0, 0, 0]); -export const ONE3 = Object.freeze([1, 1, 1]); - export const op3 = (fn: (x: number) => number, a: Vec, ia = 0, sa = 1) => ( a[ia] = fn(a[ia]), a[ia + sa] = fn(a[ia + sa]), @@ -62,7 +68,7 @@ export const setN3 = (a: Vec, n: number, ia = 0, sa = 1) => ( export const setS3 = (a: Vec, x: number, y: number, z: number, ia = 0, sa = 1) => (a[ia] = x, a[ia + sa] = y, a[ia + 2 * sa] = z, a); -export const swizzle3 = (a: Vec, b: Vec, x: number, y: number, z: number, ia = 0, ib = 0, sa = 1, sb = 1) => { +export const swizzle3 = (a: Vec, b: ReadonlyVec, x: number, y: number, z: number, ia = 0, ib = 0, sa = 1, sb = 1) => { const xx = b[ib + x * sb]; const yy = b[ib + y * sb]; const zz = b[ib + z * sb]; @@ -72,6 +78,15 @@ export const swizzle3 = (a: Vec, b: Vec, x: number, y: number, z: number, ia = 0 return a; }; +export const swap3 = (a: Vec, b: Vec, ia = 0, ib = 0, sa = 1, sb = 1) => { + let t = a[ia]; a[ia] = b[ib]; b[ib] = t; + ia += sa; ib += sb; + t = a[ia]; a[ia] = b[ib]; b[ib] = t; + ia += sa; ib += sb; + t = a[ia]; a[ia] = b[ib]; b[ib] = t; + return a; +}; + export const equiv3 = (a: ReadonlyVec, b: ReadonlyVec, ia = 0, ib = 0, sa = 1, sb = 1) => a[ia] === b[ib] && a[ia + sa] === b[ib + sb] && @@ -137,6 +152,9 @@ export const floor3 = (a: Vec, ia = 0, sa = 1) => export const ceil3 = (a: Vec, ia = 0, sa = 1) => op3(Math.ceil, a, ia, sa); +export const fract3 = (a: Vec, ia = 0, sa = 1) => + op3(fract1, a, ia, sa); + export const sin3 = (a: Vec, ia = 0, sa = 1) => op3(Math.sin, a, ia, sa); @@ -282,23 +300,55 @@ export const rotateY3 = (a: Vec, theta: number, ia = 0, sa = 1) => export const rotateZ3 = rotate2; +export const rotateAroundAxis3 = (v: Vec, axis: Vec, theta: number, ia = 0, ib = 0, sa = 1, sb = 1) => { + const x = v[ia]; + const y = v[ia + sa]; + const z = v[ia + 2 * sa]; + const ax = axis[ib]; + const ay = axis[ib + sb]; + const az = axis[ib + 2 * sb]; + const ux = ax * x; + const uy = ax * y; + const uz = ax * z; + const vx = ay * x; + const vy = ay * y; + const vz = ay * z; + const wx = az * x; + const wy = az * y; + const wz = az * z; + const uvw = ux + vy + wz; + const s = Math.sin(theta); + const c = Math.cos(theta); + return setS3(v, + (ax * uvw + (x * (ay * ay + az * az) - ax * (vy + wz)) * c + (-wy + vz) * s), + (ay * uvw + (y * (ax * ax + az * az) - ay * (ux + wz)) * c + (wx - uz) * s), + (az * uvw + (z * (ax * ax + ay * ay) - az * (ux + vy)) * c + (-vx + uy) * s), + ia, sa + ); +} + export const headingXY3 = heading2; export const headingXZ3 = (a: ReadonlyVec, ia = 0, sa = 1) => - atan2Abs(a[ia + 2 * sa], a[ia]); + atan2Abs1(a[ia + 2 * sa], a[ia]); export const headingYZ3 = (a: ReadonlyVec, ia = 0, sa = 1) => - atan2Abs(a[ia + 2 * sa], a[ia + sa]); + atan2Abs1(a[ia + 2 * sa], a[ia + sa]); + +export const angleBetween3 = (a: ReadonlyVec, b: ReadonlyVec, normalize = false, ia = 0, ib = 0, sa = 1, sb = 1): number => + normalize ? + angleBetween3(get3(a, ia, sa), get3(b, ib, sb)) : + Math.acos(dot3(a, b, ia, ib, sa, sb)); export const toSpherical3 = (a: Vec, ia = 0, sa = 1) => { const x = a[ia]; const y = a[ia + sa]; const z = a[ia + 2 * sa]; const r = Math.sqrt(x * x + y * y + z * z); - return setS3(a, r, Math.asin(z / r), atan2Abs(y, x), ia, sa); + return setS3(a, r, Math.asin(z / r), atan2Abs1(y, x), ia, sa); }; -export const toCartesian3 = (a: Vec, b: ReadonlyVec = ZERO3, ia = 0, ib = 0, sa = 1, sb = 1) => { +export const toCartesian3 = (a: Vec, b: ReadonlyVec = ZERO4, ia = 0, ib = 0, sa = 1, sb = 1) => { const r = a[ia]; const theta = a[ia + sa]; const phi = a[ia + 2 * sa]; @@ -313,7 +363,7 @@ export const toCartesian3 = (a: Vec, b: ReadonlyVec = ZERO3, ia = 0, ib = 0, sa export const toCylindrical3 = toPolar2; -export const fromCylindrical3 = (a: Vec, b: ReadonlyVec = ZERO3, ia = 0, ib = 0, sa = 1, sb = 1) => { +export const fromCylindrical3 = (a: Vec, b: ReadonlyVec = ZERO4, ia = 0, ib = 0, sa = 1, sb = 1) => { toCartesian2(a, b, ia, ib, sa, sb); a[ia + 2 * sa] += b[ib + 2 * sb]; return a; @@ -363,12 +413,17 @@ export class Vec3 implements return new Vec3(orthoNormal3(a.buf, b.buf, c.buf, a.i, b.i, c.i, a.s, b.s, c.s)); } - static ZERO = Object.freeze(new Vec3(ZERO3)); - static ONE = Object.freeze(new Vec3(ONE3)); + static readonly ZERO = Object.freeze(new Vec3(ZERO4)); + static readonly ONE = Object.freeze(new Vec3(ONE4)); + static readonly MIN = Object.freeze(new Vec3(MIN4)); + static readonly MAX = Object.freeze(new Vec3(MAX4)); buf: Vec; i: number; s: number; + x: number; + y: number; + z: number; [0]: number; [1]: number; [2]: number; @@ -380,37 +435,15 @@ export class Vec3 implements } *[Symbol.iterator]() { - yield this.x; - yield this.y; - yield this.z; - } - - get length() { - return 3; - } - - get x() { - return this.buf[this.i]; - } - - set x(x: number) { - this.buf[this.i] = x; + yield* this.array(); } - get y() { - return this.buf[this.i + this.s]; + array() { + return get3(this.buf, this.i, this.s); } - set y(y: number) { - this.buf[this.i + this.s] = y; - } - - get z() { - return this.buf[this.i + 2 * this.s]; - } - - set z(z: number) { - this.buf[this.i + 2 * this.s] = z; + get length() { + return 3; } copy() { @@ -421,7 +454,7 @@ export class Vec3 implements return v instanceof Vec3 ? equiv3(this.buf, v.buf, this.i, v.i, this.s, v.s) : isArrayLike(v) && v.length === 3 ? - equiv3(this.buf, v, this.i, 0, this.s, 1) : + equiv3(this.buf, v, this.i, 0, this.s, 1) : false; } @@ -449,6 +482,11 @@ export class Vec3 implements return this; } + swap(v: Vec3) { + swap3(this.buf, v.buf, this.i, v.i, this.s, v.s); + return this; + } + add(v: Readonly) { add3(this.buf, v.buf, this.i, v.i, this.s, v.s); return this; @@ -514,6 +552,11 @@ export class Vec3 implements return this; } + fract() { + fract3(this.buf, this.i, this.s); + return this; + } + sqrt() { sqrt3(this.buf, this.i, this.s); return this; @@ -659,6 +702,11 @@ export class Vec3 implements return this; } + rotateAroundAxis(axis: Vec3, theta: number) { + rotateAroundAxis3(this.buf, axis.buf, theta, this.i, axis.i, this.s, axis.s); + return this; + } + headingXY() { return headingXY3(this.buf, this.i, this.s); } @@ -671,6 +719,10 @@ export class Vec3 implements return headingYZ3(this.buf, this.i, this.s); } + angleBetween(v: Readonly, normalize = false) { + return angleBetween3(this.buf, v.buf, normalize, this.i, v.i, this.s, v.s); + } + toSpherical() { toSpherical3(this.buf, this.i, this.s); return this; @@ -694,6 +746,10 @@ export class Vec3 implements toString() { return `[${this.buf[this.i]}, ${this.buf[this.i + this.s]}, ${this.buf[this.i + 2 * this.s]}]`; } + + toJSON() { + return this.array(); + } } declareIndices(Vec3.prototype, [0, 1, 2]); diff --git a/packages/vectors/src/vec4.ts b/packages/vectors/src/vec4.ts index 4f4c34fa25..c0c2303a0f 100644 --- a/packages/vectors/src/vec4.ts +++ b/packages/vectors/src/vec4.ts @@ -6,20 +6,26 @@ import { } from "@thi.ng/api/api"; import { isArrayLike } from "@thi.ng/checks/is-arraylike"; -import { IVec, ReadonlyVec, Vec } from "./api"; +import { + IVec, + MAX4, + MIN4, + ONE4, + ReadonlyVec, + Vec, + ZERO4 +} from "./api"; import { declareIndices } from "./common"; import { EPS, eqDelta1, + fract1, max4id, min4id, smoothStep1, step1 } from "./math"; -export const ZERO4 = Object.freeze([0, 0, 0, 0]); -export const ONE4 = Object.freeze([1, 1, 1, 1]); - export const op4 = (fn: (x: number) => number, a: Vec, ia = 0, sa = 1) => ( a[ia] = fn(a[ia]), a[ia + sa] = fn(a[ia + sa]), @@ -63,7 +69,7 @@ export const setS4 = (a: Vec, x: number, y: number, z: number, w: number, ia = 0 a ); -export const swizzle4 = (a: Vec, b: Vec, x: number, y: number, z: number, w: number, ia = 0, ib = 0, sa = 1, sb = 1) => { +export const swizzle4 = (a: Vec, b: ReadonlyVec, x: number, y: number, z: number, w: number, ia = 0, ib = 0, sa = 1, sb = 1) => { const xx = b[ib + x * sb]; const yy = b[ib + y * sb]; const zz = b[ib + z * sb]; @@ -75,6 +81,17 @@ export const swizzle4 = (a: Vec, b: Vec, x: number, y: number, z: number, w: num return a; }; +export const swap4 = (a: Vec, b: Vec, ia = 0, ib = 0, sa = 1, sb = 1) => { + let t = a[ia]; a[ia] = b[ib]; b[ib] = t; + ia += sa; ib += sb; + t = a[ia]; a[ia] = b[ib]; b[ib] = t; + ia += sa; ib += sb; + t = a[ia]; a[ia] = b[ib]; b[ib] = t; + ia += sa; ib += sb; + t = a[ia]; a[ia] = b[ib]; b[ib] = t; + return a; +}; + export const equiv4 = (a: ReadonlyVec, b: ReadonlyVec, ia = 0, ib = 0, sa = 1, sb = 1) => a[ia] === b[ib] && a[ia + sa] === b[ib + sb] && @@ -146,6 +163,9 @@ export const floor4 = (a: Vec, ia = 0, sa = 1) => export const ceil4 = (a: Vec, ia = 0, sa = 1) => op4(Math.ceil, a, ia, sa); +export const fract4 = (a: Vec, ia = 0, sa = 1) => + op4(fract1, a, ia, sa); + export const sin4 = (a: Vec, ia = 0, sa = 1) => op4(Math.sin, a, ia, sa); @@ -315,12 +335,18 @@ export class Vec4 implements return res; } - static ZERO = Object.freeze(new Vec4(ZERO4)); - static ONE = Object.freeze(new Vec4(ONE4)); + static readonly ZERO = Object.freeze(new Vec4(ZERO4)); + static readonly ONE = Object.freeze(new Vec4(ONE4)); + static readonly MIN = Object.freeze(new Vec4(MIN4)); + static readonly MAX = Object.freeze(new Vec4(MAX4)); buf: Vec; i: number; s: number; + x: number; + y: number; + z: number; + w: number; [0]: number; [1]: number; [2]: number; @@ -333,46 +359,15 @@ export class Vec4 implements } *[Symbol.iterator]() { - yield this.x; - yield this.y; - yield this.z; - yield this.w; - } - - get length() { - return 4; - } - - get x() { - return this.buf[this.i]; - } - - set x(x: number) { - this.buf[this.i] = x; - } - - get y() { - return this.buf[this.i + this.s]; + yield* this.array(); } - set y(y: number) { - this.buf[this.i + this.s] = y; + array() { + return get4(this.buf, this.i, this.s); } - get z() { - return this.buf[this.i + 2 * this.s]; - } - - set z(z: number) { - this.buf[this.i + 2 * this.s] = z; - } - - get w() { - return this.buf[this.i + 3 * this.s]; - } - - set w(w: number) { - this.buf[this.i + 3 * this.s] = w; + get length() { + return 4; } copy() { @@ -383,7 +378,7 @@ export class Vec4 implements return v instanceof Vec4 ? equiv4(this.buf, v.buf, this.i, v.i, this.s, v.s) : isArrayLike(v) && v.length === 4 ? - equiv4(this.buf, v, this.i, 0, this.s, 1) : + equiv4(this.buf, v, this.i, 0, this.s, 1) : false; } @@ -411,6 +406,11 @@ export class Vec4 implements return this; } + swap(v: Vec4) { + swap4(this.buf, v.buf, this.i, v.i, this.s, v.s); + return this; + } + add(v: Readonly) { add4(this.buf, v.buf, this.i, v.i, this.s, v.s); return this; @@ -476,6 +476,11 @@ export class Vec4 implements return this; } + fract() { + fract4(this.buf, this.i, this.s); + return this; + } + sqrt() { sqrt4(this.buf, this.i, this.s); return this; @@ -600,6 +605,10 @@ export class Vec4 implements toString() { return `[${this.buf[this.i]}, ${this.buf[this.i + this.s]}, ${this.buf[this.i + 2 * this.s]}, ${this.buf[this.i + 3 * this.s]}]`; } + + toJSON() { + return this.array(); + } } declareIndices(Vec4.prototype, [0, 1, 2, 3]); diff --git a/scripts/make-module b/scripts/make-module index 9ce8e70bf2..431659b7db 100755 --- a/scripts/make-module +++ b/scripts/make-module @@ -55,7 +55,7 @@ cat << EOF > $MODULE/package.json "typescript": "^3.0.1" }, "dependencies": { - "@thi.ng/api": "^4.0.6" + "@thi.ng/api": "^4.1.0" }, "keywords": [ "ES6", diff --git a/yarn.lock b/yarn.lock index 37ca9107df..8e151c7470 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8,23 +8,23 @@ dependencies: "@babel/highlight" "7.0.0-beta.51" -"@babel/code-frame@7.0.0-rc.2": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-rc.2.tgz#12b6daeb408238360744649d16c0e9fa7ab3859e" +"@babel/code-frame@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" dependencies: - "@babel/highlight" "7.0.0-rc.2" + "@babel/highlight" "^7.0.0" "@babel/core@^7.0.0-beta.46": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.0.0-rc.2.tgz#dcb46b3adb63e35b1e82c35d9130d9c27be58427" - dependencies: - "@babel/code-frame" "7.0.0-rc.2" - "@babel/generator" "7.0.0-rc.2" - "@babel/helpers" "7.0.0-rc.2" - "@babel/parser" "7.0.0-rc.2" - "@babel/template" "7.0.0-rc.2" - "@babel/traverse" "7.0.0-rc.2" - "@babel/types" "7.0.0-rc.2" + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.0.0.tgz#0cb0c0fd2e78a0a2bec97698f549ae9ce0b99515" + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/generator" "^7.0.0" + "@babel/helpers" "^7.0.0" + "@babel/parser" "^7.0.0" + "@babel/template" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" convert-source-map "^1.1.0" debug "^3.1.0" json5 "^0.5.0" @@ -43,11 +43,11 @@ source-map "^0.5.0" trim-right "^1.0.1" -"@babel/generator@7.0.0-rc.2": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-rc.2.tgz#7aed8fb4ef1bdcc168225096b5b431744ba76bf8" +"@babel/generator@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0.tgz#1efd58bffa951dc846449e58ce3a1d7f02d393aa" dependencies: - "@babel/types" "7.0.0-rc.2" + "@babel/types" "^7.0.0" jsesc "^2.5.1" lodash "^4.17.10" source-map "^0.5.0" @@ -61,13 +61,13 @@ "@babel/template" "7.0.0-beta.51" "@babel/types" "7.0.0-beta.51" -"@babel/helper-function-name@7.0.0-rc.2": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-rc.2.tgz#ad7bb9df383c5f53e4bf38c0fe0c7f93e6a27729" +"@babel/helper-function-name@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0.tgz#a68cc8d04420ccc663dd258f9cc41b8261efa2d4" dependencies: - "@babel/helper-get-function-arity" "7.0.0-rc.2" - "@babel/template" "7.0.0-rc.2" - "@babel/types" "7.0.0-rc.2" + "@babel/helper-get-function-arity" "^7.0.0" + "@babel/template" "^7.0.0" + "@babel/types" "^7.0.0" "@babel/helper-get-function-arity@7.0.0-beta.51": version "7.0.0-beta.51" @@ -75,11 +75,11 @@ dependencies: "@babel/types" "7.0.0-beta.51" -"@babel/helper-get-function-arity@7.0.0-rc.2": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-rc.2.tgz#323cb82e2d805b40c0c36be1dfcb8ffcbd0434f3" +"@babel/helper-get-function-arity@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3" dependencies: - "@babel/types" "7.0.0-rc.2" + "@babel/types" "^7.0.0" "@babel/helper-split-export-declaration@7.0.0-beta.51": version "7.0.0-beta.51" @@ -87,19 +87,19 @@ dependencies: "@babel/types" "7.0.0-beta.51" -"@babel/helper-split-export-declaration@7.0.0-rc.2": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-rc.2.tgz#726b2dec4e46baeab32db67caa6e88b6521464f8" +"@babel/helper-split-export-declaration@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz#3aae285c0311c2ab095d997b8c9a94cad547d813" dependencies: - "@babel/types" "7.0.0-rc.2" + "@babel/types" "^7.0.0" -"@babel/helpers@7.0.0-rc.2": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.0.0-rc.2.tgz#e21f54451824f55b4f5022c6e9d6fa7df65e8746" +"@babel/helpers@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.0.0.tgz#7213388341eeb07417f44710fd7e1d00acfa6ac0" dependencies: - "@babel/template" "7.0.0-rc.2" - "@babel/traverse" "7.0.0-rc.2" - "@babel/types" "7.0.0-rc.2" + "@babel/template" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" "@babel/highlight@7.0.0-beta.51": version "7.0.0-beta.51" @@ -109,9 +109,9 @@ esutils "^2.0.2" js-tokens "^3.0.0" -"@babel/highlight@7.0.0-rc.2": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-rc.2.tgz#0af688a69e3709d9cf392e1837cda18c08d34d4f" +"@babel/highlight@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" dependencies: chalk "^2.0.0" esutils "^2.0.2" @@ -121,9 +121,9 @@ version "7.0.0-beta.51" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.0.0-beta.51.tgz#27cec2df409df60af58270ed8f6aa55409ea86f6" -"@babel/parser@7.0.0-rc.2": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.0.0-rc.2.tgz#a98c01af5834e71d48a5108e3aeeee333cdf26c4" +"@babel/parser@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.0.0.tgz#697655183394facffb063437ddf52c0277698775" "@babel/template@7.0.0-beta.51": version "7.0.0-beta.51" @@ -134,13 +134,13 @@ "@babel/types" "7.0.0-beta.51" lodash "^4.17.5" -"@babel/template@7.0.0-rc.2": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-rc.2.tgz#53f6be6c1336ddc7744625c9bdca9d10be5d5d72" +"@babel/template@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0.tgz#c2bc9870405959c89a9c814376a2ecb247838c80" dependencies: - "@babel/code-frame" "7.0.0-rc.2" - "@babel/parser" "7.0.0-rc.2" - "@babel/types" "7.0.0-rc.2" + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.0.0" + "@babel/types" "^7.0.0" "@babel/traverse@7.0.0-beta.51": version "7.0.0-beta.51" @@ -157,16 +157,16 @@ invariant "^2.2.0" lodash "^4.17.5" -"@babel/traverse@7.0.0-rc.2": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-rc.2.tgz#6e54ebe82aa1b3b3cf5ec05594bc14d7c59c9766" - dependencies: - "@babel/code-frame" "7.0.0-rc.2" - "@babel/generator" "7.0.0-rc.2" - "@babel/helper-function-name" "7.0.0-rc.2" - "@babel/helper-split-export-declaration" "7.0.0-rc.2" - "@babel/parser" "7.0.0-rc.2" - "@babel/types" "7.0.0-rc.2" +"@babel/traverse@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0.tgz#b1fe9b6567fdf3ab542cfad6f3b31f854d799a61" + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/generator" "^7.0.0" + "@babel/helper-function-name" "^7.0.0" + "@babel/helper-split-export-declaration" "^7.0.0" + "@babel/parser" "^7.0.0" + "@babel/types" "^7.0.0" debug "^3.1.0" globals "^11.1.0" lodash "^4.17.10" @@ -179,9 +179,9 @@ lodash "^4.17.5" to-fast-properties "^2.0.0" -"@babel/types@7.0.0-rc.2": - version "7.0.0-rc.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-rc.2.tgz#8e025b78764cee8751823e308558a3ca144ebd9d" +"@babel/types@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0.tgz#6e191793d3c854d19c6749989e3bc55f0e962118" dependencies: esutils "^2.0.2" lodash "^4.17.10" @@ -230,8 +230,8 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.5.tgz#8a4accfc403c124a0bafe8a9fc61a05ec1032073" "@types/node@*", "@types/node@^10.5.5": - version "10.9.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.1.tgz#06f002136fbcf51e730995149050bb3c45ee54e6" + version "10.9.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.3.tgz#85f288502503ade0b3bfc049fe1777b05d0327d5" "@types/shelljs@0.7.8": version "0.7.8" @@ -412,6 +412,10 @@ add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" +ajv-errors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59" + ajv-keywords@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" @@ -437,6 +441,10 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" +ansi-colors@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.0.5.tgz#cb9dc64993b64fd6945485f797fc3853137d9a7b" + ansi-escapes@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" @@ -470,11 +478,11 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -append-transform@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" +append-transform@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" dependencies: - default-require-extensions "^1.0.0" + default-require-extensions "^2.0.0" aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" @@ -525,13 +533,6 @@ array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" -array-includes@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" - dependencies: - define-properties "^1.1.2" - es-abstract "^1.7.0" - array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -1040,7 +1041,7 @@ center-align@^0.1.1: chalk@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + resolved "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: ansi-styles "^2.2.1" escape-string-regexp "^1.0.2" @@ -1048,7 +1049,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1: +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -1170,14 +1171,14 @@ collection-visit@^1.0.0: object-visit "^1.0.0" color-convert@^1.9.0: - version "1.9.2" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" dependencies: - color-name "1.1.1" + color-name "1.1.3" -color-name@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" columnify@^1.5.4: version "1.5.4" @@ -1504,7 +1505,7 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^6.0.5: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" dependencies: @@ -1540,12 +1541,6 @@ cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" -d@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" - dependencies: - es5-ext "^0.10.9" - dargs@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17" @@ -1609,12 +1604,25 @@ deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" +default-gateway@^2.6.0: + version "2.7.2" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-2.7.2.tgz#b7ef339e5e024b045467af403d50348db4642d0f" + dependencies: + execa "^0.10.0" + ip-regex "^2.1.0" + default-require-extensions@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" dependencies: strip-bom "^2.0.0" +default-require-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7" + dependencies: + strip-bom "^3.0.0" + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -1798,7 +1806,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.5.1, es-abstract@^1.7.0: +es-abstract@^1.5.1: version "1.12.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" dependencies: @@ -1816,29 +1824,6 @@ es-to-primitive@^1.1.1: is-date-object "^1.0.1" is-symbol "^1.0.1" -es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.46" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.46.tgz#efd99f67c5a7ec789baa3daa7f79870388f7f572" - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.1" - next-tick "1" - -es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-symbol@^3.1.1, es6-symbol@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" - dependencies: - d "1" - es5-ext "~0.10.14" - escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -1897,6 +1882,18 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +execa@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" + dependencies: + cross-spawn "^6.0.0" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" @@ -2267,7 +2264,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob@7.1.2, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2: +glob@7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -2278,6 +2275,17 @@ glob@7.1.2, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glo once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-modules-path@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.3.0.tgz#b0e2bac6beac39745f7db5c59d26a36a0b94f7dc" @@ -2348,10 +2356,6 @@ has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" -has-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" - has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -2588,11 +2592,12 @@ inquirer@^6.0.0: strip-ansi "^4.0.0" through "^2.3.6" -internal-ip@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c" +internal-ip@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-3.0.1.tgz#df5c99876e1d2eb2ea2d74f520e3f669a00ece27" dependencies: - meow "^3.3.0" + default-gateway "^2.6.0" + ipaddr.js "^1.5.2" interpret@^1.0.0, interpret@^1.1.0: version "1.1.0" @@ -2608,6 +2613,10 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -2616,6 +2625,10 @@ ipaddr.js@1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" +ipaddr.js@^1.5.2: + version "1.8.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.1.tgz#fa4b79fa47fd3def5e3b159825161c0a519c9427" + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -2834,7 +2847,7 @@ isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" -istanbul-lib-coverage@^1.1.2, istanbul-lib-coverage@^1.2.0: +istanbul-lib-coverage@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz#f7d8f2e42b97e37fe796114cb0f9d68b5e3a4341" @@ -2843,10 +2856,10 @@ istanbul-lib-coverage@^2.0.1: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#2aee0e073ad8c5f6a0b00e0dfbf52b4667472eda" istanbul-lib-hook@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz#8538d970372cb3716d53e55523dd54b557a8d89b" + version "1.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.1.tgz#f614ec45287b2a8fc4f07f5660af787575601805" dependencies: - append-transform "^0.4.0" + append-transform "^1.0.0" istanbul-lib-instrument@^2.1.0: version "2.3.2" @@ -2861,10 +2874,10 @@ istanbul-lib-instrument@^2.1.0: semver "^5.5.0" istanbul-lib-report@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.3.tgz#2df12188c0fa77990c0d2176d2d0ba3394188259" + version "1.1.4" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.4.tgz#e886cdf505c4ebbd8e099e4396a90d0a28e2acb5" dependencies: - istanbul-lib-coverage "^1.1.2" + istanbul-lib-coverage "^1.2.0" mkdirp "^0.5.1" path-parse "^1.0.5" supports-color "^3.1.2" @@ -3099,23 +3112,10 @@ lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" -log-symbols@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - dependencies: - chalk "^2.0.1" - loglevel@^1.4.1: version "1.6.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa" -loglevelnext@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.5.tgz#36fc4f5996d6640f539ff203ba819641680d75a2" - dependencies: - es6-symbol "^3.1.1" - object.assign "^4.1.0" - long@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -3286,25 +3286,21 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -"mime-db@>= 1.34.0 < 2": +"mime-db@>= 1.34.0 < 2", mime-db@~1.36.0: version "1.36.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397" -mime-db@~1.35.0: - version "1.35.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47" - mime-types@~2.1.17, mime-types@~2.1.18: - version "2.1.19" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.19.tgz#71e464537a7ef81c15f2db9d97e913fc0ff606f0" + version "2.1.20" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19" dependencies: - mime-db "~1.35.0" + mime-db "~1.36.0" mime@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" -mime@^2.1.0: +mime@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369" @@ -3386,7 +3382,7 @@ mixin-deep@^1.2.0: mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" @@ -3445,8 +3441,8 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" nan@^2.9.2: - version "2.10.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" + version "2.11.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.0.tgz#574e360e4d954ab16966ec102c0c049fd961a099" nanomatch@^1.2.9: version "1.2.13" @@ -3480,13 +3476,9 @@ neo-async@^2.5.0: version "2.5.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.2.tgz#489105ce7bc54e709d736b195f82135048c50fcc" -next-tick@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" - nice-try@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" node-forge@0.7.5: version "0.7.5" @@ -3631,7 +3623,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-keys@^1.0.11, object-keys@^1.0.12: +object-keys@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" @@ -3641,15 +3633,6 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - object.getownpropertydescriptors@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" @@ -4333,6 +4316,14 @@ schema-utils@^0.4.4, schema-utils@^0.4.5: ajv "^6.1.0" ajv-keywords "^3.1.0" +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -4807,13 +4798,12 @@ tempfile@^1.1.1: uuid "^2.0.1" test-exclude@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.1.tgz#dfa222f03480bca69207ca728b37d74b45f724fa" + version "4.2.2" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.2.tgz#8b67aa8408f84afc225b06669e25c510f8582820" dependencies: arrify "^1.0.1" - micromatch "^3.1.8" - object-assign "^4.1.0" - read-pkg-up "^1.0.1" + minimatch "^3.0.4" + read-pkg-up "^3.0.0" require-main-filename "^1.0.1" text-extensions@^1.0.0: @@ -4977,7 +4967,7 @@ typedoc@^0.11.1: typescript@2.7.2: version "2.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" + resolved "http://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" typescript@^3.0.1: version "3.0.1" @@ -5129,7 +5119,7 @@ uuid@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" -uuid@^3.0.1, uuid@^3.1.0: +uuid@^3.0.1, uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" @@ -5190,24 +5180,23 @@ webpack-cli@^3.1.0: v8-compile-cache "^2.0.0" yargs "^12.0.1" -webpack-dev-middleware@3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.1.3.tgz#8b32aa43da9ae79368c1bf1183f2b6cf5e1f39ed" +webpack-dev-middleware@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.2.0.tgz#a20ceef194873710052da678f3c6ee0aeed92552" dependencies: loud-rejection "^1.6.0" memory-fs "~0.4.1" - mime "^2.1.0" + mime "^2.3.1" path-is-absolute "^1.0.0" range-parser "^1.0.3" url-join "^4.0.0" - webpack-log "^1.0.1" + webpack-log "^2.0.0" webpack-dev-server@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.5.tgz#87477252e1ac6789303fb8cd3e585fa5d508a401" + version "3.1.7" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.7.tgz#cbf8071cc092d9493732aee4f062f0e065994854" dependencies: ansi-html "0.0.7" - array-includes "^3.0.3" bonjour "^3.5.0" chokidar "^2.0.0" compression "^1.5.2" @@ -5218,12 +5207,13 @@ webpack-dev-server@^3.1.5: html-entities "^1.2.0" http-proxy-middleware "~0.18.0" import-local "^1.0.0" - internal-ip "1.2.0" + internal-ip "^3.0.1" ip "^1.1.5" killable "^1.0.0" loglevel "^1.4.1" opn "^5.1.0" portfinder "^1.0.9" + schema-utils "^1.0.0" selfsigned "^1.9.1" serve-index "^1.7.2" sockjs "0.3.19" @@ -5231,22 +5221,20 @@ webpack-dev-server@^3.1.5: spdy "^3.4.1" strip-ansi "^3.0.0" supports-color "^5.1.0" - webpack-dev-middleware "3.1.3" - webpack-log "^1.1.2" - yargs "11.0.0" + webpack-dev-middleware "3.2.0" + webpack-log "^2.0.0" + yargs "12.0.1" -webpack-log@^1.0.1, webpack-log@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-1.2.0.tgz#a4b34cda6b22b518dbb0ab32e567962d5c72a43d" +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" dependencies: - chalk "^2.1.0" - log-symbols "^2.1.0" - loglevelnext "^1.0.1" - uuid "^3.1.0" + ansi-colors "^3.0.0" + uuid "^3.3.2" webpack-sources@^1.0.1, webpack-sources@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" + version "1.2.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.2.0.tgz#18181e0d013fce096faf6f8e6d41eeffffdceac2" dependencies: source-list-map "^2.0.0" source-map "~0.6.1" @@ -5419,23 +5407,6 @@ yargs-parser@^9.0.2: dependencies: camelcase "^4.1.0" -yargs@11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.0.0.tgz#c052931006c5eee74610e5fc0354bedfd08a201b" - dependencies: - cliui "^4.0.0" - decamelize "^1.1.1" - find-up "^2.1.0" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^9.0.2" - yargs@11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" @@ -5453,7 +5424,7 @@ yargs@11.1.0: y18n "^3.2.1" yargs-parser "^9.0.2" -yargs@^12.0.1: +yargs@12.0.1, yargs@^12.0.1: version "12.0.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.1.tgz#6432e56123bb4e7c3562115401e98374060261c2" dependencies: