Skip to content

Commit b7f3d79

Browse files
Merge pull request #11 from efflore/feature/v0.7.3
Feature/v0.7.3
2 parents 0c8aaca + a5c62fc commit b7f3d79

29 files changed

+894
-1018
lines changed

README.md

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
UIElement - the "look ma, no JS framework!" library bringing signals-based reactivity to vanilla Web Components
44

5-
Version 0.7.2
5+
Version 0.7.3
66

77
## What is UIElement?
88

@@ -16,7 +16,7 @@ In the `connectedCallback()` you setup references to inner elements, add event l
1616

1717
`UIElement` is fast. In fact, faster than any JavaScript framework. Only direct fine-grained DOM updates in vanilla JavaScript can beat its performance. But then, you have no loose coupling of components and need to parse attributes and track changes yourself. This tends to get tedious and messy rather quickly. `UIElement` provides a structured way to keep your components simple, consistent and self-contained.
1818

19-
`UIElement` is tiny. 1 kB gzipped over the wire. And it has zero dependiences. If you want to understand how it works, you have to study the source code of [one single file](./index.js).
19+
`UIElement` is tiny. 889 bytes gzipped over the wire. And it has zero dependiences. If you want to understand how it works, you have to study the source code of [one single file](./index.js).
2020

2121
That's all.
2222

@@ -174,10 +174,10 @@ class MotionContext extends UIElement {
174174
connectedCallback() {
175175
const mql = matchMedia('(prefers-reduced-motion)');
176176
this.set('reduced-motion', mql.matches);
177+
super.connectedCallback();
177178
mql.onchange = e => this.set('reduced-motion', e.matches);
178179
}
179180
}
180-
181181
MotionContext.define('motion-context');
182182
```
183183

@@ -190,16 +190,16 @@ class MyAnimation extends UIElement {
190190
static consumedContexts = ['reduced-motion'];
191191

192192
connectedCallback() {
193+
super.connectedCallback();
193194
effect(() => this.get('reduced-motion') ? subtleFadeIn() : pulsateAndMoveAround());
194195
}
195196
}
196-
197197
MyAnimation.define('my-animation');
198198
```
199199

200200
### Scheduling and Cleanup
201201

202-
If you use `effect()` with a callback function like `() => doMyEffectWork()`, it will do all work synchronously in the tracking phase of the effect. That might not be ideal for several reasons:
202+
If you use `effect()` with a callback function like `() => doMyEffectWork()`, it will do all work synchronously. That might not be ideal for several reasons:
203203

204204
- If effect work is expensive and takes more time than a single tick, not all DOM updates of the effect might happen concurrently.
205205
- If you `set()` a signal which is used in the effect, you risk producing an infite loop.
@@ -246,7 +246,7 @@ It consists of three functions:
246246
- `derive()` returns a getter function for the current value of the derived computation
247247
- `effect()` accepts a callback function to be exectuted when used signals change
248248

249-
Cause & Effect is possibly the simplest way to turn JavaScript into a reactive language – with just 385 bytes gezipped code. By default, Cause & Effect doesn't do any memoization for derived signals but recalculates their current value each time. Contrary to general expectations, this seems to be faster in most cases. If you however are performing expensive work in computed signals or rely on count of the execution times, you should turn memoization on, by setting the second parameter of `derive()` to `true`.
249+
Cause & Effect is possibly the simplest way to turn JavaScript into a reactive language – with just 386 bytes gezipped code. By default, Cause & Effect doesn't do any memoization for derived signals but recalculates their current values each time. Contrary to general expectations, this seems to be faster in most cases. If you however are performing expensive work in computed signals or rely on the count of execution times, you should turn memoization on, by setting the second parameter of `derive()` to `true`.
250250

251251
#### Usage Example
252252

@@ -292,7 +292,16 @@ The last option comes with a bit more library code (around 1.6 kB gzipped), but
292292
<script type="module">
293293
import { component, asInteger } from './component';
294294
295-
component('my-counter', { value: v => asInteger }, (el, my) => {
295+
component('my-counter', {
296+
attributeMap: { // static observedAttributes is derived from the keys of optional `attributeMap` object
297+
value: v => asInteger
298+
},
299+
consumedContexts: [], // for static consumedContexts an array of keys can be provided optionally
300+
providedContexts: [], // for static providedContexts an array of keys can be provided optionally
301+
},
302+
(el, my) => {
303+
// `el` is `this` of your custom element
304+
// `my` is a UI reference of your custom element
296305
my.first('.decrement').on('click', () => el.set('value', v => --v));
297306
my.first('.increment').on('click', () => el.set('value', v => ++v));
298307
my.first('span').text('value');
@@ -320,7 +329,7 @@ import { component, ui } from './component';
320329
component('my-counter', {}, (el, my) => {
321330
// third parameter of component is called in connectedCallback()
322331
// `el` is `this` of your custom element
323-
// `my` is a UI reference function to the element with convenience methods to query sub-elements, bind events, and auto-create effects
332+
// `my` is a UI reference of your custom the element with convenience methods to query sub-elements, bind events, and auto-create effects
324333

325334
// console.log(my()); // calling the UI reference returns the native DOM element
326335
const count = my.first('.count'); // first does querySelector and returns a UI reference
@@ -348,12 +357,14 @@ component('my-counter', {}, (el, my) => {
348357

349358
On `UI` reference objects you can call these utility methods:
350359

351-
- `ref.text()` preserves comment nodes in contrast to `element.textContent` assignments
352-
- `ref.prop()` sets or deletes a property on an element
353-
- `ref.attr()` sets or removes an attribute on an element
354-
- `ref.bool()` toggles a boolean attribute on an element
355-
- `ref.class()` adds or removes a class on an element
356-
- `ref.style()` sets or removes a style property on an element
360+
- `ref()` returns the native element
361+
- `ref.setText()` replaces text content of element while preserving comment nodes; to be used in effects
362+
- `ref.text()` auto-effect to replace text content while preserving comment nodes in contrast to `element.textContent` assignments
363+
- `ref.prop()` auto-effect to set or delete a property on an element
364+
- `ref.attr()` auto-effect to set or remove an attribute on an element
365+
- `ref.bool()` auto-effect to toggle a boolean attribute on an element
366+
- `ref.class()` auto-effect to add or remove a class on an element
367+
- `ref.style()` auto-effect to set or remove a style property on an element
357368

358369
[Source](./component.js)
359370

@@ -370,6 +381,7 @@ class MyElement extends DebugElement {
370381
connectedCallback() {
371382
this.set('debug', true); // will enable debugging for all instances
372383
// use the 'debug' boolean attribute to enable debugging just for that specific instance
384+
super.connectedCallback(); // for context provider / consumer + debug logging
373385
/* ... */
374386
}
375387
}
@@ -399,6 +411,7 @@ import VisibilityObserver from './src/lib/visibility-observer';
399411
class MyAnimation extends UIElement {
400412

401413
connectedCallback() {
414+
super.connectedCallback(); // needed if you use context provider or consumer
402415
this.visibilityObserver = new VisibilityObserver(this); // sets and updates 'visible' state on `this`
403416
effect(() => this.get('visible') ? startAnimation() : stopAnimation());
404417
}

cause-effect.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const isState = (value) => isFunction(value) && isFunction(value.set);
3131
*
3232
* @since 0.1.0
3333
* @param {any} value - initial value of the state; may be a function for derived state
34-
* @returns {UIState} getter function for the current value with a `set` method to update the value
34+
* @returns {UIState<T>} getter function for the current value with a `set` method to update the value
3535
*/
3636
const cause = (value) => {
3737
const state = () => {
@@ -52,9 +52,9 @@ const cause = (value) => {
5252
* Create a derived state from an existing state
5353
*
5454
* @since 0.1.0
55-
* @param {() => any} fn - existing state to derive from
55+
* @param {() => T} fn - existing state to derive from
5656
* @param {boolean} [memo=false] - whether to use memoization
57-
* @returns {UIComputed<any>} derived state
57+
* @returns {UIComputed<T>} derived state
5858
*/
5959
const derive = (fn, memo = false) => {
6060
let value;
@@ -90,7 +90,7 @@ const effect = (fn) => {
9090
active = next;
9191
const cleanup = fn((element, domFn) => {
9292
!targets.has(element) && targets.set(element, new Set());
93-
targets.get(element).add(domFn);
93+
targets.get(element)?.add(domFn);
9494
});
9595
for (const domFns of targets.values()) {
9696
for (const domFn of domFns)

cause-effect.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)