diff --git a/examples/json-components/README.md b/examples/json-components/README.md new file mode 100644 index 0000000000..ff80cd54ce --- /dev/null +++ b/examples/json-components/README.md @@ -0,0 +1,12 @@ +# json-components + +[Live demo](http://demo.thi.ng/umbrella/json-components/) + +``` +git clone https://github.com/thi-ng/umbrella.git +cd umbrella/examples/json-components +yarn install +yarn dev +``` + +Once webpack has completed building, refresh your browser... diff --git a/examples/json-components/package.json b/examples/json-components/package.json new file mode 100644 index 0000000000..b720622166 --- /dev/null +++ b/examples/json-components/package.json @@ -0,0 +1,23 @@ +{ + "name": "json-components", + "version": "0.0.1", + "repository": "https://github.com/thi-ng/umbrella", + "author": "Karsten Schmidt ", + "license": "Apache-2.0", + "scripts": { + "build": "yarn clean && webpack", + "clean": "rm -rf bundle.*", + "dev": "open index.html && webpack -w" + }, + "devDependencies": { + "ts-loader": "^3.3.1", + "typescript": "^2.7.1", + "webpack": "^3.10.0" + }, + "dependencies": { + "@thi.ng/api": "^2.0.1", + "@thi.ng/hiccup-dom": "^1.0.0", + "@thi.ng/rstream": "^0.9.2", + "@thi.ng/transducers": "^1.0.7" + } +} \ No newline at end of file diff --git a/examples/json-components/src/index.ts b/examples/json-components/src/index.ts new file mode 100644 index 0000000000..08f7a5b16d --- /dev/null +++ b/examples/json-components/src/index.ts @@ -0,0 +1,78 @@ +import { isFunction } from "@thi.ng/checks/is-function"; +import { start } from "@thi.ng/hiccup-dom"; + +// some dummy JSON records +export const db = [ + { + meta: { + author: { + name: "Alice Bobbera", + email: "a@b.it" + }, + created: "2018-02-03T12:13:14Z", + tags: ["drama", "queen"] + }, + title: "'I love my books, all three of them'", + content: "Sed doloribus molestias voluptatem ut delectus vitae quo eum. Ut praesentium sed omnis sequi rerum praesentium aperiam modi. Occaecati voluptatum quis vel facere quis quisquam." + }, + { + meta: { + author: { + name: "Charlie Doran", + email: "c@d.es" + }, + created: "2018-02-02T01:23:45Z", + tags: ["simplicity", "rules"] + }, + title: "Look ma, so simple", + content: "Ratione necessitatibus doloremque itaque. Nihil hic alias cumque beatae esse sapiente incidunt. Illum vel eveniet officia." + } +]; + +// component function for individual keys in the JSON objects +const summary = (item) => ["div.item", item.title, item.meta, item.content]; +const meta = (meta) => ["div.meta", meta.author, meta.created, meta.tags]; +const author = (author) => ["div", ["strong", "author: "], link(`mailto:${author.email}`, author.name)]; +const date = (d) => ["div", ["strong", "date: "], new Date(Date.parse(d)).toLocaleString()]; +const link = (href, body) => ["a", { href }, body]; +const tag = (t) => ["li", link("#", t)]; +const tags = (tags) => ["ul.tags", ...tags.map(tag)]; +const title = (title, level = 3) => [`h${level}`, title]; +const content = (body) => ["div", body]; + +// generic JSON object tree transformer +// called with a nested object spec reflecting the structure +// of the source data and returns composed component function +export const componentFromSpec = (spec) => { + if (isFunction(spec)) { + return spec; + } + const mapfns = Object.keys(spec[1]).reduce( + (acc, k) => (acc[k] = componentFromSpec(spec[1][k]), acc), + {} + ); + return (x) => { + const res = {}; + for (let k in mapfns) { + res[k] = mapfns[k](x[k]); + } + return spec[0](res); + }; +}; + +// build component function for the above JSON object format +// the spec is an array of this recursive structure: [mapfn, {optional chid key specs...}] +// for leaf keys only a function needs to be given, no need to wrap in array +// giving component functions the same name as their object keys +// makes this format very succinct +const item = componentFromSpec([ + summary, + { + meta: [meta, { author, tags, created: date }], + title, + content + } +]); + +// start UI +start(document.getElementById("app"), ["div", ...db.map(item)]); diff --git a/examples/json-components/tsconfig.json b/examples/json-components/tsconfig.json new file mode 100644 index 0000000000..bd6481a5a6 --- /dev/null +++ b/examples/json-components/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "." + }, + "include": [ + "./src/**/*.ts" + ] +}