@ixfx/ui 0.50.3 → 0.56.2

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.
Files changed (38) hide show
  1. package/LICENSE +26 -1
  2. package/dist/chunk-BN_g-Awi.js +18 -0
  3. package/dist/index.d.ts +557 -0
  4. package/dist/index.js +958 -0
  5. package/package.json +32 -18
  6. package/bundle/chunk-Cl8Af3a2.js +0 -1
  7. package/bundle/index.d.ts +0 -569
  8. package/bundle/index.d.ts.map +0 -1
  9. package/bundle/index.js +0 -2
  10. package/bundle/index.js.map +0 -1
  11. package/dist/__tests__/test.d.ts +0 -2
  12. package/dist/__tests__/test.d.ts.map +0 -1
  13. package/dist/__tests__/test.js +0 -1
  14. package/dist/src/index.d.ts +0 -2
  15. package/dist/src/index.d.ts.map +0 -1
  16. package/dist/src/index.js +0 -1
  17. package/dist/src/rx/browser-resize.d.ts +0 -26
  18. package/dist/src/rx/browser-resize.d.ts.map +0 -1
  19. package/dist/src/rx/browser-resize.js +0 -41
  20. package/dist/src/rx/browser-theme-change.d.ts +0 -13
  21. package/dist/src/rx/browser-theme-change.d.ts.map +0 -1
  22. package/dist/src/rx/browser-theme-change.js +0 -28
  23. package/dist/src/rx/colour.d.ts +0 -8
  24. package/dist/src/rx/colour.d.ts.map +0 -1
  25. package/dist/src/rx/colour.js +0 -20
  26. package/dist/src/rx/dom-source.d.ts +0 -96
  27. package/dist/src/rx/dom-source.d.ts.map +0 -1
  28. package/dist/src/rx/dom-source.js +0 -374
  29. package/dist/src/rx/dom-types.d.ts +0 -128
  30. package/dist/src/rx/dom-types.d.ts.map +0 -1
  31. package/dist/src/rx/dom-types.js +0 -1
  32. package/dist/src/rx/dom.d.ts +0 -284
  33. package/dist/src/rx/dom.d.ts.map +0 -1
  34. package/dist/src/rx/dom.js +0 -727
  35. package/dist/src/rx/index.d.ts +0 -7
  36. package/dist/src/rx/index.d.ts.map +0 -1
  37. package/dist/src/rx/index.js +0 -5
  38. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,727 +0,0 @@
1
- import { resolveEl } from "@ixfx/dom";
2
- import { Pathed } from "@ixfx/core";
3
- import * as Rx from "@ixfx/rx";
4
- import * as RxFrom from "@ixfx/rx/from";
5
- import { findBySomeKey as mapFindBySomeKey } from "@ixfx/core/maps";
6
- import { afterMatch, beforeMatch, stringSegmentsWholeToEnd, stringSegmentsWholeToFirst } from "@ixfx/core/text";
7
- import { QueueMutable } from "@ixfx/collections";
8
- /**
9
- * Reactive stream of array of elements that match `query`.
10
- * @param query
11
- * @returns
12
- */
13
- export function fromDomQuery(query) {
14
- const elements = [...document.querySelectorAll(query)];
15
- return Rx.From.object(elements);
16
- /// TODO: MutationObserver to update element list
17
- }
18
- /**
19
- * Updates an element's `textContent` when the source value changes.
20
- * ```js
21
- * bindText(source, `#blah`);
22
- * ```
23
- * @param elOrQuery
24
- * @param source
25
- * @param bindOpts
26
- */
27
- export const bindText = (source, elOrQuery, bindOpts = {}) => {
28
- return bindElement(source, elOrQuery, { ...bindOpts, elField: `textContent` });
29
- };
30
- /**
31
- * Updates an element's `value` (as well as the 'value' attribute) when the source value changes.s
32
- * @param source
33
- * @param elOrQuery
34
- * @param bindOpts
35
- * @returns
36
- */
37
- export const bindValueText = (source, elOrQuery, bindOpts = {}) => {
38
- return bindElement(source, elOrQuery, { ...bindOpts, elField: `value`, attribName: `value` });
39
- };
40
- /**
41
- * Updates an element's `valueAsNumber` (as well as the 'value' attribute) when the source value changes.
42
- * ```js
43
- * // Create a reactive number, with a default value of 10
44
- * const r1 = Rx.From.number(10);
45
- * // Bind reactive to HTML input element with id 'inputRange'
46
- * const b1 = Rx.Dom.bindValueRange(r1,`#inputRange`);
47
- *
48
- * // Demo: Change the reactive value every second
49
- * // ...changing the reactive in turn updates the HTML
50
- * setInterval(() => {
51
- * r1.set(Math.floor(Math.random()*100));
52
- * }, 1000);
53
- * ```
54
- * @param source
55
- * @param elOrQuery
56
- * @param bindOpts
57
- * @returns
58
- */
59
- // export const bindValueRange = (source: Rx.Reactive<number>, elOrQuery: string | HTMLInputElement | null, bindOpts: Partial<Rx.DomBindInputOptions<number, number>> = {}) => {
60
- // const el = validateElement(elOrQuery, `range`);
61
- // const b = bindElement<number, number>(source, el, { ...bindOpts, elField: `valueAsNumber`, attribName: `value` });
62
- // const twoway = bindOpts.twoway ?? false;
63
- // const transformFromInput = bindOpts.transformFromInput ?? ((value) => {
64
- // if (typeof value === `number`) return value;
65
- // return Number.parseFloat(value);
66
- // });
67
- // const input = Rx.From.domValueAsNumber(el);
68
- // return setupInput(b, input, source, twoway, transformFromInput);
69
- // }
70
- // export const bindValueColour = (source: Rx.Reactive<Colour.Colourish>, elOrQuery: string | HTMLInputElement | null, bindOpts: Partial<Rx.DomBindInputOptions<Colour.Colourish, string>> = {}) => {
71
- // const el = validateElement(elOrQuery, `color`);
72
- // const b = bindElement<Colour.Colourish, string>(source, el, {
73
- // ...bindOpts,
74
- // elField: `value`,
75
- // attribName: `value`,
76
- // transform(input) {
77
- // console.log(`transform from: ${ JSON.stringify(input) } to hex`);
78
- // const c = Colour.resolve(input);
79
- // return c.to(`srgb`).toString({ format: `hex`, collapse: false });
80
- // },
81
- // });
82
- // const twoway = bindOpts.twoway ?? false;
83
- // const transformFromInput = bindOpts.transformFromInput ?? ((value) => {
84
- // const x = Colour.toHsl(value);
85
- // console.log(`transformFromInput: ${ value } x: ${ JSON.stringify(x) }`);
86
- // return x;
87
- // });
88
- // const input = Rx.From.domValue<Colour.Hsl>(el, {
89
- // domToValue: transformFromInput
90
- // });
91
- // return setupInput(b, input, source, twoway, transformFromInput);
92
- // }
93
- const setupInput = (b, input, source, twoway, transformFromInput) => {
94
- input.onValue(value => {
95
- const v = transformFromInput(value);
96
- if (twoway && Rx.isWritable(source)) {
97
- source.set(v);
98
- }
99
- });
100
- const dispose = () => {
101
- input.dispose(`bindInput twoway dispose`);
102
- b.remove(false);
103
- };
104
- return { ...b, dispose, input };
105
- };
106
- const validateElement = (elOrQuery, type) => {
107
- const el = resolveEl(elOrQuery);
108
- if (el.nodeName !== `INPUT`)
109
- throw new Error(`HTML INPUT element expected. Got: ${el.nodeName}`);
110
- if (type !== undefined && el.type !== type)
111
- throw new Error(`HTML INPUT element expected with type 'range'. Got: ${el.type}`);
112
- return el;
113
- };
114
- /**
115
- * Updates an element's `innerHTML` when the source value changes
116
- * ```js
117
- * bindHtml(source, `#blah`);
118
- * ```
119
- *
120
- * Uses {@link bindElement}, with `{elField:'innerHTML'}` as the options.
121
- * @param elOrQuery
122
- * @param source
123
- * @param bindOpts
124
- * @returns
125
- */
126
- export const bindHtml = (source, elOrQuery, bindOpts = {}) => {
127
- return bindElement(source, elOrQuery, { ...bindOpts, elField: `innerHTML` });
128
- };
129
- /**
130
- * Shortcut to bind to an elements attribute
131
- * @param elOrQuery
132
- * @param source
133
- * @param attribute
134
- * @param bindOpts
135
- * @returns
136
- */
137
- // export const bindAttribute = <V>(elOrQuery: string | HTMLElement, source: Rx.Reactive<V>, attribute: string, bindOpts: Partial<DomBindOptions<V>> = {}) => {
138
- // return bind(elOrQuery, source, { ...bindOpts, attribName: attribute });
139
- // }
140
- /**
141
- * Shortcut to bind to a CSS variable
142
- * @param elOrQuery
143
- * @param source
144
- * @param cssVariable
145
- * @param bindOpts
146
- * @returns
147
- */
148
- // export const bindCssVariable = <V>(elOrQuery: string | HTMLElement, source: Rx.Reactive<V>, cssVariable: string, bindOpts: Partial<DomBindOptions<V>> = {}) => {
149
- // return bind(elOrQuery, source, { ...bindOpts, cssVariable: cssVariable });
150
- // }
151
- /**
152
- * Creates a new HTML element, calling {@link bind} on it to update when `source` emits new values.
153
- *
154
- *
155
- * ```js
156
- * // Set textContent of a SPAN with values from `source`
157
- * create(source, { tagName: `span`, parentEl: document.body })
158
- * ```
159
- *
160
- * If `parentEl` is not given in the options, the created element needs to be manually added
161
- * ```js
162
- * const b = create(source);
163
- * someEl.append(b.el); // Append manually
164
- * ```
165
- *
166
- * ```
167
- * // Set 'title' attribute based on values from `source`
168
- * create(source, { parentEl: document.body, attribName: `title` })
169
- * ```
170
- * @param source
171
- * @param options
172
- * @returns
173
- */
174
- // export const create = <V>(source: Rx.Reactive<V>, options: Partial<DomCreateOptions> & Partial<DomBindOptions<V>> = {}): PipeDomBinding => {
175
- // const nodeType = options.tagName ?? `DIV`;
176
- // const el = document.createElement(nodeType);
177
- // const b = bind(el, source, options);
178
- // if (options.parentEl) {
179
- // const parentElementOrQuery = resolveEl(options.parentEl);
180
- // if (parentElementOrQuery === undefined) throw new Error(`Parent element could not be resolved`);
181
- // parentElementOrQuery.append(el);
182
- // }
183
- // return b;
184
- // }
185
- /**
186
- * Update a DOM element's field, attribute or CSS variable when `source` produces a value.
187
- *
188
- * ```js
189
- * // Access via DOM query. Binds to 'textContent' by default
190
- * bind(readableSource, `#someEl`);
191
- *
192
- * // Set innerHTML instead
193
- * bind(readableSource, someEl, { elField: `innerHTML` });
194
- *
195
- * // An attribute
196
- * bind(readableSource, someEl, { attribName: `width` });
197
- *
198
- * // A css variable ('--' optiona)
199
- * bind(readableSource, someEl, { cssVariable: `hue` });
200
- *
201
- * // Pluck a particular field from source data.
202
- * // Ie someEl.textContent = value.colour
203
- * bind(readableSource, someEl, { sourceField: `colour` });
204
- *
205
- * // Transform value before setting it to field
206
- * bind(readableSource, someEl, {
207
- * field: `innerHTML`,
208
- * transform: (v) => `Colour: ${v.colour}`
209
- * })
210
- * ```
211
- *
212
- * If `source` has an initial value, this is used when first bound.
213
- *
214
- * Returns {@link PipeDomBinding} to control binding:
215
- * ```js
216
- * const bind = bind(source, `#someEl`);
217
- * bind.remove(); // Unbind
218
- * bind.remove(true); // Unbind and remove HTML element
219
- * ```
220
- *
221
- * If several fields need to be updated based on a new value, consider using {@link bindUpdate} instead.
222
- * @param elOrQuery Element to update to, or query string such as '#someid'
223
- * @param source Source of data
224
- * @param binds Bindings
225
- */
226
- export const bindElement = (source, elOrQuery, ...binds) => {
227
- if (elOrQuery === null)
228
- throw new Error(`Param 'elOrQuery' is null`);
229
- if (elOrQuery === undefined)
230
- throw new Error(`Param 'elOrQuery' is undefined`);
231
- const el = resolveEl(elOrQuery);
232
- let b = [];
233
- if (binds.length === 0) {
234
- b.push({ elField: `textContent` });
235
- }
236
- else {
237
- b = [...binds];
238
- }
239
- const bb = b.map(bind => {
240
- if (`element` in bind)
241
- return bind;
242
- return { ...bind, element: el };
243
- });
244
- return bind(source, ...bb);
245
- };
246
- const resolveBindUpdater = (bind, element) => {
247
- const b = resolveBindUpdaterBase(bind);
248
- return (value) => {
249
- b(value, element);
250
- };
251
- };
252
- const resolveBindUpdaterBase = (bind) => {
253
- if (bind.elField !== undefined || (bind.cssVariable === undefined && bind.attribName === undefined && bind.cssProperty === undefined && bind.textContent === undefined && bind.htmlContent === undefined)) {
254
- const field = bind.elField ?? `textContent`;
255
- return (v, element) => {
256
- element[field] = v;
257
- };
258
- }
259
- if (bind.attribName !== undefined) {
260
- const attrib = bind.attribName;
261
- return (v, element) => {
262
- element.setAttribute(attrib, v);
263
- };
264
- }
265
- if (bind.textContent) {
266
- return (v, element) => {
267
- element.textContent = v;
268
- };
269
- }
270
- if (bind.htmlContent) {
271
- return (v, element) => {
272
- element.innerHTML = v;
273
- };
274
- }
275
- if (bind.cssVariable !== undefined) {
276
- let css = bind.cssVariable;
277
- if (!css.startsWith(`--`))
278
- css = `--` + css;
279
- return (v, element) => {
280
- element.style.setProperty(css, v);
281
- };
282
- }
283
- if (bind.cssProperty !== undefined) {
284
- return (v, element) => {
285
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
286
- element.style[bind.cssProperty] = v;
287
- };
288
- }
289
- return (_, _element) => {
290
- /** no-op */
291
- };
292
- };
293
- const resolveTransform = (bind) => {
294
- if (!bind.transform && !bind.transformValue)
295
- return;
296
- if (bind.transformValue) {
297
- if (bind.sourceField === undefined)
298
- throw new Error(`Expects 'sourceField' to be set when 'transformValue' is set`);
299
- return (value) => {
300
- const fieldValue = value[bind.sourceField];
301
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
302
- return bind.transformValue(fieldValue);
303
- };
304
- }
305
- else if (bind.transform) {
306
- if (bind.sourceField !== undefined)
307
- throw new Error(`If 'transform' is set, 'sourceField' is ignored`);
308
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
309
- return (value) => bind.transform(value);
310
- }
311
- };
312
- /**
313
- * Binds `source` to one or more element(s). One or more bindings for the same source
314
- * can be provided.
315
- *
316
- * ```js
317
- * bind(source,
318
- * // Binds .name field of source values to textContent of #some-element
319
- * { query: `#some-element`, sourceField: `name` },
320
- * { query: `section`, }
321
- * );
322
- * ```
323
- *
324
- * Can update
325
- * * CSS variables
326
- * * CSS styles
327
- * * textContent / innerHTML
328
- * * HTML DOM attributes and object fields
329
- *
330
- * Can use a particular field on source values, or use the whole value. These can
331
- * pass through `transformValue` or `transform` respectively.
332
- *
333
- * Returns a function to unbind from source and optionally remove HTML element
334
- * ```js
335
- * const unbind = bind( . . . );
336
- * unbind(); // Unbind
337
- * unbind(true); // Unbind and remove HTML element(s)
338
- * ```
339
- * @param source
340
- * @param bindsUnresolvedElements
341
- * @returns
342
- */
343
- export const bind = (source, ...bindsUnresolvedElements) => {
344
- const binds = bindsUnresolvedElements.map(bind => {
345
- if (bind.element && bind.element !== undefined)
346
- return bind;
347
- if (bind.query)
348
- return {
349
- ...bind,
350
- element: resolveEl(bind.query)
351
- };
352
- throw new Error(`Unable to resolve element. Missing 'element' or 'query' values on bind. ${JSON.stringify(bind)}`);
353
- });
354
- const bindsResolved = binds.map(bind => ({
355
- update: resolveBindUpdater(bind, bind.element),
356
- transformer: resolveTransform(bind),
357
- sourceField: bind.sourceField
358
- }));
359
- const update = (value) => {
360
- for (const bind of bindsResolved) {
361
- if (bind.transformer) {
362
- bind.update(bind.transformer(value));
363
- }
364
- else {
365
- const v = (bind.sourceField) ? value[bind.sourceField] : value;
366
- if (typeof v === `object`) {
367
- if (bind.sourceField) {
368
- bind.update(JSON.stringify(v));
369
- }
370
- else {
371
- bind.update(JSON.stringify(v));
372
- }
373
- }
374
- else
375
- bind.update(v);
376
- }
377
- }
378
- };
379
- const unsub = source.on(message => {
380
- if (Rx.messageHasValue(message)) {
381
- update(message.value);
382
- }
383
- else if (Rx.messageIsSignal(message)) {
384
- console.warn(message);
385
- }
386
- });
387
- if (Rx.hasLast(source)) {
388
- update(source.last());
389
- }
390
- return {
391
- remove: (removeElements) => {
392
- unsub();
393
- if (removeElements) {
394
- for (const bind of binds) {
395
- bind.element.remove();
396
- }
397
- }
398
- }
399
- };
400
- };
401
- /**
402
- * Calls `updater` whenever `source` produces a value. Useful when several fields from a value
403
- * are needed to update an element.
404
- * ```js
405
- * bindUpdate(source, `#someEl`, (v, el) => {
406
- * el.setAttribute(`width`, v.width);
407
- * el.setAttribute(`height`, v.height);
408
- * });
409
- * ```
410
- *
411
- * Returns a {@link PipeDomBinding} to manage binding
412
- * ```js
413
- * const b = bindUpdate(...);
414
- * b.remove(); // Disconnect binding
415
- * b.remove(true); // Disconnect binding and remove element
416
- * b.el; // HTML element
417
- * ```
418
- * @param elOrQuery
419
- * @param source
420
- * @param updater
421
- * @returns
422
- */
423
- export const bindUpdate = (source, elOrQuery, updater) => {
424
- const el = resolveEl(elOrQuery);
425
- const update = (value) => {
426
- updater(value, el);
427
- };
428
- const unsub = source.on(message => {
429
- if (Rx.messageHasValue(message)) {
430
- console.log(message);
431
- update(message.value);
432
- }
433
- else {
434
- console.warn(message);
435
- }
436
- });
437
- if (Rx.hasLast(source)) {
438
- update(source.last());
439
- }
440
- return {
441
- remove: (removeElement) => {
442
- unsub();
443
- if (removeElement) {
444
- el.remove();
445
- }
446
- }
447
- };
448
- };
449
- /**
450
- * Updates a HTML element based on diffs on an object.
451
- * ```js
452
- * // Wrap an object
453
- * const o = Rx.object({ name: `Jane`, ticks: 0 });
454
- * const b = bindDiffUpdate(`#test`, o, (diffs, el) => {
455
- * // el = reference to #test
456
- * // diff = Array of Changes,
457
- * // eg [ { path: `ticks`, value: 797, previous: 0 } ]
458
- * for (const diff of diffs) {
459
- * if (diff.path === `ticks`) el.textContent = `${diff.previous} -> ${diff.value}`
460
- * }
461
- * })
462
- *
463
- * // Eg. update field
464
- * o.updateField(`ticks`, Math.floor(Math.random()*1000));
465
- * ```
466
- *
467
- * If `initial` is provided as an option, this will be called if `source` has an initial value. Without this, the DOM won't be updated until the first data
468
- * update happens.
469
- * ```js
470
- * bindDiffUpdate(el, source, updater, {
471
- * initial: (v, el) => {
472
- * el.innerHTML = v.name;
473
- * }
474
- * })
475
- * ```
476
- * @param elOrQuery
477
- * @param source
478
- * @param updater
479
- * @param opts
480
- * @returns
481
- */
482
- export const bindDiffUpdate = (source, elOrQuery, updater, opts = {}) => {
483
- if (elOrQuery === null)
484
- throw new Error(`Param 'elOrQuery' is null`);
485
- if (elOrQuery === undefined)
486
- throw new Error(`Param 'elOrQuery' is undefined`);
487
- const el = resolveEl(elOrQuery);
488
- //const binds = opts.binds;
489
- const update = (value) => {
490
- updater(value, el);
491
- };
492
- const unsub = source.onDiff(value => {
493
- update(value);
494
- });
495
- const init = () => {
496
- if (Rx.hasLast(source) && opts.initial)
497
- opts.initial(source.last(), el);
498
- };
499
- init();
500
- return {
501
- refresh: () => {
502
- init();
503
- },
504
- remove: (removeElement) => {
505
- unsub();
506
- if (removeElement) {
507
- el.remove();
508
- }
509
- }
510
- };
511
- };
512
- /**
513
- * Creates a new HTML element and calls `bindUpdate` so values from `source` can be used
514
- * to update it.
515
- *
516
- *
517
- * ```js
518
- * // Creates a span, adding it to <body>
519
- * const b = createUpdate(dataSource, (value, el) => {
520
- * el.width = value.width;
521
- * el.height = value.height;
522
- * }, {
523
- * tagName: `SPAN`,
524
- * parentEl: document.body
525
- * })
526
- * ```
527
- * @param source
528
- * @param updater
529
- * @param options
530
- * @returns
531
- */
532
- // export const createUpdate = <V>(source: Rx.Reactive<V>, updater: (v: V, el: HTMLElement) => void, options: Partial<DomCreateOptions> = {}): PipeDomBinding => {
533
- // const tag = options.tagName ?? `DIV`;
534
- // const el = document.createElement(tag);
535
- // if (options.parentEl) {
536
- // const parent = resolveEl(options.parentEl);
537
- // parent.append(el);
538
- // }
539
- // const b = bindUpdate(source, el, updater);
540
- // return b;
541
- // }
542
- /**
543
- * Creates, updates & deletes elements based on pathed values from a reactive.
544
- *
545
- * This means that elements are only manipulated if its associated data changes,
546
- * and elements are not modified if there's no need to.
547
- * @param source
548
- * @param options
549
- */
550
- export const elements = (source, options) => {
551
- const containerEl = options.container ? resolveEl(options.container) : document.body;
552
- const defaultTag = options.defaultTag ?? `div`;
553
- const elByField = new Map();
554
- const binds = new Map();
555
- for (const [key, value] of Object.entries(options.binds ?? {})) {
556
- const tagName = value.tagName ?? defaultTag;
557
- //console.log(`key: ${ key }`);
558
- binds.set(key, {
559
- ...value,
560
- update: resolveBindUpdaterBase(value),
561
- transform: resolveTransform(value),
562
- tagName,
563
- path: key
564
- });
565
- }
566
- const findBind = (path) => {
567
- const bind = mapFindBySomeKey(binds, stringSegmentsWholeToEnd(path));
568
- if (bind !== undefined)
569
- return bind;
570
- if (!path.includes(`.`))
571
- return binds.get(`_root`);
572
- };
573
- function* ancestorBinds(path) {
574
- for (const p of stringSegmentsWholeToFirst(path)) {
575
- //console.log(` ancestorBinds path: ${ path } segment: ${ p }`)
576
- if (binds.has(p)) {
577
- //console.log(` bind: ${ p } found: ${ JSON.stringify(binds.get(p)) }`);
578
- yield binds.get(p);
579
- }
580
- else {
581
- //console.log(` bind: ${ p } not found`);
582
- }
583
- }
584
- if (binds.has(`_root`) && path.includes(`.`))
585
- yield binds.get(`_root`);
586
- }
587
- const create = (path, value) => {
588
- const rootedPath = getRootedPath(path);
589
- console.log(`Rx.Dom.elements.create: ${path} rooted: ${rootedPath} value: ${JSON.stringify(value)}`);
590
- // Create
591
- const bind = findBind(getRootedPath(path));
592
- let tagName = defaultTag;
593
- if (bind?.tagName)
594
- tagName = bind.tagName;
595
- const el = document.createElement(tagName);
596
- el.setAttribute(`data-path`, path);
597
- update(path, el, value);
598
- let parentForEl;
599
- for (const b of ancestorBinds(rootedPath)) {
600
- //console.log(` path: ${ rootedPath } b: ${ JSON.stringify(b) }`);
601
- if (b?.nestChildren) {
602
- // Get root of path
603
- const absoluteRoot = beforeMatch(path, `.`);
604
- const findBy = b.path.replace(`_root`, absoluteRoot);
605
- parentForEl = elByField.get(findBy);
606
- if (parentForEl === undefined) {
607
- //console.log(` could not find parent. path: ${ path } b.path: ${ b.path } findBy: ${ findBy }`);
608
- }
609
- else {
610
- //console.log(` found parent`);
611
- break;
612
- }
613
- }
614
- }
615
- (parentForEl ?? containerEl).append(el);
616
- elByField.set(path, el);
617
- console.log(`Added el: ${path}`);
618
- };
619
- const update = (path, el, value) => {
620
- console.log(`Rx.dom.update path: ${path} value:`, value);
621
- const bind = findBind(getRootedPath(path));
622
- if (bind === undefined) {
623
- //console.log(`Rx.dom.update no bind for ${ path }`)
624
- if (typeof value === `object`)
625
- value = JSON.stringify(value);
626
- el.textContent = value;
627
- }
628
- else {
629
- //console.log(`Rx.dom.update got bind! ${ path } `);
630
- if (bind.transform)
631
- value = bind.transform(value);
632
- bind.update(value, el);
633
- }
634
- };
635
- const changes = (changes) => {
636
- const queue = new QueueMutable({}, changes);
637
- let d = queue.dequeue();
638
- const seenPaths = new Set();
639
- while (d !== undefined) {
640
- //for (const d of changes) {
641
- const path = d.path;
642
- if (!(`previous` in d) || d.previous === undefined) {
643
- // Create
644
- console.log(`Rx.Dom.elements.changes no previous. path: ${path}`);
645
- create(path, d.value);
646
- const subdata = [...Pathed.getPathsAndData(d.value, false, Number.MAX_SAFE_INTEGER, path)];
647
- console.log(subdata);
648
- for (const dd of subdata) {
649
- if (!seenPaths.has(dd.path)) {
650
- queue.enqueue(dd);
651
- seenPaths.add(dd.path);
652
- }
653
- }
654
- }
655
- else if (d.value === undefined) {
656
- // Delete
657
- const el = elByField.get(path);
658
- if (el === undefined) {
659
- console.warn(`No element to delete? ${path} `);
660
- }
661
- else {
662
- console.log(`Rx.Dom.elements.changes delete ${path}`);
663
- el.remove();
664
- }
665
- }
666
- else {
667
- // Update
668
- const el = elByField.get(path);
669
- if (el === undefined) {
670
- console.warn(`Rx.Dom.elements.changes No element to update ? ${path} `);
671
- create(path, d.value);
672
- }
673
- else {
674
- //console.log(`Rx.Dom.elements.changes Updating ${ path } `, el);
675
- update(path, el, d.value);
676
- }
677
- }
678
- d = queue.dequeue();
679
- }
680
- };
681
- /**
682
- * Source has changed
683
- */
684
- source.onDiff(value => {
685
- //console.log(`Rx.Dom.elements diff ${ JSON.stringify(value) } `);
686
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
687
- changes(value);
688
- });
689
- // Source has an initial value, use that
690
- if (Rx.hasLast(source)) {
691
- const last = source.last();
692
- // Get data of value as a set of paths and data
693
- // but only at first level of depth, because changes() will probe
694
- // deeper itself
695
- changes([...Pathed.getPathsAndData(last, false, 1)]);
696
- }
697
- };
698
- /**
699
- * Replaces the root portion of `path` with the magic keyword `_root`
700
- * @param path
701
- * @returns
702
- */
703
- const getRootedPath = (path) => {
704
- const after = afterMatch(path, `.`);
705
- return after === path ? `_root` : `_root.` + after;
706
- };
707
- export function win() {
708
- const generateRect = () => ({ width: window.innerWidth, height: window.innerHeight });
709
- const size = RxFrom.event(window, `resize`, {
710
- lazy: `very`,
711
- transform: () => generateRect(),
712
- });
713
- const pointer = RxFrom.event(window, `pointermove`, {
714
- lazy: `very`,
715
- transform: (args) => {
716
- if (args === undefined)
717
- return { x: 0, y: 0 };
718
- const pe = args;
719
- return { x: pe.x, y: pe.y };
720
- }
721
- });
722
- const dispose = (reason = `Reactive.win.dispose`) => {
723
- size.dispose(reason);
724
- pointer.dispose(reason);
725
- };
726
- return { dispose, size, pointer };
727
- }