canvasengine 2.0.0-beta.2 → 2.0.0-beta.21
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 +1289 -0
- package/dist/index.js +4248 -0
- package/dist/index.js.map +1 -0
- package/index.d.ts +4 -0
- package/package.json +5 -12
- package/src/components/Canvas.ts +53 -45
- package/src/components/Container.ts +2 -2
- package/src/components/DOMContainer.ts +123 -0
- package/src/components/DOMElement.ts +421 -0
- package/src/components/DisplayObject.ts +263 -189
- package/src/components/Graphic.ts +213 -36
- package/src/components/Mesh.ts +222 -0
- package/src/components/NineSliceSprite.ts +4 -1
- package/src/components/ParticleEmitter.ts +12 -8
- package/src/components/Sprite.ts +77 -14
- package/src/components/Text.ts +34 -14
- package/src/components/Video.ts +110 -0
- package/src/components/Viewport.ts +59 -43
- package/src/components/index.ts +6 -4
- package/src/components/types/DisplayObject.ts +30 -0
- package/src/directives/Drag.ts +357 -52
- package/src/directives/KeyboardControls.ts +3 -1
- package/src/directives/Sound.ts +94 -31
- package/src/directives/ViewportFollow.ts +35 -7
- package/src/engine/animation.ts +41 -5
- package/src/engine/bootstrap.ts +22 -3
- package/src/engine/directive.ts +2 -2
- package/src/engine/reactive.ts +337 -168
- package/src/engine/trigger.ts +65 -9
- package/src/engine/utils.ts +97 -9
- package/src/hooks/useProps.ts +1 -1
- package/src/index.ts +5 -1
- package/src/utils/RadialGradient.ts +29 -0
- package/src/utils/functions.ts +7 -0
- package/testing/index.ts +12 -0
- package/src/components/DrawMap/index.ts +0 -65
- package/src/components/Tilemap/Tile.ts +0 -79
- package/src/components/Tilemap/TileGroup.ts +0 -207
- package/src/components/Tilemap/TileLayer.ts +0 -163
- package/src/components/Tilemap/TileSet.ts +0 -41
- package/src/components/Tilemap/index.ts +0 -80
package/src/engine/reactive.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Signal, WritableArraySignal, isSignal } from "@signe/reactive";
|
|
1
|
+
import { ArrayChange, ObjectChange, Signal, WritableArraySignal, WritableObjectSignal, isComputed, isSignal, signal } from "@signe/reactive";
|
|
2
2
|
import {
|
|
3
3
|
Observable,
|
|
4
4
|
Subject,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
from,
|
|
8
8
|
map,
|
|
9
9
|
of,
|
|
10
|
+
share,
|
|
10
11
|
switchMap,
|
|
11
12
|
} from "rxjs";
|
|
12
13
|
import { ComponentInstance } from "../components/DisplayObject";
|
|
@@ -17,18 +18,6 @@ export interface Props {
|
|
|
17
18
|
[key: string]: any;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
export type ArrayChange<T> = {
|
|
21
|
-
type: "add" | "remove" | "update" | "init" | "reset";
|
|
22
|
-
index?: number;
|
|
23
|
-
items: T[];
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
type ElementObservable<T> = Observable<
|
|
27
|
-
ArrayChange<T> & {
|
|
28
|
-
value: Element | Element[];
|
|
29
|
-
}
|
|
30
|
-
>;
|
|
31
|
-
|
|
32
21
|
type NestedSignalObjects = {
|
|
33
22
|
[Key in string]: NestedSignalObjects | Signal<any>;
|
|
34
23
|
};
|
|
@@ -53,10 +42,13 @@ export interface Element<T = ComponentInstance> {
|
|
|
53
42
|
allElements: Subject<void>;
|
|
54
43
|
}
|
|
55
44
|
|
|
56
|
-
type
|
|
45
|
+
type FlowResult = {
|
|
57
46
|
elements: Element[];
|
|
58
47
|
prev?: Element;
|
|
59
|
-
|
|
48
|
+
fullElements?: Element[];
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type FlowObservable = Observable<FlowResult>;
|
|
60
52
|
|
|
61
53
|
const components: { [key: string]: any } = {};
|
|
62
54
|
|
|
@@ -92,13 +84,14 @@ function destroyElement(element: Element | Element[]) {
|
|
|
92
84
|
if (!element) {
|
|
93
85
|
return;
|
|
94
86
|
}
|
|
95
|
-
element.propSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
96
|
-
element.effectSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
97
87
|
for (let name in element.directives) {
|
|
98
|
-
element.directives[name].onDestroy?.();
|
|
88
|
+
element.directives[name].onDestroy?.(element);
|
|
99
89
|
}
|
|
100
|
-
element.componentInstance.onDestroy
|
|
101
|
-
|
|
90
|
+
element.componentInstance.onDestroy(element.parent as any, () => {
|
|
91
|
+
element.propSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
92
|
+
element.effectSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
93
|
+
element.effectUnmounts.forEach((fn) => fn?.());
|
|
94
|
+
});
|
|
102
95
|
}
|
|
103
96
|
|
|
104
97
|
/**
|
|
@@ -155,7 +148,7 @@ export function createComponent(tag: string, props?: Props): Element {
|
|
|
155
148
|
_value.observable.subscribe((value) => {
|
|
156
149
|
_set(path, key, value);
|
|
157
150
|
if (element.directives[key]) {
|
|
158
|
-
element.directives[key].onUpdate?.(value);
|
|
151
|
+
element.directives[key].onUpdate?.(value, element);
|
|
159
152
|
}
|
|
160
153
|
if (key == "tick") {
|
|
161
154
|
return
|
|
@@ -182,9 +175,24 @@ export function createComponent(tag: string, props?: Props): Element {
|
|
|
182
175
|
}
|
|
183
176
|
|
|
184
177
|
instance.onInit?.(element.props);
|
|
185
|
-
instance.onUpdate?.(element.props);
|
|
186
178
|
|
|
187
|
-
const
|
|
179
|
+
const elementsListen = new Subject<any>()
|
|
180
|
+
|
|
181
|
+
if (props?.isRoot) {
|
|
182
|
+
element.allElements = elementsListen
|
|
183
|
+
element.props.context.rootElement = element;
|
|
184
|
+
element.componentInstance.onMount?.(element);
|
|
185
|
+
propagateContext(element);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (props) {
|
|
189
|
+
for (let key in props) {
|
|
190
|
+
const directive = applyDirective(element, key);
|
|
191
|
+
if (directive) element.directives[key] = directive;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function onMount(parent: Element, element: Element, index?: number) {
|
|
188
196
|
element.props.context = parent.props.context;
|
|
189
197
|
element.parent = parent;
|
|
190
198
|
element.componentInstance.onMount?.(element, index);
|
|
@@ -196,68 +204,79 @@ export function createComponent(tag: string, props?: Props): Element {
|
|
|
196
204
|
});
|
|
197
205
|
};
|
|
198
206
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (!element.props.children) {
|
|
205
|
-
return;
|
|
207
|
+
async function propagateContext(element) {
|
|
208
|
+
if (element.props.attach) {
|
|
209
|
+
const isReactiveAttach = isSignal(element.propObservables?.attach)
|
|
210
|
+
if (!isReactiveAttach) {
|
|
211
|
+
element.props.children.push(element.props.attach)
|
|
206
212
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}: {
|
|
218
|
-
elements: Element[];
|
|
219
|
-
prev?: Element;
|
|
220
|
-
}) => {
|
|
221
|
-
// if prev, insert element after this
|
|
222
|
-
const components = comp.filter((c) => c !== null);
|
|
223
|
-
if (prev) {
|
|
224
|
-
components.forEach((c) => {
|
|
225
|
-
const index = element.props.children.indexOf(prev.props.key);
|
|
226
|
-
onMount(element, c, index + 1);
|
|
227
|
-
propagateContext(c);
|
|
228
|
-
});
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
components.forEach((component) => {
|
|
232
|
-
if (!Array.isArray(component)) {
|
|
233
|
-
onMount(element, component);
|
|
234
|
-
propagateContext(component);
|
|
235
|
-
} else {
|
|
236
|
-
component.forEach((comp) => {
|
|
237
|
-
onMount(element, comp);
|
|
238
|
-
propagateContext(comp);
|
|
239
|
-
});
|
|
213
|
+
else {
|
|
214
|
+
await new Promise((resolve) => {
|
|
215
|
+
let lastElement = null
|
|
216
|
+
element.propSubscriptions.push(element.propObservables.attach.observable.subscribe(async (args) => {
|
|
217
|
+
const value = args?.value ?? args
|
|
218
|
+
if (!value) {
|
|
219
|
+
throw new Error(`attach in ${element.tag} is undefined or null, add a component`)
|
|
220
|
+
}
|
|
221
|
+
if (lastElement) {
|
|
222
|
+
destroyElement(lastElement)
|
|
240
223
|
}
|
|
224
|
+
lastElement = value
|
|
225
|
+
await createElement(element, value)
|
|
226
|
+
resolve(undefined)
|
|
227
|
+
}))
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (!element.props.children) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
for (let child of element.props.children) {
|
|
235
|
+
if (!child) continue;
|
|
236
|
+
await createElement(element, child)
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
async function createElement(parent: Element, child: Element) {
|
|
241
|
+
if (isPromise(child)) {
|
|
242
|
+
child = await child;
|
|
243
|
+
}
|
|
244
|
+
if (child instanceof Observable) {
|
|
245
|
+
child.subscribe(
|
|
246
|
+
({
|
|
247
|
+
elements: comp,
|
|
248
|
+
prev,
|
|
249
|
+
}: {
|
|
250
|
+
elements: Element[];
|
|
251
|
+
prev?: Element;
|
|
252
|
+
}) => {
|
|
253
|
+
// if prev, insert element after this
|
|
254
|
+
const components = comp.filter((c) => c !== null);
|
|
255
|
+
if (prev) {
|
|
256
|
+
components.forEach((c) => {
|
|
257
|
+
const index = parent.props.children.indexOf(prev.props.key);
|
|
258
|
+
onMount(parent, c, index + 1);
|
|
259
|
+
propagateContext(c);
|
|
260
|
+
});
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
components.forEach((component) => {
|
|
264
|
+
if (!Array.isArray(component)) {
|
|
265
|
+
onMount(parent, component);
|
|
266
|
+
propagateContext(component);
|
|
267
|
+
} else {
|
|
268
|
+
component.forEach((comp) => {
|
|
269
|
+
onMount(parent, comp);
|
|
270
|
+
propagateContext(comp);
|
|
241
271
|
});
|
|
242
|
-
elementsListen.next(undefined)
|
|
243
272
|
}
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
onMount(element, child);
|
|
247
|
-
await propagateContext(child);
|
|
273
|
+
});
|
|
274
|
+
elementsListen.next(undefined)
|
|
248
275
|
}
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
element.componentInstance.onMount?.(element);
|
|
254
|
-
propagateContext(element);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (props) {
|
|
258
|
-
for (let key in props) {
|
|
259
|
-
const directive = applyDirective(element, key);
|
|
260
|
-
if (directive) element.directives[key] = directive;
|
|
276
|
+
);
|
|
277
|
+
} else {
|
|
278
|
+
onMount(parent, child);
|
|
279
|
+
await propagateContext(child);
|
|
261
280
|
}
|
|
262
281
|
}
|
|
263
282
|
|
|
@@ -266,114 +285,264 @@ export function createComponent(tag: string, props?: Props): Element {
|
|
|
266
285
|
}
|
|
267
286
|
|
|
268
287
|
/**
|
|
269
|
-
* Observes a BehaviorSubject containing an array of items and dynamically creates child elements for each item.
|
|
288
|
+
* Observes a BehaviorSubject containing an array or object of items and dynamically creates child elements for each item.
|
|
270
289
|
*
|
|
271
|
-
* @param {
|
|
290
|
+
* @param {WritableArraySignal<T> | WritableObjectSignal<T>} itemsSubject - A signal that emits an array or object of items.
|
|
272
291
|
* @param {Function} createElementFn - A function that takes an item and returns an element representation.
|
|
273
292
|
* @returns {Observable} An observable that emits the list of created child elements.
|
|
274
293
|
*/
|
|
275
|
-
export function loop<T
|
|
276
|
-
itemsSubject:
|
|
277
|
-
createElementFn: (item:
|
|
294
|
+
export function loop<T>(
|
|
295
|
+
itemsSubject: any,
|
|
296
|
+
createElementFn: (item: T, index: number | string) => Element | null
|
|
278
297
|
): FlowObservable {
|
|
279
|
-
let elements: Element[] = [];
|
|
280
298
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
};
|
|
299
|
+
if (isComputed(itemsSubject) && itemsSubject.dependencies.size == 0) {
|
|
300
|
+
itemsSubject = signal(itemsSubject());
|
|
301
|
+
}
|
|
302
|
+
else if (!isSignal(itemsSubject)) {
|
|
303
|
+
itemsSubject = signal(itemsSubject);
|
|
304
|
+
}
|
|
288
305
|
|
|
289
306
|
return defer(() => {
|
|
290
|
-
let
|
|
291
|
-
let
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
307
|
+
let elements: Element[] = [];
|
|
308
|
+
let elementMap = new Map<string | number, Element>();
|
|
309
|
+
let isFirstSubscription = true;
|
|
310
|
+
|
|
311
|
+
const isArraySignal = (signal: any): signal is WritableArraySignal<T[]> =>
|
|
312
|
+
Array.isArray(signal());
|
|
313
|
+
|
|
314
|
+
return new Observable<FlowResult>(subscriber => {
|
|
315
|
+
const subscription = isArraySignal(itemsSubject)
|
|
316
|
+
? itemsSubject.observable.subscribe(change => {
|
|
317
|
+
if (isFirstSubscription) {
|
|
318
|
+
isFirstSubscription = false;
|
|
319
|
+
elements.forEach(el => el.destroy());
|
|
320
|
+
elements = [];
|
|
321
|
+
elementMap.clear();
|
|
322
|
+
|
|
323
|
+
const items = itemsSubject();
|
|
324
|
+
if (items) {
|
|
325
|
+
items.forEach((item, index) => {
|
|
326
|
+
const element = createElementFn(item, index);
|
|
327
|
+
if (element) {
|
|
328
|
+
elements.push(element);
|
|
329
|
+
elementMap.set(index, element);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
subscriber.next({
|
|
334
|
+
elements: [...elements]
|
|
335
|
+
});
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (change.type === 'init' || change.type === 'reset') {
|
|
340
|
+
elements.forEach(el => el.destroy());
|
|
341
|
+
elements = [];
|
|
342
|
+
elementMap.clear();
|
|
343
|
+
|
|
344
|
+
const items = itemsSubject();
|
|
345
|
+
if (items) {
|
|
346
|
+
items.forEach((item, index) => {
|
|
347
|
+
const element = createElementFn(item, index);
|
|
348
|
+
if (element) {
|
|
349
|
+
elements.push(element);
|
|
350
|
+
elementMap.set(index, element);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
} else if (change.type === 'add' && change.index !== undefined) {
|
|
355
|
+
const newElements = change.items.map((item, i) => {
|
|
356
|
+
const element = createElementFn(item as T, change.index! + i);
|
|
357
|
+
if (element) {
|
|
358
|
+
elementMap.set(change.index! + i, element);
|
|
359
|
+
}
|
|
360
|
+
return element;
|
|
361
|
+
}).filter((el): el is Element => el !== null);
|
|
362
|
+
|
|
363
|
+
elements.splice(change.index, 0, ...newElements);
|
|
364
|
+
} else if (change.type === 'remove' && change.index !== undefined) {
|
|
365
|
+
const removed = elements.splice(change.index, 1);
|
|
366
|
+
removed.forEach(el => {
|
|
367
|
+
el.destroy();
|
|
368
|
+
elementMap.delete(change.index!);
|
|
369
|
+
});
|
|
370
|
+
} else if (change.type === 'update' && change.index !== undefined && change.items.length === 1) {
|
|
371
|
+
const index = change.index;
|
|
372
|
+
const newItem = change.items[0];
|
|
373
|
+
|
|
374
|
+
// Check if the previous item at this index was effectively undefined or non-existent
|
|
375
|
+
if (index >= elements.length || elements[index] === undefined || !elementMap.has(index)) {
|
|
376
|
+
// Treat as add operation
|
|
377
|
+
const newElement = createElementFn(newItem as T, index);
|
|
378
|
+
if (newElement) {
|
|
379
|
+
elements.splice(index, 0, newElement); // Insert at the correct index
|
|
380
|
+
elementMap.set(index, newElement);
|
|
381
|
+
// Adjust indices in elementMap for subsequent elements might be needed if map relied on exact indices
|
|
382
|
+
// This simple implementation assumes keys are stable or createElementFn handles context correctly
|
|
383
|
+
} else {
|
|
384
|
+
console.warn(`Element creation returned null for index ${index} during add-like update.`);
|
|
385
|
+
}
|
|
386
|
+
} else {
|
|
387
|
+
// Treat as a standard update operation
|
|
388
|
+
const oldElement = elements[index];
|
|
389
|
+
oldElement.destroy();
|
|
390
|
+
const newElement = createElementFn(newItem as T, index);
|
|
391
|
+
if (newElement) {
|
|
392
|
+
elements[index] = newElement;
|
|
393
|
+
elementMap.set(index, newElement);
|
|
394
|
+
} else {
|
|
395
|
+
// Handle case where new element creation returns null
|
|
396
|
+
elements.splice(index, 1);
|
|
397
|
+
elementMap.delete(index);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
subscriber.next({
|
|
403
|
+
elements: [...elements] // Create a new array to ensure change detection
|
|
313
404
|
});
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
405
|
+
})
|
|
406
|
+
: (itemsSubject as WritableObjectSignal<T>).observable.subscribe(change => {
|
|
407
|
+
const key = change.key as string | number
|
|
408
|
+
if (isFirstSubscription) {
|
|
409
|
+
isFirstSubscription = false;
|
|
410
|
+
elements.forEach(el => el.destroy());
|
|
411
|
+
elements = [];
|
|
412
|
+
elementMap.clear();
|
|
413
|
+
|
|
414
|
+
const items = (itemsSubject as WritableObjectSignal<T>)();
|
|
415
|
+
if (items) {
|
|
416
|
+
Object.entries(items).forEach(([key, value]) => {
|
|
417
|
+
const element = createElementFn(value, key);
|
|
418
|
+
if (element) {
|
|
419
|
+
elements.push(element);
|
|
420
|
+
elementMap.set(key, element);
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
subscriber.next({
|
|
425
|
+
elements: [...elements]
|
|
426
|
+
});
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (change.type === 'init' || change.type === 'reset') {
|
|
431
|
+
elements.forEach(el => el.destroy());
|
|
432
|
+
elements = [];
|
|
433
|
+
elementMap.clear();
|
|
434
|
+
|
|
435
|
+
const items = (itemsSubject as WritableObjectSignal<T>)();
|
|
436
|
+
if (items) {
|
|
437
|
+
Object.entries(items).forEach(([key, value]) => {
|
|
438
|
+
const element = createElementFn(value, key);
|
|
439
|
+
if (element) {
|
|
440
|
+
elements.push(element);
|
|
441
|
+
elementMap.set(key, element);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
} else if (change.type === 'add' && change.key && change.value !== undefined) {
|
|
446
|
+
const element = createElementFn(change.value as T, key);
|
|
447
|
+
if (element) {
|
|
448
|
+
elements.push(element);
|
|
449
|
+
elementMap.set(key, element);
|
|
450
|
+
}
|
|
451
|
+
} else if (change.type === 'remove' && change.key) {
|
|
452
|
+
const index = elements.findIndex(el => elementMap.get(key) === el);
|
|
453
|
+
if (index !== -1) {
|
|
454
|
+
const [removed] = elements.splice(index, 1);
|
|
455
|
+
removed.destroy();
|
|
456
|
+
elementMap.delete(key);
|
|
457
|
+
}
|
|
458
|
+
} else if (change.type === 'update' && change.key && change.value !== undefined) {
|
|
459
|
+
const index = elements.findIndex(el => elementMap.get(key) === el);
|
|
460
|
+
if (index !== -1) {
|
|
461
|
+
const oldElement = elements[index];
|
|
462
|
+
oldElement.destroy();
|
|
463
|
+
const newElement = createElementFn(change.value as T, key);
|
|
464
|
+
if (newElement) {
|
|
465
|
+
elements[index] = newElement;
|
|
466
|
+
elementMap.set(key, newElement);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
subscriber.next({
|
|
472
|
+
elements: [...elements] // Create a new array to ensure change detection
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
return subscription;
|
|
477
|
+
});
|
|
343
478
|
});
|
|
344
479
|
}
|
|
345
480
|
|
|
481
|
+
/**
|
|
482
|
+
* Conditionally creates and destroys elements based on a condition signal.
|
|
483
|
+
*
|
|
484
|
+
* @param {Signal<boolean> | boolean} condition - A signal or boolean that determines whether to create an element.
|
|
485
|
+
* @param {Function} createElementFn - A function that returns an element or a promise that resolves to an element.
|
|
486
|
+
* @returns {Observable} An observable that emits the created or destroyed element.
|
|
487
|
+
*/
|
|
346
488
|
export function cond(
|
|
347
|
-
condition: Signal,
|
|
489
|
+
condition: Signal<boolean> | boolean,
|
|
348
490
|
createElementFn: () => Element | Promise<Element>
|
|
349
491
|
): FlowObservable {
|
|
350
492
|
let element: Element | null = null;
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
493
|
+
|
|
494
|
+
if (isSignal(condition)) {
|
|
495
|
+
const signalCondition = condition as WritableObjectSignal<boolean>;
|
|
496
|
+
return new Observable<{elements: Element[], type?: "init" | "remove"}>(subscriber => {
|
|
497
|
+
return signalCondition.observable.subscribe(bool => {
|
|
498
|
+
if (bool) {
|
|
499
|
+
let _el = createElementFn();
|
|
500
|
+
if (isPromise(_el)) {
|
|
501
|
+
from(_el as Promise<Element>).subscribe(el => {
|
|
502
|
+
element = el;
|
|
503
|
+
subscriber.next({
|
|
360
504
|
type: "init",
|
|
361
505
|
elements: [el],
|
|
362
|
-
};
|
|
363
|
-
})
|
|
364
|
-
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
} else {
|
|
509
|
+
element = _el as Element;
|
|
510
|
+
subscriber.next({
|
|
511
|
+
type: "init",
|
|
512
|
+
elements: [element],
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
} else if (element) {
|
|
516
|
+
destroyElement(element);
|
|
517
|
+
subscriber.next({
|
|
518
|
+
elements: [],
|
|
519
|
+
});
|
|
520
|
+
} else {
|
|
521
|
+
subscriber.next({
|
|
522
|
+
elements: [],
|
|
523
|
+
});
|
|
365
524
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
525
|
+
});
|
|
526
|
+
}).pipe(share())
|
|
527
|
+
} else {
|
|
528
|
+
// Handle boolean case
|
|
529
|
+
if (condition) {
|
|
530
|
+
let _el = createElementFn();
|
|
531
|
+
if (isPromise(_el)) {
|
|
532
|
+
return from(_el as Promise<Element>).pipe(
|
|
533
|
+
map((el) => ({
|
|
534
|
+
type: "init",
|
|
535
|
+
elements: [el],
|
|
536
|
+
}))
|
|
537
|
+
);
|
|
373
538
|
}
|
|
374
539
|
return of({
|
|
375
|
-
|
|
540
|
+
type: "init",
|
|
541
|
+
elements: [_el as Element],
|
|
376
542
|
});
|
|
377
|
-
}
|
|
378
|
-
|
|
543
|
+
}
|
|
544
|
+
return of({
|
|
545
|
+
elements: [],
|
|
546
|
+
});
|
|
547
|
+
}
|
|
379
548
|
}
|