From 947b75616b5f552710b546f31111b4fccde2cf6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emil=20Gj=C3=B8rup?= Date: Wed, 28 Aug 2019 16:14:31 +0200 Subject: [PATCH] examples use component function --- examples/continuous-time/index.ts | 49 +++++--------- examples/counters/src/index.ts | 17 ++--- examples/counters/src/version1.ts | 9 ++- examples/counters/src/version2.ts | 9 ++- examples/counters/src/version3.ts | 17 +++-- examples/counters/src/version4.ts | 21 +++--- examples/drag/src/index.ts | 107 +++++++++++------------------- examples/zip-codes/index.ts | 76 ++++++++------------- 8 files changed, 117 insertions(+), 188 deletions(-) diff --git a/examples/continuous-time/index.ts b/examples/continuous-time/index.ts index 004f45d..c874f87 100644 --- a/examples/continuous-time/index.ts +++ b/examples/continuous-time/index.ts @@ -1,46 +1,27 @@ -import { - Behavior, - map, - snapshot, - stepper, - Stream, - time -} from "@funkia/hareactive"; -import { elements, fgo, modelView, runComponent } from "../../src/index"; +import { map, snapshot, stepper, Stream, time } from "@funkia/hareactive"; +import { elements as E, runComponent, component } from "../../src/index"; -const { p, button, h1 } = elements; +const formatTime = (t: number) => new Date(t).toTimeString().split(" ")[0]; -const formatTime = (t: number): string => - new Date(t).toTimeString().split(" ")[0]; - -type ToView = { - time: Behavior; - message: Behavior; -}; - -type ViewOut = { +type On = { snapClick: Stream; }; -const model = fgo(function*({ snapClick }: ViewOut) { +const main = component((on, start) => { const msgFromClick = map( (t) => "You last pressed the button at " + formatTime(t), - snapshot(time, snapClick) + snapshot(time, on.snapClick) ); - const message = yield stepper( - "You've not clicked the button yet", - msgFromClick + const message = start( + stepper("You've not clicked the button yet", msgFromClick) ); - return { time, message }; -}); - -const view = ({ time, message }: ToView) => [ - h1("Continuous time example"), - p(map(formatTime, time)), - p(button("Click to snap time").use({ snapClick: "click" })), - p(message) -]; -const main = modelView(model, view)(); + return [ + E.h1("Continuous time example"), + E.p(map(formatTime, time)), + E.p(E.button("Click to snap time").use({ snapClick: "click" })), + E.p(message) + ]; +}); runComponent("#mount", main); diff --git a/examples/counters/src/index.ts b/examples/counters/src/index.ts index 897ba3a..361a78c 100644 --- a/examples/counters/src/index.ts +++ b/examples/counters/src/index.ts @@ -1,20 +1,17 @@ import { Behavior, stepper, Stream } from "@funkia/hareactive"; -import { go } from "@funkia/jabz"; -import { elements, fgo, runComponent, view, component } from "../../../src"; +import { elements as E, runComponent, view, component } from "../../../src"; import { main1 } from "./version1"; import { main2 } from "./version2"; import { main3 } from "./version3"; import { main4 } from "./version4"; -const { button, div } = elements; - const numberToApp = { "1": main1, "2": main2, "3": main3, "4": main4 }; type AppId = keyof (typeof numberToApp); const selectorButton = (n: AppId, selected: Behavior) => view( - button( + E.button( { class: ["btn btn-default", { active: selected.map((m) => n === m) }] }, @@ -26,13 +23,13 @@ type On = { selectVersion: Stream; }; -type FromModel = { +type Output = { selected: Behavior; }; -const versionSelector = component((on, start) => { +const versionSelector = component((on, start) => { const selected = start(stepper("1", on.selectVersion)); - return div({ class: "btn-group" }, [ + return E.div({ class: "btn-group" }, [ selectorButton("1", selected).use({ selectVersion: "selectVersion" }), selectorButton("2", selected).use({ selectVersion: "selectVersion" }), selectorButton("3", selected).use({ selectVersion: "selectVersion" }), @@ -40,9 +37,9 @@ const versionSelector = component((on, start) => { ]).output({ selected }); }); -const main = component((on) => { +const main = component((on) => { const currentApp = on.selected.map((n: AppId) => numberToApp[n]); - return [versionSelector.use({ selected: "selected" }), div(currentApp)]; + return [versionSelector.use({ selected: "selected" }), E.div(currentApp)]; }); runComponent("#mount", main); diff --git a/examples/counters/src/version1.ts b/examples/counters/src/version1.ts index 11f8c3f..bb92521 100644 --- a/examples/counters/src/version1.ts +++ b/examples/counters/src/version1.ts @@ -1,14 +1,13 @@ -import { elements } from "../../../src"; -const { div, button } = elements; +import { elements as E } from "../../../src"; // Counter -const counterView = div([ +const counterView = E.div([ "Counter ", 1, " ", - button({ class: "btn btn-default" }, "+"), + E.button({ class: "btn btn-default" }, "+"), " ", - button({ class: "btn btn-default" }, " - ") + E.button({ class: "btn btn-default" }, " - ") ]); export const main1 = counterView; diff --git a/examples/counters/src/version2.ts b/examples/counters/src/version2.ts index 6e52d26..d16f81f 100644 --- a/examples/counters/src/version2.ts +++ b/examples/counters/src/version2.ts @@ -1,6 +1,5 @@ import { accum, Stream, combine } from "@funkia/hareactive"; -import { elements, component } from "../../../src"; -const { div, button } = elements; +import { elements as E, component } from "../../../src"; type On = { incrementClick: Stream; @@ -13,15 +12,15 @@ const counter = component((on, start) => { const changes = combine(increment, decrement); const count = start(accum((n, m) => n + m, 0, changes)); - return div([ + return E.div([ "Counter ", count, " ", - button({ class: "btn btn-default" }, " + ").use({ + E.button({ class: "btn btn-default" }, " + ").use({ incrementClick: "click" }), " ", - button({ class: "btn btn-default" }, " - ").use({ + E.button({ class: "btn btn-default" }, " - ").use({ decrementClick: "click" }) ]); diff --git a/examples/counters/src/version3.ts b/examples/counters/src/version3.ts index 28de83f..934a8dc 100644 --- a/examples/counters/src/version3.ts +++ b/examples/counters/src/version3.ts @@ -1,7 +1,6 @@ import { Behavior, map, accum, scan, Stream } from "@funkia/hareactive"; -import { elements, list, component } from "../../../src"; -const { br, li, button, h1, ul } = elements; +import { elements as E, list, component } from "../../../src"; const add = (n: number, m: number) => n + m; const apply = (f: (a: A) => B, a: A) => f(a); @@ -18,15 +17,15 @@ const counter = () => component((on, start) => { const count = start(accum(add, 0, on.delta)); - return li([ + return E.li([ "Counter ", count, " ", - button({ class: "btn btn-default" }, " + ").use((o) => ({ + E.button({ class: "btn btn-default" }, " + ").use((o) => ({ delta: o.click.mapTo(1) })), " ", - button({ class: "btn btn-default" }, " - ").use((o) => ({ + E.button({ class: "btn btn-default" }, " - ").use((o) => ({ delta: o.click.mapTo(-1) })) ]).output({ count }); @@ -44,12 +43,12 @@ const counterList = component(({ addCounter }, start) => { ); const counterIds = start(accum(apply, [0], appendCounterFn)); return [ - h1("Counters"), - button({ class: "btn btn-primary" }, "Add counter").use({ + E.h1("Counters"), + E.button({ class: "btn btn-primary" }, "Add counter").use({ addCounter: "click" }), - br, - ul(list(counter, counterIds)) + E.br, + E.ul(list(counter, counterIds)) ]; }); diff --git a/examples/counters/src/version4.ts b/examples/counters/src/version4.ts index c67df2b..3d61314 100644 --- a/examples/counters/src/version4.ts +++ b/examples/counters/src/version4.ts @@ -10,8 +10,7 @@ import { moment } from "@funkia/hareactive"; -import { list, elements, component } from "../../../src"; -const { ul, li, p, br, button, h1 } = elements; +import { list, elements as E, component } from "../../../src"; const add = (n: number, m: number) => n + m; const apply = (f: (a: A) => A, a: A) => f(a); @@ -35,19 +34,19 @@ const counter = (id: Id) => const deleteS = on.deleteClick.mapTo(id); const count = start(accum(add, 0, on.delta)); - return li([ + return E.li([ "Counter ", count, " ", - button({ class: "btn btn-default" }, " + ").use((o) => ({ + E.button({ class: "btn btn-default" }, " + ").use((o) => ({ delta: o.click.mapTo(1) })), " ", - button({ class: "btn btn-default" }, " - ").use((o) => ({ + E.button({ class: "btn btn-default" }, " - ").use((o) => ({ delta: o.click.mapTo(-1) })), " ", - button({ class: "btn btn-default" }, "x").use({ + E.button({ class: "btn btn-default" }, "x").use({ deleteClick: "click" }) ]).output({ count, deleteS }); @@ -75,13 +74,13 @@ const counterList = component((on, start) => { const modifications = combine(appendCounterFn, removeCounterIdFn); const counterIds = start(accum(apply, [0, 1, 2], modifications)); return [ - h1("Counters"), - p(["Sum ", sum]), - button({ class: "btn btn-primary" }, "Add counter").use({ + E.h1("Counters"), + E.p(["Sum ", sum]), + E.button({ class: "btn btn-primary" }, "Add counter").use({ addCounter: "click" }), - br, - ul( + E.br, + E.ul( list((n) => counter(n).use((o) => o), counterIds).use((o) => ({ listOut: o })) diff --git a/examples/drag/src/index.ts b/examples/drag/src/index.ts index 1d47fb5..29306d4 100644 --- a/examples/drag/src/index.ts +++ b/examples/drag/src/index.ts @@ -1,26 +1,13 @@ -import { - map, - streamFromEvent, - stepper, - toggle, - snapshot, - switcher, - Behavior, - combine, - lift, - runNow, - Stream, - accum -} from "@funkia/hareactive"; -import { runComponent, elements, go, fgo, modelView } from "../../../src"; -const { div } = elements; +import * as H from "@funkia/hareactive"; +import { streamFromEvent } from "@funkia/hareactive/dom"; +import { runComponent, elements as E, component } from "../../../src"; type Point = { x: number; y: number }; -const mousemove = streamFromEvent(window, "mousemove"); -const mousePosition = runNow( - stepper({ x: 0, y: 0 }, mousemove.map((e) => ({ x: e.pageX, y: e.pageY }))) -); +const mousemove = streamFromEvent(window, "mousemove", (e) => ({ + x: e.pageX, + y: e.pageY +})); const addPoint = (p1: Point, p2: Point) => ({ x: p1.x + p2.x, @@ -28,52 +15,38 @@ const addPoint = (p1: Point, p2: Point) => ({ }); type BoxModelInput = { - startDrag: Stream; - endDrag: Stream; + startDrag: H.Stream; + endDrag: H.Stream; }; -const boxModel = fgo(function*( - { startDrag, endDrag }: BoxModelInput, - color: string -) { - const startDragAt = snapshot(mousePosition, startDrag); - const dragOffset = map( - (p) => map((p2) => ({ x: p2.x - p.x, y: p2.y - p.y }), mousePosition), - startDragAt - ); - const offset: Behavior = yield switcher( - Behavior.of({ x: 0, y: 0 }), - combine(dragOffset, endDrag.mapTo(Behavior.of({ x: 0, y: 0 }))) - ); - const committed: Behavior = yield accum( - addPoint, - { x: 0, y: 0 }, - snapshot(offset, endDrag) - ); - const position = lift(addPoint, committed, offset); - const isBeingDragged = yield toggle(false, startDrag, endDrag); - return { isBeingDragged, position }; -}); - -type BoxViewInput = { - position: Behavior; - isBeingDragged: Behavior; -}; - -const boxView = ({ isBeingDragged, position }: BoxViewInput, color: string) => - div({ - class: ["box", { dragged: isBeingDragged }], - style: { - background: color, - left: position.map(({ x }) => x + "px"), - top: position.map(({ y }) => y + "px") - } - }).use({ startDrag: "mousedown", endDrag: "mouseup" }); - -const box = modelView(boxModel, boxView); - -const main = go(function*() { - yield box("red"); -}); - -runComponent("#mount", main); +const box = (color: string) => + component((on, start) => { + const mousePosition = start(H.stepper({ x: 0, y: 0 }, mousemove)); + const startDragAt = H.snapshot(mousePosition, on.startDrag); + const dragOffset = H.map( + (p) => H.map((p2) => ({ x: p2.x - p.x, y: p2.y - p.y }), mousePosition), + startDragAt + ); + const offset: H.Behavior = start( + H.switcher( + H.Behavior.of({ x: 0, y: 0 }), + H.combine(dragOffset, on.endDrag.mapTo(H.Behavior.of({ x: 0, y: 0 }))) + ) + ); + const committed: H.Behavior = start( + H.accum(addPoint, { x: 0, y: 0 }, H.snapshot(offset, on.endDrag)) + ); + const position = H.lift(addPoint, committed, offset); + const isBeingDragged = start(H.toggle(false, on.startDrag, on.endDrag)); + + return E.div({ + class: ["box", { dragged: isBeingDragged }], + style: { + background: color, + left: position.map(({ x }) => x + "px"), + top: position.map(({ y }) => y + "px") + } + }).use({ startDrag: "mousedown", endDrag: "mouseup" }); + }); + +runComponent("#mount", box("red")); diff --git a/examples/zip-codes/index.ts b/examples/zip-codes/index.ts index 13b4eef..1e086ea 100644 --- a/examples/zip-codes/index.ts +++ b/examples/zip-codes/index.ts @@ -1,12 +1,4 @@ -import { - Behavior, - changes, - Now, - performStreamLatest, - split, - stepper, - Stream -} from "@funkia/hareactive"; +import * as H from "@funkia/hareactive"; import { catchE, combine, @@ -16,9 +8,7 @@ import { right, withEffectsP } from "@funkia/jabz"; -import { elements, modelView, runComponent, fgo } from "../../src/index"; - -const { span, br, input } = elements; +import { elements as E, runComponent, component } from "../../src/index"; const apiUrl = "http://api.zippopotam.us/us/"; @@ -30,7 +20,7 @@ const fetchJSON = withEffectsP( } ); -function fetchZip(zip: string): IO> { +function fetchZip(zip: string): IO> { return catchE((err) => IO.of(left(err)), fetchJSON(apiUrl + zip).map(right)); } @@ -46,54 +36,46 @@ type ZipResult = { places: Place[]; }; -type ToView = { - status: Behavior; -}; - type ViewOut = { - zipCode: Behavior; + zipCode: H.Behavior; }; -const model = fgo(function*({ zipCode }: ViewOut): Iterator> { - // A stream that occurs whenever the current zip code changes - const zipCodeChange = changes(zipCode); +const main = component((on, start) => { + const zipCodeChange = H.changes(on.zipCode); // Split the zip code changes into those that represent valid zip // codes and those that represent invalid zip codes. - const [validZipCodeChange, invalidZipCodeChange] = split( + const [validZipCodeChange, invalidZipCodeChange] = H.split( isValidZip, zipCodeChange ); // A stream of IO requests for each time the zipCode changes const requests = validZipCodeChange.map(fetchZip); // A stream of results obtained from performing the IO requests - const results: Stream> = yield performStreamLatest( - requests - ); - const status = yield stepper( - "", - combine( - invalidZipCodeChange.mapTo("Not a valid zip code"), - validZipCodeChange.mapTo("Loading ..."), - results.map((r) => - r.match({ - left: () => "Zip code does not exist", - right: (res) => `Valid zip code for ${res.places[0]["place name"]}` - }) + const results = start(H.performStreamLatest(requests)); + const status = start( + H.stepper( + "", + combine( + invalidZipCodeChange.mapTo("Not a valid zip code"), + validZipCodeChange.mapTo("Loading ..."), + results.map((r) => + r.match({ + left: () => "Zip code does not exist", + right: (res) => `Valid zip code for ${res.places[0]["place name"]}` + }) + ) ) ) ); - return { status }; -}); -const view = ({ status }: ToView) => [ - span("Please type a valid US zip code: "), - input({ - props: { placeholder: "Zip code" } - }).use({ zipCode: "value" }), - br, - span(status) -]; - -const main = modelView(model, view)(); + return [ + E.span("Please type a valid US zip code: "), + E.input({ + props: { placeholder: "Zip code" } + }).use({ zipCode: "value" }), + E.br, + E.span(status) + ]; +}); runComponent("#mount", main);