function Main(sources: {state: StateSource<any>}) { sources.state.stream.addListener({ next(x) { assert.deepEqual(x.list, expected.shift()); }, error(e) { done(e.message); }, complete() { done('complete should not be called'); }, }); const childSinks = isolate(List, 'list')(sources); const childReducer$ = childSinks.state; const initReducer$ = xs.of(function initReducer(prevState: any): any { return {list: [{val: 3}]}; }); const addReducer$ = xs .of(function addSecond(prev: any) { return {list: prev.list.concat({val: null})}; }) .compose(delay(100)); const parentReducer$ = xs.merge(initReducer$, addReducer$); const reducer$ = xs.merge(parentReducer$, childReducer$) as Stream< Reducer<any> >; return { state: reducer$, }; }
function Client (sources) { const stateUpdate$ = sources.Socket.messages .filter(message => message.type === 'UPDATE_STATE') .map(message => message.data); const stateOverride$ = stateUpdate$ .map(serverState => ({type: 'OVERRIDE', data: serverState})); const id$ = sources.Socket.messages .filter(message => message.type === 'SET_ID') .map(message => message.data) .remember(); const move$ = sources.DOM .select('svg') .events('click') .map(mousePosition) .map(destination => ({type: 'MOVE', data: destination})); const attack$ = sources.DOM .select('.enemy') .events('click') .debug(event => event.stopPropagation()) .map(event => ({type: 'ATTACK', data: event.target.id})); const chat$ = sources.DOM .select('document') .events('keydown') .map(event => ({type: 'CHAT', data: event.key})); const action$ = xs.merge( move$, chat$, attack$ ); const gameActionWithId$ = id$ .map(id => action$.map(action => ({...action, id}))) .flatten(); const gameAction$ = xs.merge(gameActionWithId$, stateOverride$); const state$ = Game({ Animation: sources.Animation, action$: gameAction$ as Stream<Action> }); return { DOM: xs.combine(id$, state$).map(view), Socket: action$ } }
function ProjectName(sources: IProjectNameSources): IProjectNameSinks { const startEditing$ = sources.DOM .select(".edit-project-name") .events("click") .mapTo(true); const save$ = sources.DOM .select(".save-project-name") .events("click") .mapTo(false); const cancelEditing$ = sources.DOM .select(".cancel-editing-project-name") .events("click") .mapTo(false); const editing$ = xs.merge(xs.of(false), startEditing$, save$, cancelEditing$); const newName$ = sources.DOM .select(".project-name-input") .events("input") .map((ev: any) => ev.currentTarget.value); const nameChange$ = newName$.map(name => save$.mapTo(name)).flatten(); function view([name, editing]: [string, boolean]): VNode { if (editing) { return div(".project-name-container", [ div([ input(".project-name-input", { props: { value: name } }), a(".save-project-name", " ✓ "), a(".cancel-editing-project-name", " ✖ ") ]) ]); } return div(".project-name-container", [ div([span(".project-name", `${name}`), a(".edit-project-name", " ✎ ")]) ]); } return { DOM: xs.combine(sources.name$, editing$).map(view), name$: xs.merge(sources.name$, nameChange$), nameChange$ }; }
function DOMDriver(vnode$: Stream<VNode>, name = 'DOM'): MainDOMSource { domDriverInputGuard(vnode$); const sanitation$ = xs.create<null>(); const rootElement$ = xs .merge(vnode$.endWhen(sanitation$), sanitation$) .map(vnode => vnodeWrapper.call(vnode)) .fold(patch, toVNode(rootElement)) .drop(1) .map(unwrapElementFromVNode) .compose(dropCompletion) // don't complete this stream .startWith(rootElement as any); // Start the snabbdom patching, over time const listener = {error: reportSnabbdomError}; if (document.readyState === 'loading') { document.addEventListener('readystatechange', () => { if (document.readyState === 'interactive') { rootElement$.addListener(listener); } }); } else { rootElement$.addListener(listener); } return new MainDOMSource( rootElement$, sanitation$, [], isolateModule, delegators, name, ); }
function Counter(sources: Sources): Sinks { const IncrementButton = isolate(Button); const DecrementButton = isolate(Button); let incrementButtonProps$ = xs.of<ButtonProps>({ text: 'Increment', amount: 1 }).remember(); let decrementButtonProps$ = xs.of<ButtonProps>({ text: 'Decrement', amount: -1 }).remember(); let incrementButton = IncrementButton({DOM: sources.DOM, props$: incrementButtonProps$}); let decrementButton = DecrementButton({DOM: sources.DOM, props$: decrementButtonProps$}); let count$ = xs.merge(incrementButton.delta$, decrementButton.delta$) .fold((acc, x) => acc + x, 0); return { DOM: xs.combine(count$, incrementButton.DOM, decrementButton.DOM) .map(([count, incrementVTree, decrementVTree]) => div([ incrementVTree, decrementVTree, h2('Clicks: ' + count), ]) ) }; }
return function (): Stream<Input> { return xs.merge( fromEvent(window, 'keydown') .map((e: KeyboardEvent) => { switch (e.keyCode) { case 74: return xs.of('up'); case 75: return xs.of('down'); default: return xs.empty(); } }).flatten(), fromEvent(window, 'keyup') .map((e: KeyboardEvent) => { switch (e.keyCode) { case 188: return xs.of('decrement'); case 190: return xs.of('increment'); default: return xs.empty(); } }).flatten() ); }
function Main(sources: {state: StateSource<any>}) { sources.state.stream.addListener({ next(x) { assert.deepEqual(x.wrap, expected.shift()); }, error(e) { done(e.message); }, complete() { done('complete should not be called'); }, }); const wrapperSinks = isolate(Wrapper, 'wrap')(sources); const wrapperReducer$ = wrapperSinks.state; const initReducer$ = xs.of(function initReducer(prevState: any): any { return {wrap: {key: 'a', val: null}}; }); const reducer$ = xs.merge(initReducer$, wrapperReducer$) as Stream< Reducer<any> >; return { state: reducer$, }; }
function main(sources: {state: StateSource<any>}) { const expected = [7, 9]; sources.state.stream.addListener({ next(x) { assert.strictEqual(x.child.count, expected.shift()); calledMain += 1; }, error(e) { done(e); }, complete() {}, }); const childSinks = child({state: isolateSource(sources.state, 'child')}); assert(childSinks.state); const childReducer$ = isolateSink(childSinks.state, 'child'); const parentReducer$ = xs.of(function initReducer(prevState: any): any { return {child: {count: 7}}; }); const reducer$ = xs.merge(parentReducer$, childReducer$); return { state: reducer$, }; }
function main(sources: {state: StateSource<any>}) { assert(sources.state); assert(sources.state.stream); const expected = [1, 0, -1]; sources.state.stream.addListener({ next(x) { assert.strictEqual(x.count, expected.shift()); calledMain += 1; }, error(e) { done(e); }, complete() {}, }); const childSinks = isolate(child, 'count')(sources); assert(childSinks.state); const childReducer$ = childSinks.state; const parentReducer$ = xs.of(function initReducer(prevState: any): any { return {count: 1}; }); const reducer$ = xs.merge(parentReducer$, childReducer$) as Stream< Reducer<any> >; return { state: reducer$, }; }
function main(sources: {state: StateSource<any>}) { assert(sources.state); assert(sources.state.stream); const expected = [[3, 5, 6], [3, 15, 6], [3, 6]]; sources.state.stream.addListener({ next(x) { assert.deepEqual(x, expected.shift()); calledMain += 1; }, error(e) { done(e); }, complete() {}, }); const childSinks = isolate(secondEntry, 1)(sources); assert(childSinks.state); const childReducer$ = childSinks.state; const parentReducer$ = xs.of(function initReducer(prevState: any): any { return [3, 5, 6]; }); const reducer$ = xs.merge(parentReducer$, childReducer$) as Stream< Reducer<any> >; return { state: reducer$, }; }