@xaendar/core 0.6.7 → 0.6.10
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/xaendar-core.es.d.ts +64 -19
- package/dist/xaendar-core.es.js +172 -102
- package/package.json +3 -3
|
@@ -3,6 +3,7 @@ import { Beautify } from '@xaendar/types';
|
|
|
3
3
|
import { ClassDecorator as ClassDecorator_2 } from '@xaendar/types';
|
|
4
4
|
import { Constructor } from '@xaendar/types';
|
|
5
5
|
import { EffectOptions } from '@xaendar/signals';
|
|
6
|
+
import { Function as Function_2 } from '@xaendar/types';
|
|
6
7
|
import { NoArgsFunction } from '@xaendar/types';
|
|
7
8
|
import { NoArgsVoidFunction } from '@xaendar/types';
|
|
8
9
|
import { NoArgsVoidFunction as NoArgsVoidFunction_2 } from '@xaendar/types';
|
|
@@ -18,10 +19,11 @@ import { VoidFunction as VoidFunction_2 } from '@xaendar/types';
|
|
|
18
19
|
* It won't appear by intellisense but it's there.
|
|
19
20
|
*/
|
|
20
21
|
export declare class BaseWebComponent extends HTMLElement {
|
|
22
|
+
[key: string]: unknown;
|
|
21
23
|
/**
|
|
22
24
|
* Array of functions to unwatch all the signals used in the component
|
|
23
25
|
*/
|
|
24
|
-
protected
|
|
26
|
+
protected context: Context;
|
|
25
27
|
/**
|
|
26
28
|
* The root of the Web Component, where the content is rendered
|
|
27
29
|
*/
|
|
@@ -74,7 +76,7 @@ export declare type BaseWebComponentConstructor = Constructor<BaseWebComponent,
|
|
|
74
76
|
*/
|
|
75
77
|
declare type Block = {
|
|
76
78
|
condition?: NoArgsFunction<boolean>;
|
|
77
|
-
block:
|
|
79
|
+
block: Function_2<[HTMLElement, Context], Context>;
|
|
78
80
|
};
|
|
79
81
|
|
|
80
82
|
/**
|
|
@@ -107,6 +109,55 @@ export declare type Computed<Value = any> = Signal.Computed<Value> & {
|
|
|
107
109
|
*/
|
|
108
110
|
export declare function computed<Value = any>(value: Value, options?: SignalOptions<Value>): Computed<Value>;
|
|
109
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Tracks identifier scope during run time template function execution
|
|
114
|
+
* Each `Context` instance represents one lexical scope (e.g. a `@for` loop body)
|
|
115
|
+
* and can be chained to a parent context for outer-scope resolution.
|
|
116
|
+
*/
|
|
117
|
+
export declare class Context {
|
|
118
|
+
private _root;
|
|
119
|
+
private _parent;
|
|
120
|
+
private _identifiers;
|
|
121
|
+
/**
|
|
122
|
+
* Array of functions to be called when the current context is destroyed (e.g. a `@if` becomes false)
|
|
123
|
+
* These functions contains
|
|
124
|
+
* - Remove Element
|
|
125
|
+
* - Remove EventListener
|
|
126
|
+
* - Effect unsubscription Function
|
|
127
|
+
*/
|
|
128
|
+
private _unwatchFns;
|
|
129
|
+
private _children;
|
|
130
|
+
private _nodes;
|
|
131
|
+
/**
|
|
132
|
+
* Creates a new scope context.
|
|
133
|
+
*
|
|
134
|
+
* @param _root - Web Component Reference to access properties and methods directly for bindings
|
|
135
|
+
* @param _parent Optional parent context representing the enclosing scope.
|
|
136
|
+
* @param _identifiers List of loop variable names declared in this scope.
|
|
137
|
+
*/
|
|
138
|
+
constructor(_root: BaseWebComponent, _parent: Context, _identifiers?: Map<string, ContextIdentifier>);
|
|
139
|
+
addIdentifier(name: string, valueFn: NoArgsFunction<unknown>): void;
|
|
140
|
+
/**
|
|
141
|
+
* Returns the innermost identifier in the current scope chain, or
|
|
142
|
+
* delegates to the parent context if none is found in this scope.
|
|
143
|
+
*
|
|
144
|
+
* @returns The most recently declared identifier name, or `undefined` if none exists.
|
|
145
|
+
*/
|
|
146
|
+
getIdentifier<ReturnType = unknown>(name: string): ContextIdentifier<ReturnType> | undefined;
|
|
147
|
+
getEventHandler(handler: string): VoidFunction;
|
|
148
|
+
addChild(context: Context): void;
|
|
149
|
+
removeChild(context: Context): void;
|
|
150
|
+
addNode(node: Node): void;
|
|
151
|
+
removeNode(nodeToRemove: Node): void;
|
|
152
|
+
listen(...fns: NoArgsVoidFunction[]): void;
|
|
153
|
+
unlisten(): void;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
declare type ContextIdentifier<ReturnType = unknown> = {
|
|
157
|
+
get: NoArgsFunction<ReturnType>;
|
|
158
|
+
reactive: boolean;
|
|
159
|
+
};
|
|
160
|
+
|
|
110
161
|
/**
|
|
111
162
|
* Runs a side-effectful function and automatically re-runs it whenever any
|
|
112
163
|
* Signal read during its execution changes.
|
|
@@ -156,11 +207,12 @@ export declare type EventOptions = Beautify<RequireOne<Omit<CustomEventInit, 'de
|
|
|
156
207
|
* Reactively iterates over a list of items, re-running the loop whenever the tracked condition changes.
|
|
157
208
|
* Previous iteration side-effects are cleaned up before each re-evaluation.
|
|
158
209
|
*
|
|
159
|
-
* @param
|
|
210
|
+
* @param parentNode - The parent HTML element where the conditional structure is applied.
|
|
211
|
+
* @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
|
|
160
212
|
* @param condition - A reactive function that returns the array of items to iterate over.
|
|
161
|
-
* @param forFn - A callback invoked for each item, receiving the item and its index. Must return an array of cleanup functions.
|
|
213
|
+
* @param forFn - A callback invoked for each item, receiving the parent node, the item, and its index. Must return an array of cleanup functions.
|
|
162
214
|
*/
|
|
163
|
-
export declare function _for(
|
|
215
|
+
export declare function _for(parentNode: HTMLElement, parentContext: Context, condition: () => unknown[], forFn: Function_2<[parentNode: HTMLElement, context: Context, item: unknown, index: number], Context>): void;
|
|
164
216
|
|
|
165
217
|
/**
|
|
166
218
|
* Creates a reactive conditional structure from a list of branches.
|
|
@@ -176,12 +228,14 @@ export declare function _for(unwatchFns: NoArgsVoidFunction[], condition: () =>
|
|
|
176
228
|
* state (which branch is active) and the related cleanup functions are only updated if
|
|
177
229
|
* the active branch has actually changed.
|
|
178
230
|
*
|
|
231
|
+
* @param parentNode - The parent HTML element where the conditional structure is applied.
|
|
232
|
+
* @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
|
|
179
233
|
* @param unwatchFns - Shared array that collects all active cleanup functions.
|
|
180
234
|
* Mutated in place: the created effect and branch functions are added to and
|
|
181
235
|
* removed from this array.
|
|
182
236
|
* @param blocks - Ordered list of conditional branches to evaluate.
|
|
183
237
|
*/
|
|
184
|
-
export declare function _if(
|
|
238
|
+
export declare function _if(parentNode: HTMLElement, parentContext: Context, blocks: Block[]): void;
|
|
185
239
|
|
|
186
240
|
/**
|
|
187
241
|
* An `InputSignal` is a specialized `Signal.State` designed for use as a property signal in web components.
|
|
@@ -363,6 +417,8 @@ export { Signal_2 as Signal }
|
|
|
363
417
|
* Because it delegates to `_if`, the switch benefits from the same optimisation:
|
|
364
418
|
* the active branch is only re-rendered when it actually changes.
|
|
365
419
|
*
|
|
420
|
+
* @param parentNode - The parent HTML element where the conditional structure is applied.
|
|
421
|
+
* @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
|
|
366
422
|
* @param unwatchFns - Shared array that collects all active cleanup functions.
|
|
367
423
|
* Mutated in place by the underlying {@link _if} call.
|
|
368
424
|
* @param expression - Function that evaluates the switch expression. Called
|
|
@@ -373,22 +429,11 @@ export { Signal_2 as Signal }
|
|
|
373
429
|
* - `block`: function that applies the branch's side effects and returns
|
|
374
430
|
* its cleanup functions.
|
|
375
431
|
*/
|
|
376
|
-
export declare function _switch(
|
|
432
|
+
export declare function _switch(parentNode: HTMLElement, parentContext: Context, expression: NoArgsFunction<unknown>, blocks: Array<{
|
|
377
433
|
condition: unknown[] | null;
|
|
378
|
-
block:
|
|
434
|
+
block: Function_2<[HTMLElement, Context], Context>;
|
|
379
435
|
}>): void;
|
|
380
436
|
|
|
381
|
-
/**
|
|
382
|
-
* Executes and removes the cleanup functions of the previously active branch.
|
|
383
|
-
*
|
|
384
|
-
* For each local function: removes it from the shared `unwatchFns` array (if present)
|
|
385
|
-
* and then invokes it to release resources / tear down associated subscriptions.
|
|
386
|
-
*
|
|
387
|
-
* @param unwatchFns - Shared array of active cleanup functions, mutated in place.
|
|
388
|
-
* @param localUnwatchFns - Cleanup functions of the branch to deactivate.
|
|
389
|
-
*/
|
|
390
|
-
export declare function unwatch(unwatchFns: NoArgsVoidFunction[], localUnwatchFns: NoArgsVoidFunction[]): void;
|
|
391
|
-
|
|
392
437
|
/**
|
|
393
438
|
* Decorator to define a web component
|
|
394
439
|
* @param selector Name or names of the custom element
|
package/dist/xaendar-core.es.js
CHANGED
|
@@ -215,7 +215,7 @@ var BaseWebComponent = class extends HTMLElement {
|
|
|
215
215
|
/**
|
|
216
216
|
* Array of functions to unwatch all the signals used in the component
|
|
217
217
|
*/
|
|
218
|
-
|
|
218
|
+
context;
|
|
219
219
|
/**
|
|
220
220
|
* The root of the Web Component, where the content is rendered
|
|
221
221
|
*/
|
|
@@ -269,8 +269,7 @@ var BaseWebComponent = class extends HTMLElement {
|
|
|
269
269
|
* the properties initialization won't call the render method
|
|
270
270
|
*/
|
|
271
271
|
disconnectedCallback() {
|
|
272
|
-
this.
|
|
273
|
-
this.unwatchFns = [];
|
|
272
|
+
this.context.unlisten();
|
|
274
273
|
}
|
|
275
274
|
};
|
|
276
275
|
//#endregion
|
|
@@ -401,6 +400,136 @@ function signal(value, options) {
|
|
|
401
400
|
return getter;
|
|
402
401
|
}
|
|
403
402
|
//#endregion
|
|
403
|
+
//#region ../packages/core/src/utils/context.utils.ts
|
|
404
|
+
/**
|
|
405
|
+
* Tracks identifier scope during run time template function execution
|
|
406
|
+
* Each `Context` instance represents one lexical scope (e.g. a `@for` loop body)
|
|
407
|
+
* and can be chained to a parent context for outer-scope resolution.
|
|
408
|
+
*/
|
|
409
|
+
var Context = class {
|
|
410
|
+
_root;
|
|
411
|
+
_parent;
|
|
412
|
+
_identifiers;
|
|
413
|
+
/**
|
|
414
|
+
* Array of functions to be called when the current context is destroyed (e.g. a `@if` becomes false)
|
|
415
|
+
* These functions contains
|
|
416
|
+
* - Remove Element
|
|
417
|
+
* - Remove EventListener
|
|
418
|
+
* - Effect unsubscription Function
|
|
419
|
+
*/
|
|
420
|
+
_unwatchFns = new Array();
|
|
421
|
+
_children = new Array();
|
|
422
|
+
_nodes = new Array();
|
|
423
|
+
/**
|
|
424
|
+
* Creates a new scope context.
|
|
425
|
+
*
|
|
426
|
+
* @param _root - Web Component Reference to access properties and methods directly for bindings
|
|
427
|
+
* @param _parent Optional parent context representing the enclosing scope.
|
|
428
|
+
* @param _identifiers List of loop variable names declared in this scope.
|
|
429
|
+
*/
|
|
430
|
+
constructor(_root, _parent, _identifiers = /* @__PURE__ */ new Map()) {
|
|
431
|
+
this._root = _root;
|
|
432
|
+
this._parent = _parent;
|
|
433
|
+
this._identifiers = _identifiers;
|
|
434
|
+
}
|
|
435
|
+
addIdentifier(name, valueFn) {
|
|
436
|
+
if (this.getIdentifier(name)) throw new Error(`Identifier "${name}" is already declared in this scope.`);
|
|
437
|
+
this._identifiers.set(name, {
|
|
438
|
+
get: valueFn,
|
|
439
|
+
reactive: true
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Returns the innermost identifier in the current scope chain, or
|
|
444
|
+
* delegates to the parent context if none is found in this scope.
|
|
445
|
+
*
|
|
446
|
+
* @returns The most recently declared identifier name, or `undefined` if none exists.
|
|
447
|
+
*/
|
|
448
|
+
getIdentifier(name) {
|
|
449
|
+
if (this._identifiers.has(name)) return this._identifiers.get(name);
|
|
450
|
+
if (this._parent) return this._parent.getIdentifier(name);
|
|
451
|
+
return {
|
|
452
|
+
get: () => this._root[name],
|
|
453
|
+
reactive: true
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
getEventHandler(handler) {
|
|
457
|
+
return this._root[handler];
|
|
458
|
+
}
|
|
459
|
+
addChild(context) {
|
|
460
|
+
this._children.push(context);
|
|
461
|
+
}
|
|
462
|
+
removeChild(context) {
|
|
463
|
+
this._children = this._children.filter((child) => child === context);
|
|
464
|
+
}
|
|
465
|
+
addNode(node) {
|
|
466
|
+
this._nodes.push(node);
|
|
467
|
+
}
|
|
468
|
+
removeNode(nodeToRemove) {
|
|
469
|
+
this._nodes = this._nodes.filter((node) => node === nodeToRemove);
|
|
470
|
+
}
|
|
471
|
+
listen(...fns) {
|
|
472
|
+
this._unwatchFns.push(...fns);
|
|
473
|
+
}
|
|
474
|
+
unlisten() {
|
|
475
|
+
this._unwatchFns.forEach((fn) => fn());
|
|
476
|
+
this._children.forEach((child) => child.unlisten());
|
|
477
|
+
this._unwatchFns = [];
|
|
478
|
+
this._nodes = [];
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
//#endregion
|
|
482
|
+
//#region ../packages/core/src/utils/for.utils.ts
|
|
483
|
+
/**
|
|
484
|
+
* Reactively iterates over a list of items, re-running the loop whenever the tracked condition changes.
|
|
485
|
+
* Previous iteration side-effects are cleaned up before each re-evaluation.
|
|
486
|
+
*
|
|
487
|
+
* @param parentNode - The parent HTML element where the conditional structure is applied.
|
|
488
|
+
* @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
|
|
489
|
+
* @param condition - A reactive function that returns the array of items to iterate over.
|
|
490
|
+
* @param forFn - A callback invoked for each item, receiving the parent node, the item, and its index. Must return an array of cleanup functions.
|
|
491
|
+
*/
|
|
492
|
+
function _for(parentNode, parentContext, condition, forFn) {
|
|
493
|
+
let contexts = new Array();
|
|
494
|
+
const unlistener = effect(() => {
|
|
495
|
+
contexts.forEach((context) => {
|
|
496
|
+
context.unlisten();
|
|
497
|
+
parentContext.removeChild(context);
|
|
498
|
+
});
|
|
499
|
+
contexts = [];
|
|
500
|
+
const items = condition();
|
|
501
|
+
Signal.subtle.untrack(() => {
|
|
502
|
+
for (let i = 0; i < items.length; i++) {
|
|
503
|
+
const context = forFn(parentNode, parentContext, items[i], i);
|
|
504
|
+
contexts.push(context);
|
|
505
|
+
parentContext.addChild(context);
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
parentContext.listen(unlistener);
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Builds a record of iteration context variables for a given index in the loop.
|
|
513
|
+
* Provides the current item, index, and convenience flags (`$first`, `$last`, `$even`, `$odd`).
|
|
514
|
+
*
|
|
515
|
+
* @param items - The full array being iterated.
|
|
516
|
+
* @param index - The current iteration index.
|
|
517
|
+
* @param itemAlias - The template alias name for the current item.
|
|
518
|
+
* @param indexAlias - The template alias name for the current index.
|
|
519
|
+
* @returns A record mapping alias names and built-in variables to their values.
|
|
520
|
+
*/
|
|
521
|
+
function _iterationVariables(items, index, itemAlias, indexAlias) {
|
|
522
|
+
const even = index % 2 === 0;
|
|
523
|
+
return {
|
|
524
|
+
[itemAlias]: items[index],
|
|
525
|
+
[indexAlias]: index,
|
|
526
|
+
$first: index === 0,
|
|
527
|
+
$last: index === items.length - 1,
|
|
528
|
+
$even: even,
|
|
529
|
+
$odd: !even
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
//#endregion
|
|
404
533
|
//#region ../packages/core/src/utils/if.utils.ts
|
|
405
534
|
/**
|
|
406
535
|
* Creates a reactive conditional structure from a list of branches.
|
|
@@ -416,32 +545,34 @@ function signal(value, options) {
|
|
|
416
545
|
* state (which branch is active) and the related cleanup functions are only updated if
|
|
417
546
|
* the active branch has actually changed.
|
|
418
547
|
*
|
|
548
|
+
* @param parentNode - The parent HTML element where the conditional structure is applied.
|
|
549
|
+
* @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
|
|
419
550
|
* @param unwatchFns - Shared array that collects all active cleanup functions.
|
|
420
551
|
* Mutated in place: the created effect and branch functions are added to and
|
|
421
552
|
* removed from this array.
|
|
422
553
|
* @param blocks - Ordered list of conditional branches to evaluate.
|
|
423
554
|
*/
|
|
424
|
-
function _if(
|
|
425
|
-
let state
|
|
426
|
-
let localUnwatchFns = new Array();
|
|
555
|
+
function _if(parentNode, parentContext, blocks) {
|
|
556
|
+
let state;
|
|
427
557
|
let fn;
|
|
428
558
|
switch (blocks.length) {
|
|
429
559
|
case 1:
|
|
430
|
-
fn = () => handleIf(blocks[0], state
|
|
560
|
+
fn = (state) => handleIf(parentNode, parentContext, blocks[0], state);
|
|
431
561
|
break;
|
|
432
562
|
case 2:
|
|
433
|
-
fn = () => handleIfElse(blocks[0], blocks[1], state
|
|
563
|
+
fn = (state) => handleIfElse(parentNode, parentContext, blocks[0], blocks[1], state);
|
|
434
564
|
break;
|
|
435
|
-
default: fn = () => handleIfElseIf(
|
|
565
|
+
default: fn = (state) => handleIfElseIf(parentNode, parentContext, blocks, state);
|
|
436
566
|
}
|
|
437
567
|
const unlistener = effect(() => {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
state
|
|
441
|
-
localUnwatchFns = retVal.fns;
|
|
568
|
+
if (state) {
|
|
569
|
+
state.context.unlisten();
|
|
570
|
+
parentContext.removeChild(state.context);
|
|
442
571
|
}
|
|
572
|
+
state = fn(state);
|
|
573
|
+
if (state) parentContext.addChild(state.context);
|
|
443
574
|
});
|
|
444
|
-
|
|
575
|
+
parentContext.listen(unlistener);
|
|
445
576
|
}
|
|
446
577
|
/**
|
|
447
578
|
* Handles the simple `if` case (a single branch, no `else`).
|
|
@@ -450,32 +581,33 @@ function _if(unwatchFns, blocks) {
|
|
|
450
581
|
* previously active branch is deactivated by resetting the state to `null` and
|
|
451
582
|
* executing an empty block.
|
|
452
583
|
*
|
|
584
|
+
* @param parentNode - The parent HTML element where the conditional structure is applied.
|
|
585
|
+
* @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
|
|
453
586
|
* @param ifBlock - The `if` branch to evaluate.
|
|
454
587
|
* @param state - The current state (index of the active branch, or `null` if none).
|
|
455
|
-
* @param unwatchFns - Shared array of active cleanup functions.
|
|
456
|
-
* @param localUnwatchFns - Cleanup functions generated by the last branch activation.
|
|
457
588
|
* @returns The new state and new cleanup functions if the active branch changed,
|
|
458
589
|
* otherwise `undefined`.
|
|
459
590
|
*/
|
|
460
|
-
function handleIf(
|
|
461
|
-
|
|
591
|
+
function handleIf(parentNode, parentContext, ifBlock, state) {
|
|
592
|
+
if (ifBlock.condition()) return checkAndUpdateState(parentNode, parentContext, state, 0, ifBlock.block);
|
|
593
|
+
else parentContext.unlisten();
|
|
462
594
|
}
|
|
463
595
|
/**
|
|
464
596
|
* Handles the `if` / `else` case (exactly two branches).
|
|
465
597
|
*
|
|
466
598
|
* If the `if` condition is true, the first branch is activated (state `0`); otherwise,
|
|
467
599
|
* the `else` branch is activated (state `1`).
|
|
468
|
-
*
|
|
600
|
+
*
|
|
601
|
+
* @param parentNode - The parent HTML element where the conditional structure is applied.
|
|
602
|
+
* @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
|
|
469
603
|
* @param ifBlock - The `if` branch to evaluate.
|
|
470
604
|
* @param elseBlock - The `else` branch used when the `if` condition is false.
|
|
471
605
|
* @param state - The current state (index of the active branch, or `null` if none).
|
|
472
|
-
* @param unwatchFns - Shared array of active cleanup functions.
|
|
473
|
-
* @param localUnwatchFns - Cleanup functions generated by the last branch activation.
|
|
474
606
|
* @returns The new state and new cleanup functions if the active branch changed,
|
|
475
607
|
* otherwise `undefined`.
|
|
476
608
|
*/
|
|
477
|
-
function handleIfElse(
|
|
478
|
-
return ifBlock.condition() ? checkAndUpdateState(state, 0, ifBlock.block
|
|
609
|
+
function handleIfElse(parentNode, parentContext, ifBlock, elseBlock, state) {
|
|
610
|
+
return ifBlock.condition() ? checkAndUpdateState(parentNode, parentContext, state, 0, ifBlock.block) : checkAndUpdateState(parentNode, parentContext, state, 1, elseBlock.block);
|
|
479
611
|
}
|
|
480
612
|
/**
|
|
481
613
|
* Handles the general case of an `if` / `else if` / ... / `else` chain (three or more branches).
|
|
@@ -483,18 +615,18 @@ function handleIfElse(ifBlock, elseBlock, state, unwatchFns, localUnwatchFns) {
|
|
|
483
615
|
* Iterates through the branches in order and activates the first one whose condition is
|
|
484
616
|
* true; a branch without a condition is always considered valid and acts as the final
|
|
485
617
|
* `else`. The state is set to the index of the activated branch.
|
|
486
|
-
*
|
|
618
|
+
*
|
|
619
|
+
* @param parentNode - The parent HTML element where the conditional structure is applied.
|
|
620
|
+
* @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
|
|
487
621
|
* @param blocks - Ordered list of conditional branches to evaluate.
|
|
488
622
|
* @param state - The current state (index of the active branch, or `null` if none).
|
|
489
|
-
* @param unwatchFns - Shared array of active cleanup functions.
|
|
490
|
-
* @param localUnwatchFns - Cleanup functions generated by the last branch activation.
|
|
491
623
|
* @returns The new state and new cleanup functions if the active branch changed,
|
|
492
624
|
* otherwise `undefined`. Also returns `undefined` if no branch matches.
|
|
493
625
|
*/
|
|
494
|
-
function handleIfElseIf(
|
|
626
|
+
function handleIfElseIf(parentNode, parentContext, blocks, state) {
|
|
495
627
|
for (let i = 0; i < blocks.length; i++) {
|
|
496
628
|
const { condition, block } = blocks[i];
|
|
497
|
-
if (!condition || condition()) return checkAndUpdateState(
|
|
629
|
+
if (!condition || condition()) return checkAndUpdateState(parentNode, parentContext, state, i, block);
|
|
498
630
|
}
|
|
499
631
|
}
|
|
500
632
|
/**
|
|
@@ -508,88 +640,24 @@ function handleIfElseIf(blocks, state, unwatchFns, localUnwatchFns) {
|
|
|
508
640
|
*
|
|
509
641
|
* If the branch has not changed, no operation is performed.
|
|
510
642
|
*
|
|
643
|
+
* @param parentNode - The parent HTML element where the conditional structure is applied.
|
|
644
|
+
* @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
|
|
511
645
|
* @param state - The current state (index of the active branch, or `null` if none).
|
|
512
646
|
* @param newState - The new state to set (index of the branch to activate, or `null`).
|
|
513
647
|
* @param conditionalBlockFn - Branch function to execute, which returns its own
|
|
514
648
|
* cleanup functions.
|
|
515
|
-
* @param unwatchFns - Shared array of active cleanup functions.
|
|
516
|
-
* @param localUnwatchFns - Cleanup functions of the currently active branch, to be
|
|
517
|
-
* removed before activating the new branch.
|
|
518
649
|
* @returns Il nuovo stato e le nuove funzioni di cleanup se il ramo è cambiato, altrimenti
|
|
519
650
|
* `undefined`.
|
|
520
651
|
*/
|
|
521
|
-
function checkAndUpdateState(
|
|
522
|
-
if (state !== newState) {
|
|
523
|
-
|
|
524
|
-
localUnwatchFns = Signal.subtle.untrack(conditionalBlockFn);
|
|
525
|
-
unwatchFns.push(...localUnwatchFns);
|
|
652
|
+
function checkAndUpdateState(parentNode, parentContext, state, newState, conditionalBlockFn) {
|
|
653
|
+
if (state?.activeBranch !== newState) {
|
|
654
|
+
state?.context.unlisten();
|
|
526
655
|
return {
|
|
527
|
-
|
|
528
|
-
|
|
656
|
+
activeBranch: newState,
|
|
657
|
+
context: Signal.subtle.untrack(() => conditionalBlockFn(parentNode, parentContext))
|
|
529
658
|
};
|
|
530
659
|
}
|
|
531
660
|
}
|
|
532
|
-
/**
|
|
533
|
-
* Executes and removes the cleanup functions of the previously active branch.
|
|
534
|
-
*
|
|
535
|
-
* For each local function: removes it from the shared `unwatchFns` array (if present)
|
|
536
|
-
* and then invokes it to release resources / tear down associated subscriptions.
|
|
537
|
-
*
|
|
538
|
-
* @param unwatchFns - Shared array of active cleanup functions, mutated in place.
|
|
539
|
-
* @param localUnwatchFns - Cleanup functions of the branch to deactivate.
|
|
540
|
-
*/
|
|
541
|
-
function unwatch(unwatchFns, localUnwatchFns) {
|
|
542
|
-
for (const fn of localUnwatchFns) {
|
|
543
|
-
const index = unwatchFns.indexOf(fn);
|
|
544
|
-
if (index !== -1) unwatchFns.splice(index, 1);
|
|
545
|
-
fn();
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
//#endregion
|
|
549
|
-
//#region ../packages/core/src/utils/for.utils.ts
|
|
550
|
-
/**
|
|
551
|
-
* Reactively iterates over a list of items, re-running the loop whenever the tracked condition changes.
|
|
552
|
-
* Previous iteration side-effects are cleaned up before each re-evaluation.
|
|
553
|
-
*
|
|
554
|
-
* @param unwatchFns - Array collecting cleanup functions for the parent scope.
|
|
555
|
-
* @param condition - A reactive function that returns the array of items to iterate over.
|
|
556
|
-
* @param forFn - A callback invoked for each item, receiving the item and its index. Must return an array of cleanup functions.
|
|
557
|
-
*/
|
|
558
|
-
function _for(unwatchFns, condition, forFn) {
|
|
559
|
-
const localUnwatchFns = new Array();
|
|
560
|
-
const unlistener = effect(() => {
|
|
561
|
-
unwatch(unwatchFns, localUnwatchFns);
|
|
562
|
-
const items = condition();
|
|
563
|
-
Signal.subtle.untrack(() => {
|
|
564
|
-
for (let i = 0; i < items.length; i++) {
|
|
565
|
-
localUnwatchFns.push(...forFn(items[i], i));
|
|
566
|
-
unwatchFns.push(...localUnwatchFns);
|
|
567
|
-
}
|
|
568
|
-
});
|
|
569
|
-
});
|
|
570
|
-
unwatchFns.push(unlistener);
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* Builds a record of iteration context variables for a given index in the loop.
|
|
574
|
-
* Provides the current item, index, and convenience flags (`$first`, `$last`, `$even`, `$odd`).
|
|
575
|
-
*
|
|
576
|
-
* @param items - The full array being iterated.
|
|
577
|
-
* @param index - The current iteration index.
|
|
578
|
-
* @param itemAlias - The template alias name for the current item.
|
|
579
|
-
* @param indexAlias - The template alias name for the current index.
|
|
580
|
-
* @returns A record mapping alias names and built-in variables to their values.
|
|
581
|
-
*/
|
|
582
|
-
function _iterationVariables(items, index, itemAlias, indexAlias) {
|
|
583
|
-
const even = index % 2 === 0;
|
|
584
|
-
return {
|
|
585
|
-
[itemAlias]: items[index],
|
|
586
|
-
[indexAlias]: index,
|
|
587
|
-
$first: index === 0,
|
|
588
|
-
$last: index === items.length - 1,
|
|
589
|
-
$even: even,
|
|
590
|
-
$odd: !even
|
|
591
|
-
};
|
|
592
|
-
}
|
|
593
661
|
//#endregion
|
|
594
662
|
//#region ../packages/core/src/utils/switch.utils.ts
|
|
595
663
|
/**
|
|
@@ -605,6 +673,8 @@ function _iterationVariables(items, index, itemAlias, indexAlias) {
|
|
|
605
673
|
* Because it delegates to `_if`, the switch benefits from the same optimisation:
|
|
606
674
|
* the active branch is only re-rendered when it actually changes.
|
|
607
675
|
*
|
|
676
|
+
* @param parentNode - The parent HTML element where the conditional structure is applied.
|
|
677
|
+
* @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
|
|
608
678
|
* @param unwatchFns - Shared array that collects all active cleanup functions.
|
|
609
679
|
* Mutated in place by the underlying {@link _if} call.
|
|
610
680
|
* @param expression - Function that evaluates the switch expression. Called
|
|
@@ -615,11 +685,11 @@ function _iterationVariables(items, index, itemAlias, indexAlias) {
|
|
|
615
685
|
* - `block`: function that applies the branch's side effects and returns
|
|
616
686
|
* its cleanup functions.
|
|
617
687
|
*/
|
|
618
|
-
function _switch(
|
|
619
|
-
_if(
|
|
688
|
+
function _switch(parentNode, parentContext, expression, blocks) {
|
|
689
|
+
_if(parentNode, parentContext, blocks.map(({ condition, block }) => ({
|
|
620
690
|
condition: condition ? () => condition.some((condition) => condition === expression()) : void 0,
|
|
621
691
|
block
|
|
622
692
|
})));
|
|
623
693
|
}
|
|
624
694
|
//#endregion
|
|
625
|
-
export { BaseWebComponent, Event, Property, WebComponent, _for, _if, _iterationVariables, _switch, computed, effect, input, signal
|
|
695
|
+
export { BaseWebComponent, Context, Event, Property, WebComponent, _for, _if, _iterationVariables, _switch, computed, effect, input, signal };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xaendar/core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.10",
|
|
4
4
|
"description": "A library containing core utils such as webcomponent base classes and theming support",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@xaendar/signals": "0.6.
|
|
20
|
-
"@xaendar/types": "0.6.
|
|
19
|
+
"@xaendar/signals": "0.6.10",
|
|
20
|
+
"@xaendar/types": "0.6.10"
|
|
21
21
|
}
|
|
22
22
|
}
|