canvasengine 2.0.0-beta.6 → 2.0.0-beta.7
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/dist/index.d.ts +15 -7
- package/dist/index.js +74 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/reactive.ts +100 -23
package/package.json
CHANGED
package/src/engine/reactive.ts
CHANGED
|
@@ -23,8 +23,15 @@ export type ArrayChange<T> = {
|
|
|
23
23
|
items: T[];
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
export type ObjectChange<T> = {
|
|
27
|
+
type: "add" | "remove" | "update" | "init" | "reset";
|
|
28
|
+
key?: string;
|
|
29
|
+
value?: T;
|
|
30
|
+
items: T[];
|
|
31
|
+
};
|
|
32
|
+
|
|
26
33
|
type ElementObservable<T> = Observable<
|
|
27
|
-
ArrayChange<T> & {
|
|
34
|
+
(ArrayChange<T> | ObjectChange<T>) & {
|
|
28
35
|
value: Element | Element[];
|
|
29
36
|
}
|
|
30
37
|
>;
|
|
@@ -53,10 +60,13 @@ export interface Element<T = ComponentInstance> {
|
|
|
53
60
|
allElements: Subject<void>;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
|
-
type
|
|
63
|
+
type FlowResult = {
|
|
57
64
|
elements: Element[];
|
|
58
65
|
prev?: Element;
|
|
59
|
-
|
|
66
|
+
fullElements?: Element[];
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
type FlowObservable = Observable<FlowResult>;
|
|
60
70
|
|
|
61
71
|
const components: { [key: string]: any } = {};
|
|
62
72
|
|
|
@@ -292,32 +302,59 @@ export function createComponent(tag: string, props?: Props): Element {
|
|
|
292
302
|
}
|
|
293
303
|
|
|
294
304
|
/**
|
|
295
|
-
* Observes a BehaviorSubject containing an array of items and dynamically creates child elements for each item.
|
|
305
|
+
* Observes a BehaviorSubject containing an array or object of items and dynamically creates child elements for each item.
|
|
296
306
|
*
|
|
297
|
-
* @param {
|
|
307
|
+
* @param {WritableArraySignal<T> | WritableObjectSignal<T>} itemsSubject - A signal that emits an array or object of items.
|
|
298
308
|
* @param {Function} createElementFn - A function that takes an item and returns an element representation.
|
|
299
309
|
* @returns {Observable} An observable that emits the list of created child elements.
|
|
300
310
|
*/
|
|
301
|
-
export function loop<T
|
|
302
|
-
itemsSubject: WritableArraySignal<T>,
|
|
303
|
-
createElementFn: (item:
|
|
311
|
+
export function loop<T>(
|
|
312
|
+
itemsSubject: WritableArraySignal<T[]> | WritableObjectSignal<T>,
|
|
313
|
+
createElementFn: (item: T, index: number | string) => Element | null
|
|
304
314
|
): FlowObservable {
|
|
305
315
|
let elements: Element[] = [];
|
|
306
316
|
|
|
307
|
-
const
|
|
317
|
+
const isArraySignal = '_subject' in itemsSubject && 'items' in (itemsSubject as any)._subject;
|
|
318
|
+
|
|
319
|
+
const addAt = (items: T[], insertIndex: number | string, keys?: string[]): Element[] => {
|
|
308
320
|
return items.map((item, index) => {
|
|
309
|
-
const
|
|
310
|
-
|
|
321
|
+
const key = keys ? keys[index] : (typeof insertIndex === 'number' ? insertIndex + index : insertIndex);
|
|
322
|
+
const element = createElementFn(item, key);
|
|
323
|
+
if (typeof insertIndex === 'number') {
|
|
324
|
+
elements.splice(insertIndex + index, 0, element);
|
|
325
|
+
} else {
|
|
326
|
+
elements.push(element);
|
|
327
|
+
}
|
|
311
328
|
return element;
|
|
312
329
|
});
|
|
313
330
|
};
|
|
314
331
|
|
|
332
|
+
const getInitialItems = () => {
|
|
333
|
+
if (isArraySignal) {
|
|
334
|
+
return {
|
|
335
|
+
items: (itemsSubject as any)._subject.items as T[],
|
|
336
|
+
keys: undefined as string[] | undefined
|
|
337
|
+
};
|
|
338
|
+
} else {
|
|
339
|
+
const entries = Object.entries((itemsSubject as any)._subject.value.value) as [string, T][];
|
|
340
|
+
return {
|
|
341
|
+
items: entries.map(([_, value]) => value),
|
|
342
|
+
keys: entries.map(([key]) => key)
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const { items, keys } = getInitialItems();
|
|
348
|
+
|
|
315
349
|
return defer(() => {
|
|
316
|
-
let initialItems = [...
|
|
350
|
+
let initialItems = [...items];
|
|
351
|
+
let initialKeys = keys ? [...keys] : undefined;
|
|
317
352
|
let init = true;
|
|
318
|
-
return itemsSubject.observable.pipe(
|
|
319
|
-
map((event: ArrayChange<T>) => {
|
|
320
|
-
const { type, items
|
|
353
|
+
return (itemsSubject.observable as Observable<ArrayChange<T> | ObjectChange<T>>).pipe(
|
|
354
|
+
map((event: ArrayChange<T> | ObjectChange<T>): FlowResult => {
|
|
355
|
+
const { type, items } = event;
|
|
356
|
+
const index = 'index' in event ? event.index : (event as ObjectChange<T>).key;
|
|
357
|
+
|
|
321
358
|
if (init) {
|
|
322
359
|
if (elements.length > 0) {
|
|
323
360
|
return {
|
|
@@ -325,8 +362,9 @@ export function loop<T = any>(
|
|
|
325
362
|
fullElements: elements,
|
|
326
363
|
};
|
|
327
364
|
}
|
|
328
|
-
const newElements = addAt(initialItems, 0);
|
|
365
|
+
const newElements = addAt(initialItems, 0, initialKeys);
|
|
329
366
|
initialItems = [];
|
|
367
|
+
initialKeys = undefined;
|
|
330
368
|
init = false;
|
|
331
369
|
return {
|
|
332
370
|
elements: newElements,
|
|
@@ -339,25 +377,64 @@ export function loop<T = any>(
|
|
|
339
377
|
});
|
|
340
378
|
elements = [];
|
|
341
379
|
}
|
|
342
|
-
|
|
380
|
+
if (!isArraySignal) {
|
|
381
|
+
const entries = Object.entries((itemsSubject as any)._subject.value.value) as [string, T][];
|
|
382
|
+
const newElements = addAt(
|
|
383
|
+
entries.map(([_, value]) => value),
|
|
384
|
+
0,
|
|
385
|
+
entries.map(([key]) => key)
|
|
386
|
+
);
|
|
387
|
+
return {
|
|
388
|
+
elements: newElements,
|
|
389
|
+
fullElements: elements,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
const newElements = addAt(items as T[], 0);
|
|
343
393
|
return {
|
|
344
394
|
elements: newElements,
|
|
345
395
|
fullElements: elements,
|
|
346
396
|
};
|
|
347
397
|
} else if (type == "add" && index != undefined) {
|
|
348
|
-
const lastElement = elements[index - 1];
|
|
349
|
-
|
|
398
|
+
const lastElement = typeof index === 'number' ? elements[index - 1] : elements[elements.length - 1];
|
|
399
|
+
let newElements: Element[];
|
|
400
|
+
if (!isArraySignal && typeof index === 'string') {
|
|
401
|
+
// For object updates, create a single element with the new value
|
|
402
|
+
const value = (event as ObjectChange<T>).value;
|
|
403
|
+
if (value !== undefined) {
|
|
404
|
+
newElements = [createElementFn(value, index)];
|
|
405
|
+
elements.push(newElements[0]);
|
|
406
|
+
} else {
|
|
407
|
+
newElements = [];
|
|
408
|
+
}
|
|
409
|
+
} else {
|
|
410
|
+
// For array updates, use addAt with the items array
|
|
411
|
+
newElements = addAt(items as T[], index);
|
|
412
|
+
}
|
|
350
413
|
return {
|
|
351
414
|
prev: lastElement,
|
|
352
415
|
elements: newElements,
|
|
353
416
|
fullElements: elements,
|
|
354
417
|
};
|
|
355
|
-
} else if (
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
418
|
+
} else if (type == "remove") {
|
|
419
|
+
if (!isArraySignal && typeof index === 'string') {
|
|
420
|
+
// For object property deletion
|
|
421
|
+
const elementIndex = elements.findIndex(el => {
|
|
422
|
+
return el.props.text === index || el.props.key === index;
|
|
423
|
+
});
|
|
424
|
+
if (elementIndex !== -1) {
|
|
425
|
+
const currentElement = elements[elementIndex];
|
|
426
|
+
destroyElement(currentElement);
|
|
427
|
+
elements.splice(elementIndex, 1);
|
|
428
|
+
}
|
|
429
|
+
} else if (typeof index === 'number') {
|
|
430
|
+
// For array element deletion
|
|
431
|
+
const currentElement = elements[index];
|
|
432
|
+
destroyElement(currentElement);
|
|
433
|
+
elements.splice(index, 1);
|
|
434
|
+
}
|
|
359
435
|
return {
|
|
360
436
|
elements: [],
|
|
437
|
+
fullElements: elements,
|
|
361
438
|
};
|
|
362
439
|
}
|
|
363
440
|
return {
|