From bda242a538ceb55fc3bfce3cd54928613e58d9ba Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Tue, 20 Aug 2019 14:32:51 +0200 Subject: [PATCH] Rename loop to component and handle two return values --- src/component.ts | 42 ++++++++++++++++++++++++++++++++---------- test/component.spec.ts | 19 +++++++++++-------- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/component.ts b/src/component.ts index 84da745..edea2d1 100644 --- a/src/component.ts +++ b/src/component.ts @@ -84,6 +84,9 @@ export abstract class Component implements Monad { ); } } + result(o: R): Result { + return { output: o, child: this }; + } view(): Component { return view(this); } @@ -253,9 +256,17 @@ const placeholderProxyHandler = { } }; -class LoopComponent extends Component { +type Result = { output: R; child: Child }; + +function isLoopResult(r: any): r is Result { + return typeof r === "object" && "child" in r; +} + +class LoopComponent extends Component { constructor( - private f: (o: O) => Child | Now>, + private f: ( + o: L + ) => Child | Now> | Result | Now>, private placeholderNames?: string[] ) { super(); @@ -272,28 +283,39 @@ class LoopComponent extends Component { } } const res = this.f(placeholderObject); - const child = Now.is(res) ? runNow(res) : res; - const { output } = toComponent(child).run(parent, destroyed); + const result = Now.is(res) ? runNow | Result>(res) : res; + const { output, child } = isLoopResult(result) + ? result + : { output: {} as O, child: result }; + const { output: looped } = toComponent(child).run(parent, destroyed); const needed = Object.keys(placeholderObject); for (const name of needed) { if (name === "destroyed") { continue; } - if (output[name] === undefined) { + if (looped[name] === undefined) { throw new Error(`The property ${name} is missing.`); } - placeholderObject[name].replaceWith(output[name]); + placeholderObject[name].replaceWith(looped[name]); } return { available: output, output: {} }; } } -export function loop( - f: (o: O) => Child | Now>, +export function component( + f: (l: L) => Child | Now>, + placeholderNames?: string[] +): Component<{}, {}>; +export function component( + f: (l: L) => Result | Now>, + placeholderNames?: string[] +): Component; +export function component( + f: (l: L) => Child | Now> | Result | Now>, placeholderNames?: string[] ): Component { - const f2 = isGeneratorFunction(f) ? fgo(f) : f; - return new LoopComponent(f2, placeholderNames); + const f2 = isGeneratorFunction(f) ? fgo(f) : f; + return new LoopComponent(f2, placeholderNames); } class MergeComponent< diff --git a/test/component.spec.ts b/test/component.spec.ts index f7970aa..785ebdd 100644 --- a/test/component.spec.ts +++ b/test/component.spec.ts @@ -15,7 +15,7 @@ import { view, emptyComponent, elements, - loop, + component, testComponent, list, runComponent, @@ -221,23 +221,26 @@ describe("component specs", () => { }); }); - describe("loop", () => { + describe("component loop", () => { type Looped = { name: H.Behavior; destroyed: H.Future }; it("passed selected output as argument", () => { let b: H.Behavior | undefined = undefined; - const comp = loop<{ foo: H.Behavior }>((input) => { + const comp = component< + { foo: H.Behavior }, + { bar: H.Behavior } + >((input) => { b = input.foo; return Component.of({ foo: H.Behavior.of(2) - }); + }).result({ bar: H.Behavior.of(3) }); }); const { available, output } = testComponent(comp); assert.deepEqual(Object.keys(output), []); - assert.deepEqual(Object.keys(available), ["foo"]); + assert.deepEqual(Object.keys(available), ["bar"]); expect(H.at(b!)).to.equal(2); }); it("works with selected fgo and looped behavior", () => { - const comp = loop( + const comp = component( fgo(function*({ name }: Looped): IterableIterator> { yield div(name); ({ name } = yield input({ props: { value: "Foo" } }).output({ @@ -252,7 +255,7 @@ describe("component specs", () => { }); it("can be told to destroy", () => { let toplevel = false; - const comp = loop( + const comp = component( fgo(function*({ name, destroyed @@ -273,7 +276,7 @@ describe("component specs", () => { expect(toplevel).to.equal(true); }); it("throws helpful error is a reactive is missing", () => { - const c = loop((props: { foo: H.Behavior }) => { + const c = component((props: { foo: H.Behavior }) => { // Access something that isn't there (props as any).bar; return div([dynamic(props.foo)]).output((_) => ({