@thi.ng/rdom 0.13.2 → 0.13.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/promise.js CHANGED
@@ -1,43 +1,27 @@
1
1
  import { Component } from "./component.js";
2
- /**
3
- * Simple component wrapper for {@link ComponentLike} promises. When this
4
- * component mounts it will `await` the given promise or if it fails, compile
5
- * the result of the given (optional) `error` handler as component body.
6
- *
7
- * @example
8
- * ```ts
9
- * const prom = Promise.resolve<ComponentLike>(
10
- * ["div", {}, "Resolved!"]
11
- * );
12
- *
13
- * $promise(prom).mount(document.body);
14
- * ```
15
- *
16
- * @param prom -
17
- * @param error -
18
- */
19
- export const $promise = (prom, error) => new $Promise(prom, error);
20
- export class $Promise extends Component {
21
- promise;
22
- error;
23
- inner;
24
- constructor(promise, error = (e) => e) {
25
- super();
26
- this.promise = promise;
27
- this.error = error;
28
- }
29
- async mount(parent, index) {
30
- try {
31
- this.inner = this.$compile(await this.promise);
32
- }
33
- catch (e) {
34
- this.inner = this.$compile(this.error(e));
35
- }
36
- return (this.el = await this.inner.mount(parent, index));
37
- }
38
- async unmount() {
39
- await this.inner.unmount();
40
- this.inner = undefined;
41
- this.el = undefined;
2
+ const $promise = (prom, error) => new $Promise(prom, error);
3
+ class $Promise extends Component {
4
+ constructor(promise, error = (e) => e) {
5
+ super();
6
+ this.promise = promise;
7
+ this.error = error;
8
+ }
9
+ inner;
10
+ async mount(parent, index) {
11
+ try {
12
+ this.inner = this.$compile(await this.promise);
13
+ } catch (e) {
14
+ this.inner = this.$compile(this.error(e));
42
15
  }
16
+ return this.el = await this.inner.mount(parent, index);
17
+ }
18
+ async unmount() {
19
+ await this.inner.unmount();
20
+ this.inner = void 0;
21
+ this.el = void 0;
22
+ }
43
23
  }
24
+ export {
25
+ $Promise,
26
+ $promise
27
+ };
package/replace.js CHANGED
@@ -3,64 +3,36 @@ import { Component } from "./component.js";
3
3
  import { __nextID } from "./idgen.js";
4
4
  import { $subWithID } from "./sub.js";
5
5
  import { $wrapText } from "./wrap.js";
6
- /**
7
- * Similar to {@link $refresh}, but more basic/simple. Takes a reactive value
8
- * `src` and wraps it in a {@link $sub} component using an inner
9
- * {@link Replace}, which then passes the value to {@link $compile} for each
10
- * change and replaces the result in the target DOM. If the value evaluates to
11
- * `null`ish, the previously mounted component will be unmounted and stays so
12
- * until the value becomes non-null again.
13
- *
14
- * @remarks
15
- * If the reactive value is null-ish when the wrapper component is first
16
- * mounted, a hidden dummy `<span>` element will be created instead. This is to
17
- * ensure the general {@link IComponent.mount} contract will not be broken. The
18
- * dummy element will later be removed/replaced as soon as the reactive value
19
- * becomes non-null.
20
- *
21
- * @example
22
- * ```ts
23
- * import { $compile, $replace } from "@thi.ng/rdom";
24
- * import { fromInterval } from "@thi.ng/rstream";
25
- *
26
- * // reactive counter component
27
- * const counter = fromInterval(16).map((x) => [
28
- * "div",
29
- * { style: { "font-size": `${(x % 100) + 10}px` } },
30
- * x,
31
- * ]);
32
- *
33
- * $compile($replace(counter)).mount(document.body);
34
- * ```
35
- *
36
- * @param src -
37
- */
38
- export const $replace = (src) => $subWithID(src, new Replace(), __nextID("replace", src));
39
- export class Replace extends Component {
40
- parent;
41
- inner;
42
- index;
43
- async mount(parent, index, val) {
44
- this.parent = parent;
45
- this.index = index;
46
- await this.update(val);
47
- if (!this.inner) {
48
- this.inner = $wrapText("span", { hidden: true });
49
- await this.inner.mount(parent, index);
50
- }
51
- return this.inner.el;
6
+ const $replace = (src) => $subWithID(src, new Replace(), __nextID("replace", src));
7
+ class Replace extends Component {
8
+ parent;
9
+ inner;
10
+ index;
11
+ async mount(parent, index, val) {
12
+ this.parent = parent;
13
+ this.index = index;
14
+ await this.update(val);
15
+ if (!this.inner) {
16
+ this.inner = $wrapText("span", { hidden: true });
17
+ await this.inner.mount(parent, index);
52
18
  }
53
- async unmount() {
54
- this.inner && (await this.inner.unmount());
55
- this.parent = undefined;
56
- this.inner = undefined;
57
- }
58
- async update(val) {
59
- this.inner && (await this.inner.unmount());
60
- this.inner = undefined;
61
- if (val != null) {
62
- this.inner = $compile(val);
63
- this.inner && (await this.inner.mount(this.parent, this.index));
64
- }
19
+ return this.inner.el;
20
+ }
21
+ async unmount() {
22
+ this.inner && await this.inner.unmount();
23
+ this.parent = void 0;
24
+ this.inner = void 0;
25
+ }
26
+ async update(val) {
27
+ this.inner && await this.inner.unmount();
28
+ this.inner = void 0;
29
+ if (val != null) {
30
+ this.inner = $compile(val);
31
+ this.inner && await this.inner.mount(this.parent, this.index);
65
32
  }
33
+ }
66
34
  }
35
+ export {
36
+ $replace,
37
+ Replace
38
+ };
package/scheduler.js CHANGED
@@ -1,54 +1,39 @@
1
- /**
2
- * {@link IScheduler} implementation which queues component updates (or other
3
- * tasks) and then only processes them during next RAF cycle. Supports task
4
- * cancellation.
5
- *
6
- * @remarks
7
- * See {@link setScheduler} and {@link NullScheduler}.
8
- */
9
- export class RAFScheduler {
10
- tasks;
11
- raf;
12
- constructor() {
13
- this.tasks = new Map();
14
- this.raf = -1;
15
- }
16
- add(scope, fn) {
17
- const tasks = this.tasks.get(scope);
18
- tasks ? tasks.push(fn) : this.tasks.set(scope, [fn]);
19
- this.raf < 0 &&
20
- (this.raf = requestAnimationFrame(this.update.bind(this)));
21
- }
22
- cancel(scope) {
23
- this.tasks.delete(scope);
24
- }
25
- update() {
26
- for (let tasks of this.tasks.values()) {
27
- for (let i = tasks.length; i-- > 0; tasks[i]())
28
- ;
29
- }
30
- this.tasks.clear();
31
- this.raf = -1;
1
+ class RAFScheduler {
2
+ tasks;
3
+ raf;
4
+ constructor() {
5
+ this.tasks = /* @__PURE__ */ new Map();
6
+ this.raf = -1;
7
+ }
8
+ add(scope, fn) {
9
+ const tasks = this.tasks.get(scope);
10
+ tasks ? tasks.push(fn) : this.tasks.set(scope, [fn]);
11
+ this.raf < 0 && (this.raf = requestAnimationFrame(this.update.bind(this)));
12
+ }
13
+ cancel(scope) {
14
+ this.tasks.delete(scope);
15
+ }
16
+ update() {
17
+ for (let tasks of this.tasks.values()) {
18
+ for (let i = tasks.length; i-- > 0; tasks[i]())
19
+ ;
32
20
  }
21
+ this.tasks.clear();
22
+ this.raf = -1;
23
+ }
33
24
  }
34
- /**
35
- * Dummy (and default) {@link IScheduler} implementation which immediately
36
- * processes component updates.
37
- *
38
- * @remarks
39
- * See {@link setScheduler} and {@link RAFScheduler}.
40
- */
41
- export class NullScheduler {
42
- add(_, fn) {
43
- fn();
44
- }
45
- cancel() { }
25
+ class NullScheduler {
26
+ add(_, fn) {
27
+ fn();
28
+ }
29
+ cancel() {
30
+ }
46
31
  }
47
- // export let SCHEDULER: IScheduler = new RAFScheduler();
48
- export let SCHEDULER = new NullScheduler();
49
- /**
50
- * Sets rdom-global scheduler for component updates (and other tasks).
51
- *
52
- * @param s -
53
- */
54
- export const setScheduler = (s) => (SCHEDULER = s);
32
+ let SCHEDULER = new NullScheduler();
33
+ const setScheduler = (s) => SCHEDULER = s;
34
+ export {
35
+ NullScheduler,
36
+ RAFScheduler,
37
+ SCHEDULER,
38
+ setScheduler
39
+ };
package/sub.js CHANGED
@@ -5,53 +5,55 @@ import { Subscription } from "@thi.ng/rstream/subscription";
5
5
  import { $attribs } from "./dom.js";
6
6
  import { SCHEDULER } from "./scheduler.js";
7
7
  import { $wrapText } from "./wrap.js";
8
- export function $sub(src, tag, attribs) {
9
- return (src.subscribe(new $Sub(isString(tag) ? $wrapText(tag, attribs) : tag)));
8
+ function $sub(src, tag, attribs) {
9
+ return src.subscribe(new $Sub(isString(tag) ? $wrapText(tag, attribs) : tag));
10
10
  }
11
- /**
12
- * Version of {@link $sub} which supports specifying an rstream stream ID for
13
- * the resulting subscription (useful for debugging/visualizing the reactive
14
- * graph topology).
15
- *
16
- * @param src
17
- * @param inner
18
- * @param id
19
- */
20
- export const $subWithID = (src, inner, id) => src.subscribe(new $Sub(inner, id));
21
- export class $Sub extends Subscription {
22
- inner;
23
- el;
24
- constructor(inner, id) {
25
- super(undefined, { id: id || `rdom$sub-${__nextID()}` });
26
- this.inner = inner;
27
- }
28
- async mount(parent, index = -1) {
29
- return (this.el = await this.inner.mount(parent, index, this.parent.deref()));
30
- }
31
- async unmount() {
32
- this.unsubscribe();
33
- SCHEDULER.cancel(this);
34
- this.el = undefined;
35
- await this.inner.unmount();
36
- }
37
- update(x) {
38
- this.next(x);
39
- }
40
- next(x) {
41
- SCHEDULER.add(this, () => this.el && this.inner.update(x));
42
- }
11
+ const $subWithID = (src, inner, id) => src.subscribe(new $Sub(inner, id));
12
+ class $Sub extends Subscription {
13
+ constructor(inner, id) {
14
+ super(void 0, { id: id || `rdom$sub-${__nextID()}` });
15
+ this.inner = inner;
16
+ }
17
+ el;
18
+ async mount(parent, index = -1) {
19
+ return this.el = await this.inner.mount(
20
+ parent,
21
+ index,
22
+ this.parent.deref()
23
+ );
24
+ }
25
+ async unmount() {
26
+ this.unsubscribe();
27
+ SCHEDULER.cancel(this);
28
+ this.el = void 0;
29
+ await this.inner.unmount();
30
+ }
31
+ update(x) {
32
+ this.next(x);
33
+ }
34
+ next(x) {
35
+ SCHEDULER.add(this, () => this.el && this.inner.update(x));
36
+ }
43
37
  }
44
- export class $SubA extends Subscription {
45
- comp;
46
- setter;
47
- attr = {};
48
- constructor(comp, path, id) {
49
- super(undefined, { id: id || `rdom$attr-${__nextID()}` });
50
- this.comp = comp;
51
- this.setter = defSetterUnsafe(path);
52
- }
53
- next(a) {
54
- const $ = this.comp;
55
- SCHEDULER.add($, () => $.el && $attribs($.el, this.setter(this.attr, a)));
56
- }
38
+ class $SubA extends Subscription {
39
+ constructor(comp, path, id) {
40
+ super(void 0, { id: id || `rdom$attr-${__nextID()}` });
41
+ this.comp = comp;
42
+ this.setter = defSetterUnsafe(path);
43
+ }
44
+ setter;
45
+ attr = {};
46
+ next(a) {
47
+ const $ = this.comp;
48
+ SCHEDULER.add(
49
+ $,
50
+ () => $.el && $attribs($.el, this.setter(this.attr, a))
51
+ );
52
+ }
57
53
  }
54
+ export {
55
+ $Sub,
56
+ $SubA,
57
+ $sub,
58
+ $subWithID
59
+ };
package/switch.js CHANGED
@@ -4,141 +4,72 @@ import { Component } from "./component.js";
4
4
  import { __nextID } from "./idgen.js";
5
5
  import { $subWithID } from "./sub.js";
6
6
  import { $wrapText } from "./wrap.js";
7
- /**
8
- * Reactive component wrapper to dynamically switch/replace itself with one of
9
- * the given components depending on subscribed value.
10
- *
11
- * @remarks
12
- * Subscribes to `src`, then calls `keyFn` for each received value and uses
13
- * result to call one of the given `ctors` async component factories. The value
14
- * returned from the chosen factory will be passsed to {@link $compile} and then
15
- * mounted in place of this `$switch` wrapper. If an uncaught error occurs in
16
- * the selected component factory, the `error` component factory will be used
17
- * instead (which is expected to succeed).
18
- *
19
- * When a new value is received from `src`, the currently active inner component
20
- * (if any) will always be fist `unmount`ed and if the optional `loader` is
21
- * given, it will be temporarily mounted whilst the actual `ctor` component
22
- * factory executes. This is can be used to show a pre-loaders.
23
- *
24
- * **IMPORTANT:** When a `null` or `undefined` value is received from `src`, the
25
- * currently active inner component will be unmounted and the optional loader
26
- * shown. However, no other inner component constructor will be called in this
27
- * case (until the next valid/non-null value is received).
28
- *
29
- * All component factories are async functions to facilitate dynamic `import()`
30
- * / code splitting and other async initializations (WASM etc.)
31
- *
32
- * @example
33
- * ```ts
34
- * $switch(
35
- * fromInterval(1000),
36
- * (x) => x % 3,
37
- * {
38
- * 0: async (x) => {
39
- * await delayed(null, 500); // fake preload
40
- * return ["div.green", {}, x];
41
- * },
42
- * 1: async (x) => ["div.yellow", {}, x]
43
- * },
44
- * async (err) => ["div.bg-red", {}, err],
45
- * async () => ["div", {}, "Loading..."]
46
- * ).mount(document.body)
47
- * ```
48
- *
49
- * This example uses a 1Hz counter to delegate to 3 different possible
50
- * components, however only two are defined, causing the last option to
51
- * throw an error and so trigger/use the error component.
52
- *
53
- * @param src -
54
- * @param keyFn -
55
- * @param ctors -
56
- * @param error -
57
- * @param loader -
58
- */
59
- export const $switch = (src, keyFn, ctors, error, loader) => $subWithID(src, new Switch(keyFn, ctors, error, loader), __nextID("switch", src));
60
- /**
61
- * Syntax sugar for {@link $switch} for cases when there's only a single
62
- * component which should transition through its entire lifecycle for
63
- * each reactive value change.
64
- *
65
- * @remarks
66
- * In other words, for each new value received from `src`. The wrapped
67
- * component will first be unmounted, an optional pre-`loader` shown
68
- * whilst the async `ctor` component factory executes, its result
69
- * `$compile`d and then getting re-mounted. See {@link $switch} for
70
- * further details.
71
- *
72
- * Also see {@link $replace}.
73
- *
74
- * @example
75
- * ```ts
76
- * $refresh(fromInterval(1000), async (x) => ["div", {}, x])
77
- * ```
78
- *
79
- * @param src -
80
- * @param ctor -
81
- * @param error -
82
- * @param loader -
83
- */
84
- export const $refresh = (src, ctor, error, loader) => $subWithID(src, new Switch(() => 0, { 0: ctor }, error, loader), __nextID("refresh", src));
85
- export class Switch extends Component {
86
- keyFn;
87
- ctors;
88
- error;
89
- loader;
90
- val;
91
- parent;
92
- inner;
93
- index;
94
- constructor(keyFn, ctors, error = async (e) => $wrapText("span", {}, e), loader = async () => $wrapText("span", {
95
- hidden: true,
96
- })) {
97
- super();
98
- this.keyFn = keyFn;
99
- this.ctors = ctors;
100
- this.error = error;
101
- this.loader = loader;
102
- }
103
- async mount(parent, index, val) {
104
- this.parent = parent;
105
- this.index = index;
106
- await this.update(val);
107
- return this.inner.el;
108
- }
109
- async unmount() {
110
- this.inner && (await this.inner.unmount());
111
- this.val = undefined;
112
- this.parent = undefined;
113
- this.inner = undefined;
114
- }
115
- async update(val) {
116
- this.inner && (await this.inner.unmount());
117
- this.inner = undefined;
118
- if (val != null) {
119
- this.val = val;
120
- let loader;
121
- if (this.loader) {
122
- loader = $compile(await this.loader(val));
123
- await loader.mount(this.parent, this.index);
124
- }
125
- try {
126
- const key = this.keyFn(val);
127
- const next = this.ctors[key];
128
- assert(!!next, `missing component for key: ${key}`);
129
- this.inner = $compile(await next(val));
130
- loader && (await loader.unmount());
131
- }
132
- catch (e) {
133
- if (this.error) {
134
- this.inner = $compile(await this.error(e));
135
- loader && (await loader.unmount());
136
- }
137
- }
138
- }
139
- else {
140
- this.loader && (this.inner = $compile(await this.loader(val)));
7
+ const $switch = (src, keyFn, ctors, error, loader) => $subWithID(
8
+ src,
9
+ new Switch(keyFn, ctors, error, loader),
10
+ __nextID("switch", src)
11
+ );
12
+ const $refresh = (src, ctor, error, loader) => $subWithID(
13
+ src,
14
+ new Switch(() => 0, { 0: ctor }, error, loader),
15
+ __nextID("refresh", src)
16
+ );
17
+ class Switch extends Component {
18
+ constructor(keyFn, ctors, error = async (e) => $wrapText("span", {}, e), loader = async () => $wrapText("span", {
19
+ hidden: true
20
+ })) {
21
+ super();
22
+ this.keyFn = keyFn;
23
+ this.ctors = ctors;
24
+ this.error = error;
25
+ this.loader = loader;
26
+ }
27
+ val;
28
+ parent;
29
+ inner;
30
+ index;
31
+ async mount(parent, index, val) {
32
+ this.parent = parent;
33
+ this.index = index;
34
+ await this.update(val);
35
+ return this.inner.el;
36
+ }
37
+ async unmount() {
38
+ this.inner && await this.inner.unmount();
39
+ this.val = void 0;
40
+ this.parent = void 0;
41
+ this.inner = void 0;
42
+ }
43
+ async update(val) {
44
+ this.inner && await this.inner.unmount();
45
+ this.inner = void 0;
46
+ if (val != null) {
47
+ this.val = val;
48
+ let loader;
49
+ if (this.loader) {
50
+ loader = $compile(await this.loader(val));
51
+ await loader.mount(this.parent, this.index);
52
+ }
53
+ try {
54
+ const key = this.keyFn(val);
55
+ const next = this.ctors[key];
56
+ assert(!!next, `missing component for key: ${key}`);
57
+ this.inner = $compile(await next(val));
58
+ loader && await loader.unmount();
59
+ } catch (e) {
60
+ if (this.error) {
61
+ this.inner = $compile(await this.error(e));
62
+ loader && await loader.unmount();
141
63
  }
142
- this.inner && (await this.inner.mount(this.parent, this.index));
64
+ }
65
+ } else {
66
+ this.loader && (this.inner = $compile(await this.loader(val)));
143
67
  }
68
+ this.inner && await this.inner.mount(this.parent, this.index);
69
+ }
144
70
  }
71
+ export {
72
+ $refresh,
73
+ $switch,
74
+ Switch
75
+ };
package/wrap.js CHANGED
@@ -1,52 +1,36 @@
1
1
  import { $addChild, $el, $html, $remove, $text } from "./dom.js";
2
2
  import { SCHEDULER } from "./scheduler.js";
3
3
  const wrapper = (update) => (tag, attribs, body) => ({
4
- el: undefined,
5
- async mount(parent, index, state) {
6
- this.el = $el(tag, attribs, null, parent, index);
7
- update(this.el, state != null ? state : body);
8
- return this.el;
9
- },
10
- async unmount() {
11
- $remove(this.el);
12
- this.el = undefined;
13
- },
14
- update(body) {
15
- SCHEDULER.add(this, () => this.el && update(this.el, body));
16
- },
4
+ el: void 0,
5
+ async mount(parent, index, state) {
6
+ this.el = $el(tag, attribs, null, parent, index);
7
+ update(this.el, state != null ? state : body);
8
+ return this.el;
9
+ },
10
+ async unmount() {
11
+ $remove(this.el);
12
+ this.el = void 0;
13
+ },
14
+ update(body2) {
15
+ SCHEDULER.add(this, () => this.el && update(this.el, body2));
16
+ }
17
17
  });
18
- /**
19
- * Returns a component wrapper for a single DOM element whose TEXT body can be
20
- * later updated/replaced via `.update()`, similarly to setting `.innerText`.
21
- *
22
- * @param tag - element name
23
- * @param attribs - element attribs
24
- * @param body - optional initial body
25
- */
26
- export const $wrapText = wrapper($text);
27
- /**
28
- * Returns a component wrapper for a single DOM element whose HTML body can be
29
- * later updated/replaced via `.update()`, similarly to setting `.innerHTML`.
30
- *
31
- * @param tag - element name
32
- * @param attribs - element attribs
33
- * @param body - optional initial body
34
- */
35
- export const $wrapHtml = wrapper($html);
36
- /**
37
- * {@link IComponent} wrapper for an existing DOM element. When mounted, the
38
- * given element will be (re)attached to the parent node provided at that time.
39
- *
40
- * @param el
41
- */
42
- export const $wrapEl = (el) => ({
43
- async mount(parent, idx) {
44
- $addChild(parent, el, idx);
45
- return (this.el = el);
46
- },
47
- async unmount() {
48
- $remove(this.el);
49
- this.el = undefined;
50
- },
51
- update() { },
18
+ const $wrapText = wrapper($text);
19
+ const $wrapHtml = wrapper($html);
20
+ const $wrapEl = (el) => ({
21
+ async mount(parent, idx) {
22
+ $addChild(parent, el, idx);
23
+ return this.el = el;
24
+ },
25
+ async unmount() {
26
+ $remove(this.el);
27
+ this.el = void 0;
28
+ },
29
+ update() {
30
+ }
52
31
  });
32
+ export {
33
+ $wrapEl,
34
+ $wrapHtml,
35
+ $wrapText
36
+ };