@xaendar/core 0.6.9 → 0.6.11

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.
@@ -19,10 +19,11 @@ import { VoidFunction as VoidFunction_2 } from '@xaendar/types';
19
19
  * It won't appear by intellisense but it's there.
20
20
  */
21
21
  export declare class BaseWebComponent extends HTMLElement {
22
+ [key: string]: unknown;
22
23
  /**
23
24
  * Array of functions to unwatch all the signals used in the component
24
25
  */
25
- protected unwatchFns: NoArgsVoidFunction[];
26
+ protected context: Context;
26
27
  /**
27
28
  * The root of the Web Component, where the content is rendered
28
29
  */
@@ -75,7 +76,7 @@ export declare type BaseWebComponentConstructor = Constructor<BaseWebComponent,
75
76
  */
76
77
  declare type Block = {
77
78
  condition?: NoArgsFunction<boolean>;
78
- block: Function_2<[HTMLElement], NoArgsVoidFunction[]>;
79
+ block: Function_2<[HTMLElement, Context], Context>;
79
80
  };
80
81
 
81
82
  /**
@@ -108,6 +109,55 @@ export declare type Computed<Value = any> = Signal.Computed<Value> & {
108
109
  */
109
110
  export declare function computed<Value = any>(value: Value, options?: SignalOptions<Value>): Computed<Value>;
110
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
+
111
161
  /**
112
162
  * Runs a side-effectful function and automatically re-runs it whenever any
113
163
  * Signal read during its execution changes.
@@ -158,11 +208,11 @@ export declare type EventOptions = Beautify<RequireOne<Omit<CustomEventInit, 'de
158
208
  * Previous iteration side-effects are cleaned up before each re-evaluation.
159
209
  *
160
210
  * @param parentNode - The parent HTML element where the conditional structure is applied.
161
- * @param unwatchFns - Array collecting cleanup functions for the parent scope.
211
+ * @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
162
212
  * @param condition - A reactive function that returns the array of items to iterate over.
163
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.
164
214
  */
165
- export declare function _for(parentNode: HTMLElement, unwatchFns: NoArgsVoidFunction[], condition: () => unknown[], forFn: (parentNode: HTMLElement, item: unknown, index: number) => NoArgsVoidFunction[]): void;
215
+ export declare function _for(parentNode: HTMLElement, parentContext: Context, condition: () => unknown[], forFn: Function_2<[parentNode: HTMLElement, context: Context, item: unknown, index: number], Context>): void;
166
216
 
167
217
  /**
168
218
  * Creates a reactive conditional structure from a list of branches.
@@ -179,12 +229,13 @@ export declare function _for(parentNode: HTMLElement, unwatchFns: NoArgsVoidFunc
179
229
  * the active branch has actually changed.
180
230
  *
181
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
182
233
  * @param unwatchFns - Shared array that collects all active cleanup functions.
183
234
  * Mutated in place: the created effect and branch functions are added to and
184
235
  * removed from this array.
185
236
  * @param blocks - Ordered list of conditional branches to evaluate.
186
237
  */
187
- export declare function _if(parentNode: HTMLElement, unwatchFns: NoArgsVoidFunction[], blocks: Block[]): void;
238
+ export declare function _if(parentNode: HTMLElement, parentContext: Context, blocks: Block[]): void;
188
239
 
189
240
  /**
190
241
  * An `InputSignal` is a specialized `Signal.State` designed for use as a property signal in web components.
@@ -315,6 +366,37 @@ declare type PropertyDecoratorOptionsWithRequired<ActualValue = unknown, Incomin
315
366
  required: true;
316
367
  };
317
368
 
369
+ export declare function _renderElement(parentNode: HTMLElement, context: Context, tagName: string, attributes: RenderElementAttribute[], events: RenderElementEvent[]): void;
370
+
371
+ declare type RenderElementAttribute = {
372
+ name: string;
373
+ value: string;
374
+ literal: boolean;
375
+ };
376
+
377
+ declare type RenderElementEvent = {
378
+ name: string;
379
+ handler: string;
380
+ };
381
+
382
+ /**
383
+ *
384
+ * @param parentNode
385
+ * @param text
386
+ * @param context
387
+ * @returns
388
+ */
389
+ export declare function _renderLiteralText(parentNode: HTMLElement, context: Context, text: string): void;
390
+
391
+ /**
392
+ *
393
+ * @param parentNode
394
+ * @param text
395
+ * @param context
396
+ * @returns
397
+ */
398
+ export declare function _renderText(parentNode: HTMLElement, context: Context, text: string): void;
399
+
318
400
  /**
319
401
  * Creates a writable reactive signal.
320
402
  *
@@ -367,6 +449,7 @@ export { Signal_2 as Signal }
367
449
  * the active branch is only re-rendered when it actually changes.
368
450
  *
369
451
  * @param parentNode - The parent HTML element where the conditional structure is applied.
452
+ * @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
370
453
  * @param unwatchFns - Shared array that collects all active cleanup functions.
371
454
  * Mutated in place by the underlying {@link _if} call.
372
455
  * @param expression - Function that evaluates the switch expression. Called
@@ -377,22 +460,11 @@ export { Signal_2 as Signal }
377
460
  * - `block`: function that applies the branch's side effects and returns
378
461
  * its cleanup functions.
379
462
  */
380
- export declare function _switch(parentNode: HTMLElement, unwatchFns: NoArgsVoidFunction[], expression: NoArgsFunction<unknown>, blocks: Array<{
463
+ export declare function _switch(parentNode: HTMLElement, parentContext: Context, expression: NoArgsFunction<unknown>, blocks: Array<{
381
464
  condition: unknown[] | null;
382
- block: NoArgsFunction<NoArgsVoidFunction[]>;
465
+ block: Function_2<[HTMLElement, Context], Context>;
383
466
  }>): void;
384
467
 
385
- /**
386
- * Executes and removes the cleanup functions of the previously active branch.
387
- *
388
- * For each local function: removes it from the shared `unwatchFns` array (if present)
389
- * and then invokes it to release resources / tear down associated subscriptions.
390
- *
391
- * @param unwatchFns - Shared array of active cleanup functions, mutated in place.
392
- * @param localUnwatchFns - Cleanup functions of the branch to deactivate.
393
- */
394
- export declare function unwatch(unwatchFns: NoArgsVoidFunction[], localUnwatchFns: NoArgsVoidFunction[]): void;
395
-
396
468
  /**
397
469
  * Decorator to define a web component
398
470
  * @param selector Name or names of the custom element
@@ -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
- unwatchFns = new Array();
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.unwatchFns.forEach((unwatch) => unwatch());
273
- this.unwatchFns = [];
272
+ this.context.unlisten();
274
273
  }
275
274
  };
276
275
  //#endregion
@@ -401,7 +400,137 @@ function signal(value, options) {
401
400
  return getter;
402
401
  }
403
402
  //#endregion
404
- //#region ../packages/core/src/utils/if.utils.ts
403
+ //#region ../packages/core/src/utils/context.util.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.util.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
533
+ //#region ../packages/core/src/utils/if.util.ts
405
534
  /**
406
535
  * Creates a reactive conditional structure from a list of branches.
407
536
  *
@@ -417,32 +546,33 @@ function signal(value, options) {
417
546
  * the active branch has actually changed.
418
547
  *
419
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
420
550
  * @param unwatchFns - Shared array that collects all active cleanup functions.
421
551
  * Mutated in place: the created effect and branch functions are added to and
422
552
  * removed from this array.
423
553
  * @param blocks - Ordered list of conditional branches to evaluate.
424
554
  */
425
- function _if(parentNode, unwatchFns, blocks) {
426
- let state = null;
427
- let localUnwatchFns = new Array();
555
+ function _if(parentNode, parentContext, blocks) {
556
+ let state;
428
557
  let fn;
429
558
  switch (blocks.length) {
430
559
  case 1:
431
- fn = () => handleIf(parentNode, blocks[0], state, unwatchFns, localUnwatchFns);
560
+ fn = (state) => handleIf(parentNode, parentContext, blocks[0], state);
432
561
  break;
433
562
  case 2:
434
- fn = () => handleIfElse(parentNode, blocks[0], blocks[1], state, unwatchFns, localUnwatchFns);
563
+ fn = (state) => handleIfElse(parentNode, parentContext, blocks[0], blocks[1], state);
435
564
  break;
436
- default: fn = () => handleIfElseIf(parentNode, blocks, state, unwatchFns, localUnwatchFns);
565
+ default: fn = (state) => handleIfElseIf(parentNode, parentContext, blocks, state);
437
566
  }
438
567
  const unlistener = effect(() => {
439
- const retVal = fn();
440
- if (retVal) {
441
- state = retVal.state;
442
- localUnwatchFns = retVal.fns;
568
+ if (state) {
569
+ state.context.unlisten();
570
+ parentContext.removeChild(state.context);
443
571
  }
572
+ state = fn(state);
573
+ if (state) parentContext.addChild(state.context);
444
574
  });
445
- unwatchFns.push(unlistener);
575
+ parentContext.listen(unlistener);
446
576
  }
447
577
  /**
448
578
  * Handles the simple `if` case (a single branch, no `else`).
@@ -451,32 +581,33 @@ function _if(parentNode, unwatchFns, blocks) {
451
581
  * previously active branch is deactivated by resetting the state to `null` and
452
582
  * executing an empty block.
453
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
454
586
  * @param ifBlock - The `if` branch to evaluate.
455
587
  * @param state - The current state (index of the active branch, or `null` if none).
456
- * @param unwatchFns - Shared array of active cleanup functions.
457
- * @param localUnwatchFns - Cleanup functions generated by the last branch activation.
458
588
  * @returns The new state and new cleanup functions if the active branch changed,
459
589
  * otherwise `undefined`.
460
590
  */
461
- function handleIf(parentNode, ifBlock, state, unwatchFns, localUnwatchFns) {
462
- return ifBlock.condition() ? checkAndUpdateState(parentNode, state, 0, ifBlock.block, unwatchFns, localUnwatchFns) : checkAndUpdateState(parentNode, state, null, () => [], unwatchFns, localUnwatchFns);
591
+ function handleIf(parentNode, parentContext, ifBlock, state) {
592
+ if (ifBlock.condition()) return checkAndUpdateState(parentNode, parentContext, state, 0, ifBlock.block);
593
+ else parentContext.unlisten();
463
594
  }
464
595
  /**
465
596
  * Handles the `if` / `else` case (exactly two branches).
466
597
  *
467
598
  * If the `if` condition is true, the first branch is activated (state `0`); otherwise,
468
599
  * the `else` branch is activated (state `1`).
469
- *
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
470
603
  * @param ifBlock - The `if` branch to evaluate.
471
604
  * @param elseBlock - The `else` branch used when the `if` condition is false.
472
605
  * @param state - The current state (index of the active branch, or `null` if none).
473
- * @param unwatchFns - Shared array of active cleanup functions.
474
- * @param localUnwatchFns - Cleanup functions generated by the last branch activation.
475
606
  * @returns The new state and new cleanup functions if the active branch changed,
476
607
  * otherwise `undefined`.
477
608
  */
478
- function handleIfElse(parentNode, ifBlock, elseBlock, state, unwatchFns, localUnwatchFns) {
479
- return ifBlock.condition() ? checkAndUpdateState(parentNode, state, 0, ifBlock.block, unwatchFns, localUnwatchFns) : checkAndUpdateState(parentNode, state, 1, elseBlock.block, unwatchFns, localUnwatchFns);
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);
480
611
  }
481
612
  /**
482
613
  * Handles the general case of an `if` / `else if` / ... / `else` chain (three or more branches).
@@ -484,18 +615,18 @@ function handleIfElse(parentNode, ifBlock, elseBlock, state, unwatchFns, localUn
484
615
  * Iterates through the branches in order and activates the first one whose condition is
485
616
  * true; a branch without a condition is always considered valid and acts as the final
486
617
  * `else`. The state is set to the index of the activated branch.
487
- *
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
488
621
  * @param blocks - Ordered list of conditional branches to evaluate.
489
622
  * @param state - The current state (index of the active branch, or `null` if none).
490
- * @param unwatchFns - Shared array of active cleanup functions.
491
- * @param localUnwatchFns - Cleanup functions generated by the last branch activation.
492
623
  * @returns The new state and new cleanup functions if the active branch changed,
493
624
  * otherwise `undefined`. Also returns `undefined` if no branch matches.
494
625
  */
495
- function handleIfElseIf(parentNode, blocks, state, unwatchFns, localUnwatchFns) {
626
+ function handleIfElseIf(parentNode, parentContext, blocks, state) {
496
627
  for (let i = 0; i < blocks.length; i++) {
497
628
  const { condition, block } = blocks[i];
498
- if (!condition || condition()) return checkAndUpdateState(parentNode, state, i, block, unwatchFns, localUnwatchFns);
629
+ if (!condition || condition()) return checkAndUpdateState(parentNode, parentContext, state, i, block);
499
630
  }
500
631
  }
501
632
  /**
@@ -509,91 +640,80 @@ function handleIfElseIf(parentNode, blocks, state, unwatchFns, localUnwatchFns)
509
640
  *
510
641
  * If the branch has not changed, no operation is performed.
511
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
512
645
  * @param state - The current state (index of the active branch, or `null` if none).
513
646
  * @param newState - The new state to set (index of the branch to activate, or `null`).
514
647
  * @param conditionalBlockFn - Branch function to execute, which returns its own
515
648
  * cleanup functions.
516
- * @param unwatchFns - Shared array of active cleanup functions.
517
- * @param localUnwatchFns - Cleanup functions of the currently active branch, to be
518
- * removed before activating the new branch.
519
649
  * @returns Il nuovo stato e le nuove funzioni di cleanup se il ramo è cambiato, altrimenti
520
650
  * `undefined`.
521
651
  */
522
- function checkAndUpdateState(parentNode, state, newState, conditionalBlockFn, unwatchFns, localUnwatchFns) {
523
- if (state !== newState) {
524
- unwatch(unwatchFns, localUnwatchFns);
525
- localUnwatchFns = Signal.subtle.untrack(() => conditionalBlockFn(parentNode));
526
- unwatchFns.push(...localUnwatchFns);
652
+ function checkAndUpdateState(parentNode, parentContext, state, newState, conditionalBlockFn) {
653
+ if (state?.activeBranch !== newState) {
654
+ state?.context.unlisten();
527
655
  return {
528
- state: newState,
529
- fns: localUnwatchFns
656
+ activeBranch: newState,
657
+ context: Signal.subtle.untrack(() => conditionalBlockFn(parentNode, parentContext))
530
658
  };
531
659
  }
532
660
  }
533
- /**
534
- * Executes and removes the cleanup functions of the previously active branch.
535
- *
536
- * For each local function: removes it from the shared `unwatchFns` array (if present)
537
- * and then invokes it to release resources / tear down associated subscriptions.
538
- *
539
- * @param unwatchFns - Shared array of active cleanup functions, mutated in place.
540
- * @param localUnwatchFns - Cleanup functions of the branch to deactivate.
541
- */
542
- function unwatch(unwatchFns, localUnwatchFns) {
543
- for (const fn of localUnwatchFns) {
544
- const index = unwatchFns.indexOf(fn);
545
- if (index !== -1) unwatchFns.splice(index, 1);
546
- fn();
547
- }
548
- }
549
661
  //#endregion
550
- //#region ../packages/core/src/utils/for.utils.ts
662
+ //#region ../packages/core/src/utils/render-text.util.ts
551
663
  /**
552
- * Reactively iterates over a list of items, re-running the loop whenever the tracked condition changes.
553
- * Previous iteration side-effects are cleaned up before each re-evaluation.
554
- *
555
- * @param parentNode - The parent HTML element where the conditional structure is applied.
556
- * @param unwatchFns - Array collecting cleanup functions for the parent scope.
557
- * @param condition - A reactive function that returns the array of items to iterate over.
558
- * @param forFn - A callback invoked for each item, receiving the parent node, the item, and its index. Must return an array of cleanup functions.
664
+ *
665
+ * @param parentNode
666
+ * @param text
667
+ * @param context
668
+ * @returns
559
669
  */
560
- function _for(parentNode, unwatchFns, condition, forFn) {
561
- const localUnwatchFns = new Array();
562
- const unlistener = effect(() => {
563
- unwatch(unwatchFns, localUnwatchFns);
564
- const items = condition();
565
- Signal.subtle.untrack(() => {
566
- for (let i = 0; i < items.length; i++) {
567
- localUnwatchFns.push(...forFn(parentNode, items[i], i));
568
- unwatchFns.push(...localUnwatchFns);
569
- }
570
- });
670
+ function _renderText(parentNode, context, text) {
671
+ const identifier = context.getIdentifier(text);
672
+ const node = document.createTextNode(identifier.get());
673
+ parentNode.appendChild(node);
674
+ context.listen(() => {
675
+ context.removeNode(node);
676
+ parentNode.removeChild(node);
571
677
  });
572
- unwatchFns.push(unlistener);
678
+ if (identifier.reactive) context.listen(effect(() => node.textContent = identifier.get()));
573
679
  }
574
680
  /**
575
- * Builds a record of iteration context variables for a given index in the loop.
576
- * Provides the current item, index, and convenience flags (`$first`, `$last`, `$even`, `$odd`).
577
- *
578
- * @param items - The full array being iterated.
579
- * @param index - The current iteration index.
580
- * @param itemAlias - The template alias name for the current item.
581
- * @param indexAlias - The template alias name for the current index.
582
- * @returns A record mapping alias names and built-in variables to their values.
681
+ *
682
+ * @param parentNode
683
+ * @param text
684
+ * @param context
685
+ * @returns
583
686
  */
584
- function _iterationVariables(items, index, itemAlias, indexAlias) {
585
- const even = index % 2 === 0;
586
- return {
587
- [itemAlias]: items[index],
588
- [indexAlias]: index,
589
- $first: index === 0,
590
- $last: index === items.length - 1,
591
- $even: even,
592
- $odd: !even
593
- };
687
+ function _renderLiteralText(parentNode, context, text) {
688
+ const node = document.createTextNode(text);
689
+ parentNode.appendChild(node);
690
+ context.listen(() => parentNode.removeChild(node));
691
+ }
692
+ //#endregion
693
+ //#region ../packages/core/src/utils/render-util.ts
694
+ function _renderElement(parentNode, context, tagName, attributes, events) {
695
+ const element = document.createElement(tagName);
696
+ parentNode.appendChild(element);
697
+ context.listen(() => {
698
+ context.removeNode(element);
699
+ parentNode.removeChild(element);
700
+ });
701
+ attributes.forEach(({ name, value, literal }) => {
702
+ if (literal) element.setAttribute(name, value);
703
+ else {
704
+ const identifier = context.getIdentifier(value);
705
+ identifier.reactive ? context.listen(effect(() => element.setAttribute(name, String(identifier.get())))) : element.setAttribute(name, String(identifier.get()));
706
+ }
707
+ });
708
+ events.forEach((event) => {
709
+ const handler = ($event) => context.getEventHandler(event.handler);
710
+ const name = event.name;
711
+ element.addEventListener(name, handler);
712
+ context.listen(() => element.removeEventListener(name, handler));
713
+ });
594
714
  }
595
715
  //#endregion
596
- //#region ../packages/core/src/utils/switch.utils.ts
716
+ //#region ../packages/core/src/utils/switch.util.ts
597
717
  /**
598
718
  * Creates a reactive switch/case structure by converting it into an if/else-if chain
599
719
  * and delegating to {@link _if}.
@@ -608,6 +728,7 @@ function _iterationVariables(items, index, itemAlias, indexAlias) {
608
728
  * the active branch is only re-rendered when it actually changes.
609
729
  *
610
730
  * @param parentNode - The parent HTML element where the conditional structure is applied.
731
+ * @param parentContext - The parent Context object containing all the variables definition from the Parent Closure
611
732
  * @param unwatchFns - Shared array that collects all active cleanup functions.
612
733
  * Mutated in place by the underlying {@link _if} call.
613
734
  * @param expression - Function that evaluates the switch expression. Called
@@ -618,11 +739,11 @@ function _iterationVariables(items, index, itemAlias, indexAlias) {
618
739
  * - `block`: function that applies the branch's side effects and returns
619
740
  * its cleanup functions.
620
741
  */
621
- function _switch(parentNode, unwatchFns, expression, blocks) {
622
- _if(parentNode, unwatchFns, blocks.map(({ condition, block }) => ({
742
+ function _switch(parentNode, parentContext, expression, blocks) {
743
+ _if(parentNode, parentContext, blocks.map(({ condition, block }) => ({
623
744
  condition: condition ? () => condition.some((condition) => condition === expression()) : void 0,
624
745
  block
625
746
  })));
626
747
  }
627
748
  //#endregion
628
- export { BaseWebComponent, Event, Property, WebComponent, _for, _if, _iterationVariables, _switch, computed, effect, input, signal, unwatch };
749
+ export { BaseWebComponent, Context, Event, Property, WebComponent, _for, _if, _iterationVariables, _renderElement, _renderLiteralText, _renderText, _switch, computed, effect, input, signal };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xaendar/core",
3
- "version": "0.6.9",
3
+ "version": "0.6.11",
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.9",
20
- "@xaendar/types": "0.6.9"
19
+ "@xaendar/signals": "0.6.11",
20
+ "@xaendar/types": "0.6.11"
21
21
  }
22
22
  }