Releases: cyclejs/dom
Small breaking change related to events()
This version fixes event bubbling for the new event delegation-based system. Highlights are:
events(eventType, options)
, whereoptions
is usually{useCapture: true}
- Use
event.ownerTarget
instead ofevent.currentTarget
. The latter is semantically the same ascurrentTarget
but works in all browsers and was necessary becausecurrentTarget
is read-only according to W3 specs and we could not override it in our custom event delegation system.
Migration guide:
-DOMSource.select('.foo').events('blur', true)
+DOMSource.select('.foo').events('blur', {useCapture: true})
DOMSource.select('.foo').events('click').map(ev =>
- ev.currentTarget.textContent
+ ev.ownerTarget.textContent
)
For a detailed discussion on these issues and more information, read issue 87.
New feature: DOMSource.events()
events()
can now be called on the top-level sources.DOM
without requiring a prior select()
:
function app(sources) {
// Event stream of every click happening in the mounted Cycle.js app
const click$ = sources.DOM.events('click');
// ...
}
This closes issue #84.
The source code was refactored to use event delegation for every events()
call. #85 This might or might not break functionality of your application. Migrate carefully. On the other hand, performance might improve significantly.
v8.0.0 - Cycle Nested
Cycle Nested
Cycle Nested is a new version of Cycle.js with a focus on hard-core reusability: any Cycle.js app can be easily reused in a larger Cycle.js app.
New documentation site at cycle.js.org.
Cycle Nested consists of:
- Cycle Core v6.0.0 or higher
- Cycle HTTP Driver v7.0.0 or higher
- Cycle DOM v8.0.0 or higher
isolate()
, a helper library for components- Other compatible drivers
NEW FEATURES in Cycle Nested:
- Components are simply Cycle.js apps (
main()
renamed to e.g.Button()
) that can be reused in larger apps. - Cycle DOM introduces hyperscript helpers, so you can create virtual DOM with functions like
div()
,h1()
,ul()
,li()
,span()
, etc.
Migration guide
Names changed.
Before | After |
---|---|
DOM.get() |
DOM.select().events() |
Response (naming convention) | Source (naming convention) |
Request (naming convention) | Sink (naming convention) |
Cycle DOM mockDOMResponse() |
Cycle DOM mockDOMSource() |
labeledSlider (custom element) |
LabeledSlider (dataflow component) |
let [sinks, sources] = Cycle.run(m, d) |
let {sinks, sources} = Cycle.run(m, d) |
Custom Elements removed. Dataflow components replace them.
-function labeledSlider(sources) {
+function LabeledSlider(sources) {
- const initialValue$ = sources.props.get('initial')
+ const initialValue$ = sources.props$
+ .map(props => props.initial)
.first();
const newValue$ = sources.DOM
.select('.slider')
.events('input')
.map(ev => ev.target.value);
const value$ = initialValue$.concat(newValue$);
- const props$ = sources.props.getAll();
const vtree$ = Rx.Observable
- .combineLatest(props$, value$, (props, value) =>
+ .combineLatest(sources.props$, value$, (props, value) =>
h('div.labeled-slider', [
h('span.label', [
props.label + ' ' + value + props.unit
]),
h('input.slider', {
type: 'range',
min: props.min,
max: props.max,
value: value
})
])
);
return {
DOM: vtree$,
- events: {
- newValue: newValue$
- }
+ value$: value$
};
}
The function above, LabeledSlider()
follows the same techniques we use to build any main()
function in Cycle.js. There is no magic and no tricks, it is simply a function that does what it says it does. The usage of components is very different to the usage of custom elements.
Not necessary to register the component anymore:
-const domDriver = CycleDOM.makeDOMDriver('#app', {
- 'labeled-slider': labeledSlider // our function
-});
+const domDriver = CycleDOM.makeDOMDriver('#app');
Using a component in a parent view has changed:
function main(sources) {
// ...
- const childValue$ = state(intent(sources.DOM));
+ const props$ = Observable.of({
+ label: 'Radius', unit: '', min: 10, initial: 30, max: 100
+ });
+ const childSources = {DOM: sources.DOM, props$};
+ const labeledSlider = LabeledSlider(childSources);
+ const childVTree$ = labeledSlider.DOM;
+ const childValue$ = labeledSlider.value$;
- const vtree$ = childValue$.map(
- value =>
+ const vtree$ = childVTree$.withLatestFrom(childValue$,
+ (childVTree, value) =>
h('div', [
- h('labeled-slider#weight', {
- key: 1, label: 'Weight', unit: 'kg',
- min: 40, initial: weight, max: 140
- }),
+ childVTree,
div({style: {
backgroundColor: '#58D3D8',
width: String(value) + 'px',
height: String(value) + 'px',
borderRadius: String(value * 0.5) + 'px'
}})
])
);
return {
DOM: vtree$
};
}
To get events from a component, we don't use anymore DOM.select('labeled-slider').events('myCustomEvent')
. Instead, we just get the Observable returned from the LabeledSlider
component function.
Read more instructions about Dataflow components.
Using hyperscript helpers:
+import { div, span, input } from '@cycle/dom';
-h('div.labeled-slider', [
+div('.labeled-slider', [
- h('span.label', [
+ span('.label', [
props.label + ' ' + value + props.unit
]),
- h('input.slider', {
+ input('.slider', {
type: 'range',
min: props.min,
max: props.max,
value: value
})
])
For more help, read the new documentation site Cycle.js.org or ask for help in the Gitter chat room.
Add support for virtual-dom Thunks
Update peer dependency Cycle Core to v5.0
RxJS updated to v4.0, removed DOM.get()
https://github.com/cyclejs/cycle-core/releases/tag/v4.0.0
Removed DOM.get(selector, eventType)
. It was deprecated. Use DOM.select(selector).events(eventType)
instead.
Fix a minor issue with nested svg custom elements
New testing helper: mockDOMResponse()
Added mockDOMResponse(mockedSelectors)
.
A testing utility which aids in creating a queryable collection of Observables. Call mockDOMResponse giving it an object specifying selectors, eventTypes and their Observabls, and get as output an object following the
same format as the DOM Driver's response.
Example:
// Create the mocked user events
const userEvents = mockDOMResponse({
'.foo': {
'click': Rx.Observable.just({target: {}}),
'mouseover': Rx.Observable.just({target: {}})
},
'.bar': {
'scroll': Rx.Observable.just({target: {}})
}
});
// Use them in your tests against `main`
const requests = main({DOM: userEvents});
requests.DOM.subscribe(function (vtree) {
// make assertions on vtree
});
New minor feature: specify useCapture in select().events()
Thanks to @Frikki for #43, you can now specify the useCapture
parameter of event listeners when defining events:
events(eventType, useCapture)
where useCapture
is optional and by default false
.
Example:
let click$ = DOM.select('.button').events('click', true);
DOM.select().events() to replace DOM.get()
DOM.get(selector, eventType)
is deprecated. Use DOM.select(selector).events(eventType)
instead.
select(selector).events(eventType)
is a more descriptive API than get()
is. It also allows you to get an Observable of DOM elements matching the selector († below).
Before | After |
---|---|
DOM.get('.like', 'click') |
DOM.select('.like').events('click') |
DOM.get(':root') |
DOM.select(':root').observable |
-- | DOM.select('.like').observable † |
Both get()
and select().events()
still work as of v5.1, but using get()
will trigger a warning in the console. In an upcoming major version, get()
will be removed and only select().events()
will work.