Skip to content

Instantly share code, notes, and snippets.

@thenikso
Created January 7, 2016 21:38
Show Gist options
  • Save thenikso/186576bf315971e22c82 to your computer and use it in GitHub Desktop.
Save thenikso/186576bf315971e22c82 to your computer and use it in GitHub Desktop.
Cycle.js component maker helper function
import { Observable } from 'rx';
import Cycle from '@cycle/core';
import { makeDOMDriver, hJSX } from '@cycle/dom';
import isolate from '@cycle/isolate';
// A helper method to streamline the creation of CycleJS components
export function makeComponent(...funcs) {
return (isolationName) => isolate(function(sources) {
const sinks = funcs.reduce((steps, f) => {
const firstArg = steps.slice(steps.length - 1);
const otherArgs = steps.slice(0, steps.length - 1);
return steps.concat(f(...firstArg, ...otherArgs));
}, [sources]);
return sinks[sinks.length - 1];
}, isolationName);
}
// Example usage
const LabelledSlider = makeComponent(
// Intentions
({ DOM, props$ }) => {
const initialValue$ = props$
.map(props => props.initial)
.first();
const changeValue$ = DOM
.select('.slider')
.events('input')
.map(ev => ev.target.value);
return {
initialValue$,
changeValue$,
};
},
// Model
({ initialValue$, changeValue$ }, { props$ }) => ({
value$: initialValue$.concat(changeValue$),
}),
// View
({ value$ }, { props$ }) => ({
value$,
DOM: Observable
.combineLatest(value$, props$)
.map(([ value, props ]) => (
<div className="labeled-slider">
<span className="label">{`${props.label} ${value}${props.unit}`}</span>
<input className="slider" type="range" min={props.min} max={props.max} value={value}/>
</div>
)),
})
);
const app = makeComponent(
// Intent
() => ({
weightProps$: Observable.of({
label: 'Weight', unit: 'kg', min: 40, initial: 70, max: 150
}),
heightProps$: Observable.of({
label: 'Height', unit: 'cm', min: 140, initial: 170, max: 210
}),
}),
// Model
({ weightProps$, heightProps$ }, { DOM }) => {
const weightSlider = LabelledSlider()({ DOM, props$: weightProps$ });
const heightSlider = LabelledSlider()({ DOM, props$: heightProps$ });
return {
weightSlider,
heightSlider,
bmi$: Observable.combineLatest(
weightSlider.value$, heightSlider.value$,
(weight, height) => {
const heightMeters = height * 0.01;
return Math.round(weight / (heightMeters * heightMeters));
}
),
};
},
// View
({ bmi$, weightSlider, heightSlider }) => ({
DOM: Observable.combineLatest(
bmi$,
weightSlider.DOM,
heightSlider.DOM,
(bmi, weightVTree, heightVTree) =>
<div>
{weightVTree}
{heightVTree}
<h2>{`BMI is ${bmi}`}</h2>
</div>
)
})
);
Cycle.run(app('myApp'), {
DOM: makeDOMDriver('#app')
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment