xt-components 0.6.3 → 0.7.0

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.
@@ -1,20 +1,31 @@
1
1
  import { isTypeReference, XtBaseTypeReference, xtTypeManager, ManagedDataHandler, isPrimitive } from 'xt-type';
2
2
  import * as i0 from '@angular/core';
3
3
  import { signal, computed, InjectionToken, inject, Injectable, input, model, output, viewChild, Component } from '@angular/core';
4
- import { loadRemoteModule } from '@angular-architects/native-federation';
5
- import { from, firstValueFrom } from 'rxjs';
4
+ import { firstValueFrom, lastValueFrom, range, delay, find, from } from 'rxjs';
6
5
  import { NgComponentOutlet, CommonModule } from '@angular/common';
7
6
  import * as i1 from '@angular/forms';
8
7
  import { ReactiveFormsModule, FormControl, FormGroup, FormBuilder, FormArray } from '@angular/forms';
9
8
 
9
+ /**
10
+ * Maintain the list of plugins loaded, with their components and actions
11
+ *
12
+ */
10
13
  class XtPluginRegistry {
11
14
  constructor() {
15
+ /** Map of all registered plugins by name */
12
16
  this.pluginRegistry = new Map();
17
+ /** Map of all registered components by component name */
13
18
  this.componentRegistry = new Map();
19
+ /** Cache of components found by value type */
14
20
  this.componentByTypeCache = new Map();
21
+ /** Map of all registered workflows by name */
22
+ this.workflowRegistry = new Map();
23
+ /** Map of actions registered by type, then by action name */
15
24
  this.actionByTypeRegistry = new Map();
16
- this.listComponents = signal(new Array(), ...(ngDevMode ? [{ debugName: "listComponents" }] : []));
17
- this.listPlugins = signal(new Array(), ...(ngDevMode ? [{ debugName: "listPlugins" }] : []));
25
+ /** Signal listing all registered component infos */
26
+ this.listComponents = signal(new Array(), ...(ngDevMode ? [{ debugName: "listComponents" }] : /* istanbul ignore next */ []));
27
+ /** Signal listing all registered plugin infos */
28
+ this.listPlugins = signal(new Array(), ...(ngDevMode ? [{ debugName: "listPlugins" }] : /* istanbul ignore next */ []));
18
29
  }
19
30
  /**
20
31
  * The component can manage any standard javascript primitives types. That's usually the default whenever we don't know any particular type
@@ -32,13 +43,20 @@ class XtPluginRegistry {
32
43
  * The components can manage any composite javascript type. Default when no type has been defined and it's a user defined javascript object (not a data type)
33
44
  */
34
45
  static { this.ANY_OBJECT_TYPE = "ANY_OBJECT_TYPE"; }
46
+ /** Components can manage any set of primitive types */
35
47
  static { this.ANY_PRIMITIVE_SET = "ANY_PRIMITIVE_SET"; }
48
+ /** Components can manage any set of object types */
36
49
  static { this.ANY_OBJECT_SET = "ANY_OBJECT_SET"; }
37
50
  /**
38
51
  * Whenever a component can handle any type of reference to a single entity or to multiple entities.
39
52
  */
40
53
  static { this.ANY_SINGLE_REFERENCE = "ANY_SINGLE_REFERENCE"; }
54
+ /** Whenever a component can handle any type of reference to multiple entities */
41
55
  static { this.ANY_MULTIPLE_REFERENCE = "ANY_MULTIPLE_REFERENCE"; }
56
+ /**
57
+ * Registers a plugin with its components, actions, and workflows
58
+ * @param info - The plugin information to register
59
+ */
42
60
  registerPlugin(info) {
43
61
  this.pluginRegistry.set(info.name, info);
44
62
  if (info.components != null) {
@@ -55,6 +73,12 @@ class XtPluginRegistry {
55
73
  this.registerActionHandler(handler);
56
74
  }
57
75
  }
76
+ if (info.workflows != null) {
77
+ for (const wfw of info.workflows) {
78
+ this.registerWorkflow(wfw);
79
+ }
80
+ }
81
+ // Updates the signal which list all plugins
58
82
  this.listPlugins.update((array) => {
59
83
  let found = false;
60
84
  for (let i = 0; i < array.length; i++) {
@@ -68,6 +92,17 @@ class XtPluginRegistry {
68
92
  return [...array]; // You have to send another value, not just update the existing one.
69
93
  });
70
94
  }
95
+ /**
96
+ * Registers a workflow component
97
+ * @param info - The workflow information to register
98
+ */
99
+ registerWorkflow(info) {
100
+ this.workflowRegistry.set(info.name, info);
101
+ }
102
+ /**
103
+ * Registers a component in the component registry
104
+ * @param info - The component information to register
105
+ */
71
106
  registerComponent(info) {
72
107
  this.componentRegistry.set(info.componentName, info);
73
108
  this.listComponents.update((array) => {
@@ -83,6 +118,12 @@ class XtPluginRegistry {
83
118
  return array;
84
119
  });
85
120
  }
121
+ /**
122
+ * Finds components that can handle the given value type
123
+ * @param valueType - The value type to search for, or null/undefined to infer from the value
124
+ * @param value - The optional value to infer the type from
125
+ * @returns An array of matching component infos
126
+ */
86
127
  findComponentsForType(valueType, value) {
87
128
  let originalType = valueType;
88
129
  //console.debug('Finding type from '+valueType+' with value ',value);
@@ -134,9 +175,18 @@ class XtPluginRegistry {
134
175
  }
135
176
  return ret;
136
177
  }
178
+ /**
179
+ * Returns the global singleton plugin registry
180
+ * @returns The global XtPluginRegistry instance
181
+ */
137
182
  static registry() {
138
183
  return XT_REGISTRY;
139
184
  }
185
+ /**
186
+ * Finds component info for the given component class type
187
+ * @param type - The component class type to search for
188
+ * @returns The component info or null if not found
189
+ */
140
190
  findComponentInfo(type) {
141
191
  // Search for the component registered with this class
142
192
  for (const info of this.componentRegistry.values()) {
@@ -146,6 +196,12 @@ class XtPluginRegistry {
146
196
  }
147
197
  return null;
148
198
  }
199
+ /**
200
+ * Gets component info for the given component class type, throwing if not found
201
+ * @param type - The component class type to search for
202
+ * @returns The component info
203
+ * @throws Error if no component is found with the given class
204
+ */
149
205
  getComponentInfo(type) {
150
206
  const ret = this.findComponentInfo(type);
151
207
  if (ret == null) {
@@ -153,6 +209,32 @@ class XtPluginRegistry {
153
209
  }
154
210
  return ret;
155
211
  }
212
+ /**
213
+ * Finds all the workflow components that can handle the given workflow type
214
+ * @param type
215
+ */
216
+ listWorkflowsForType(type) {
217
+ const ret = [];
218
+ for (const info of this.workflowRegistry.values()) {
219
+ const index = info.workflowsHandled.findIndex((val) => val == type);
220
+ if (index >= 0) {
221
+ ret.push(info);
222
+ }
223
+ }
224
+ return ret;
225
+ }
226
+ /**
227
+ * Finds workflow info by name
228
+ * @param name - The workflow name to search for
229
+ * @returns The workflow info or undefined if not found
230
+ */
231
+ findWorkflowInfo(name) {
232
+ return this.workflowRegistry.get(name);
233
+ }
234
+ /**
235
+ * Registers action handlers grouped by type
236
+ * @param handlerInfo - The action handler information to register
237
+ */
156
238
  registerActionHandler(handlerInfo) {
157
239
  for (const type of handlerInfo.types) {
158
240
  const handlers = handlerInfo.actions;
@@ -166,6 +248,12 @@ class XtPluginRegistry {
166
248
  }
167
249
  }
168
250
  }
251
+ /**
252
+ * Finds action info for the given type and action name
253
+ * @param type - The type to look up
254
+ * @param actionName - The action name to find
255
+ * @returns The action info or undefined if not found
256
+ */
169
257
  findActionInfo(type, actionName) {
170
258
  const handlers = this.actionByTypeRegistry.get(type);
171
259
  if (handlers != null) {
@@ -173,6 +261,11 @@ class XtPluginRegistry {
173
261
  }
174
262
  return undefined;
175
263
  }
264
+ /**
265
+ * Lists all action infos registered for the given type
266
+ * @param type - The type to list actions for
267
+ * @returns An array of action name and info pairs
268
+ */
176
269
  listActionInfos(type) {
177
270
  const handlers = this.actionByTypeRegistry.get(type);
178
271
  if (handlers != null) {
@@ -185,15 +278,21 @@ class XtPluginRegistry {
185
278
  }
186
279
  }
187
280
 
281
+ /**
282
+ * Base implementation of the XtContext interface.
283
+ * Manages display mode, form integration, child context hierarchy, and value tracking
284
+ * for an ng-extended component.
285
+ */
188
286
  class XtBaseContext {
189
287
  /**
190
- *
191
- * @param displayMode
192
- * @param readOnly
193
- * @param parentGroup
194
- * @param controlName
288
+ * Creates a new XtBaseContext instance.
289
+ * @param displayMode - The display mode for this context
290
+ * @param subName - Optional sub-name used within a parent form group
291
+ * @param parentGroup - Optional parent FormGroup for reactive form integration
292
+ * @param parentContext - Optional parent context for hierarchy management
195
293
  */
196
294
  constructor(displayMode, subName, parentGroup, parentContext) {
295
+ /** The current display mode for this context. Defaults to FULL_VIEW. */
197
296
  this.displayMode = 'FULL_VIEW';
198
297
  /**
199
298
  * If it's a reference, we keep the context referenced
@@ -204,7 +303,8 @@ class XtBaseContext {
204
303
  * Keeps track of all the possible actions for this context
205
304
  * @protected
206
305
  */
207
- this.listActions = signal(null, ...(ngDevMode ? [{ debugName: "listActions" }] : []));
306
+ this.listActions = signal(null, ...(ngDevMode ? [{ debugName: "listActions" }] : /* istanbul ignore next */ []));
307
+ /** Computed signal that returns the current display value from non-form storage. */
208
308
  this.displayValue = computed(() => {
209
309
  if (this.nonFormValue !== undefined) {
210
310
  return this.nonFormValue();
@@ -212,7 +312,7 @@ class XtBaseContext {
212
312
  else {
213
313
  throw new Error("Cannot display a value that does not exist. Are you sure you're not using Reactive Form with this context? " + this.toString());
214
314
  }
215
- }, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
315
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
216
316
  this.displayMode = displayMode;
217
317
  this.parentFormGroup = parentGroup;
218
318
  this.parentContext = parentContext;
@@ -225,13 +325,21 @@ class XtBaseContext {
225
325
  }
226
326
  }
227
327
  }
328
+ /**
329
+ * Sets the display value for this context when not managed by a reactive form.
330
+ * Propagates changes to child contexts and optionally notifies the parent context.
331
+ * @param newValue - The new value to set
332
+ * @param type - Optional type identifier to assign
333
+ * @param updateParent - Whether to propagate the change to the parent context (default true)
334
+ * @returns This context instance for chaining
335
+ */
228
336
  setDisplayValue(newValue, type, updateParent = true) {
229
337
  if (newValue !== undefined) {
230
338
  const oldValue = this.nonFormValue ? this.nonFormValue() : null;
231
339
  if (this.nonFormValue == null) {
232
340
  if ((this.childContexts != null) && (this.childContexts.size > 0))
233
341
  throw new Error('An XtContext with no values cannot have children ', { cause: this });
234
- this.nonFormValue = signal(newValue, ...(ngDevMode ? [{ debugName: "nonFormValue" }] : []));
342
+ this.nonFormValue = signal(newValue, ...(ngDevMode ? [{ debugName: "nonFormValue" }] : /* istanbul ignore next */ []));
235
343
  }
236
344
  else {
237
345
  this.nonFormValue.set(newValue);
@@ -259,18 +367,35 @@ class XtBaseContext {
259
367
  this.valueType = type;
260
368
  return this;
261
369
  }
370
+ /**
371
+ * Checks whether this context is bound to a reactive form group.
372
+ * @returns True if a form group exists
373
+ */
262
374
  isInForm() {
263
375
  return (this.formGroup() != null);
264
376
  }
377
+ /**
378
+ * Returns the sub-name used as the form control name, or null if not in a form.
379
+ * @returns The sub-name or null
380
+ */
265
381
  formControlNameOrNull() {
266
382
  return this.subName ?? null;
267
383
  }
384
+ /**
385
+ * Returns the current value from either the non-form signal or the form control.
386
+ * @returns The current value, or null/undefined
387
+ */
268
388
  value() {
269
389
  if (this.nonFormValue != null)
270
390
  return this.nonFormValue() ?? this.formControlValue();
271
391
  else
272
392
  return this.formControlValue();
273
393
  }
394
+ /**
395
+ * Returns a sub-value by name from the current value object.
396
+ * @param subsubName - Optional sub-key to retrieve from the value object
397
+ * @returns The sub-value or the full value if no key is given
398
+ */
274
399
  subValue(subsubName) {
275
400
  const value = this.nonFormValue ? this.nonFormValue() : this.formControlValue();
276
401
  if ((subsubName != null) && (value != null)) {
@@ -299,6 +424,10 @@ class XtBaseContext {
299
424
  throw new Error("No nonFormValue to update subDisplayValue" + this.toString());
300
425
  }
301
426
  }
427
+ /**
428
+ * Retrieves the value from the parent form group for this context's sub-name.
429
+ * @returns The form control value, or undefined if not in a form
430
+ */
302
431
  formControlValue() {
303
432
  let ret = undefined;
304
433
  if (this.isInForm()) {
@@ -316,6 +445,12 @@ class XtBaseContext {
316
445
  //console.debug("formControlValue of "+this.subName+ " is ",ret);
317
446
  return ret;
318
447
  }
448
+ /**
449
+ * Sets the value in the parent form group for this context's sub-name.
450
+ * @param newValue - The new value to set on the form control
451
+ * @param markAsDirty - Whether to mark the control as dirty (default false)
452
+ * @returns True if the value was successfully set
453
+ */
319
454
  setFormValue(newValue, markAsDirty = false) {
320
455
  if (this.isInForm()) {
321
456
  if (this.subName != null) {
@@ -366,6 +501,14 @@ class XtBaseContext {
366
501
  }
367
502
  return ret;
368
503
  }
504
+ /**
505
+ * Returns or creates a child context for the given sub-name.
506
+ * Resolves type information and references when a typeResolver is provided.
507
+ * @param subName - The sub-name for the child context
508
+ * @param subType - Optional type hint for the child
509
+ * @param typeResolver - Optional type resolver for resolving sub-types and references
510
+ * @returns The child XtContext
511
+ */
369
512
  subContext(subName, subType, typeResolver) {
370
513
  if ((subName == null) || (subName.length == 0)) {
371
514
  return this;
@@ -421,12 +564,24 @@ class XtBaseContext {
421
564
  return ret;
422
565
  }
423
566
  }
567
+ /**
568
+ * Returns the available form group, preferring localFormGroup over parentFormGroup.
569
+ * @returns The form group or undefined
570
+ */
424
571
  formGroup() {
425
572
  return this.localFormGroup ?? this.parentFormGroup;
426
573
  }
574
+ /**
575
+ * Checks whether this context holds a reference to another type.
576
+ * @returns True if a reference is set
577
+ */
427
578
  isReference() {
428
579
  return (this.reference != null);
429
580
  }
581
+ /**
582
+ * Sets the type reference information for this context.
583
+ * @param reference - The type reference to set
584
+ */
430
585
  setReferenceInfo(reference) {
431
586
  this.reference = reference;
432
587
  //this.subReferencesResolved.set(this.reference!=null);
@@ -446,6 +601,10 @@ class XtBaseContext {
446
601
  this.referencedContext.setDisplayValue(val);
447
602
  if( valueType!=null) this.referencedContext.valueType=valueType;
448
603
  }*/
604
+ /**
605
+ * Returns a string representation of this context, including its name, type, value, and reference info.
606
+ * @returns A human-readable description of the context
607
+ */
449
608
  toString() {
450
609
  let ret = 'XtContext named ';
451
610
  ret += this.subName ?? 'None';
@@ -461,12 +620,27 @@ class XtBaseContext {
461
620
  }
462
621
  }
463
622
 
623
+ /**
624
+ * Represents a resolved component with its name, class reference, and output capability.
625
+ * Created by the resolver to indicate which XtComponent should be used for a given context.
626
+ */
464
627
  class XtResolvedComponent {
465
- constructor(componantName, componentClass, outputs = false) {
466
- this.componentName = componantName;
628
+ /**
629
+ * Creates a new XtResolvedComponent instance.
630
+ * @param componentName - The registered component name
631
+ * @param componentClass - The Angular component class
632
+ * @param outputs - Whether the component has outputs (default false)
633
+ */
634
+ constructor(componentName, componentClass, outputs = false) {
635
+ this.componentName = componentName;
467
636
  this.componentClass = componentClass;
468
637
  this.outputs = outputs;
469
638
  }
639
+ /**
640
+ * Creates an XtResolvedComponent from an XtComponentInfo.
641
+ * @param info - The component info from the plugin registry
642
+ * @returns A new XtResolvedComponent instance
643
+ */
470
644
  static from(info) {
471
645
  const ret = new XtResolvedComponent(info.componentName, info.componentClass);
472
646
  if ((info.outputs != null) && (info.outputs.length > 0)) {
@@ -476,6 +650,10 @@ class XtResolvedComponent {
476
650
  }
477
651
  }
478
652
 
653
+ /**
654
+ * Just declare a type marker for Workflows
655
+ */
656
+
479
657
  /**
480
658
  * The global plugin registry.
481
659
  * Plugins will register to this when loaded.
@@ -492,19 +670,34 @@ function xtPluginRegistry() {
492
670
  return globalThis.XT_REGISTRY;
493
671
  }
494
672
 
673
+ /** Injection token for providing a custom component resolver */
495
674
  const XT_RESOLVER_TOKEN = new InjectionToken('Enable providing a custom component resolver.');
675
+ /** Injection token for providing a custom type resolver */
496
676
  const XT_TYPE_RESOLVER_TOKEN = new InjectionToken('Enable providing a custom type resolver.');
677
+ /** Injection token for providing the plugin registry with a default factory */
497
678
  const XT_REGISTRY_TOKEN = new InjectionToken("Injects the Plugin Registry right into your angular component", {
498
679
  factory: () => {
499
680
  return xtPluginRegistry();
500
681
  }
501
682
  });
502
683
 
684
+ /** Resolves components by looking them up in the plugin registry based on context type */
503
685
  class XtRegistryResolver {
686
+ /**
687
+ * Creates a new XtRegistryResolver
688
+ * @param registry - The plugin registry to use for component lookups
689
+ * @param typeResolver - The type resolver for type hierarchy information
690
+ */
504
691
  constructor(registry, typeResolver) {
505
692
  this.registry = registry;
506
693
  this.typeResolver = typeResolver;
507
694
  }
695
+ /**
696
+ * Resolves a component for the given context and optional subName
697
+ * @param baseContext - The context containing type and value information
698
+ * @param subName - Optional sub-name for nested property resolution
699
+ * @returns The resolved component info or null if no component matches
700
+ */
508
701
  resolve(baseContext, subName) {
509
702
  let typeToFind = baseContext.valueType;
510
703
  const typeInfo = this.typeResolver.findType(baseContext.valueType, subName);
@@ -525,9 +718,17 @@ class XtRegistryResolver {
525
718
  }
526
719
  }
527
720
 
721
+ /** Represents a registered action with its metadata and enabled state */
528
722
  class XtAction {
723
+ /**
724
+ * Creates a new XtAction instance
725
+ * @param name - The unique name of the action
726
+ * @param info - Metadata describing the action
727
+ * @param enabled - Whether the action is initially enabled (defaults to false)
728
+ */
529
729
  constructor(name, info, enabled) {
530
- this.enabled = signal(false, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
730
+ /** Signal indicating whether the action is currently enabled */
731
+ this.enabled = signal(false, ...(ngDevMode ? [{ debugName: "enabled" }] : /* istanbul ignore next */ []));
531
732
  this.name = name;
532
733
  this.info = info;
533
734
  if (enabled != null) {
@@ -536,178 +737,73 @@ class XtAction {
536
737
  }
537
738
  }
538
739
 
539
- /**
540
- * A very light and not 100% compatible storemanager in case you are not using xt-store.
541
- * It can emulate XtStoreManager to some extends for doing some tests
542
- */
543
- class StoreTestHelper {
544
- static ensureTestProviderOnly() {
545
- StoreSupport.setTestStoreManager(new TestStoreManager());
546
- }
547
- }
548
- class TestStoreManager {
549
- constructor() {
550
- this.defaultProvider = new TestStoreProvider();
551
- }
552
- getProvider(name) {
553
- return this.defaultProvider;
554
- }
555
- getProviderSafe(name) {
556
- return this.defaultProvider;
557
- }
558
- getDefaultProvider() {
559
- return this.defaultProvider;
560
- }
561
- getDefaultProviderSafe() {
562
- return this.defaultProvider;
563
- }
564
- newStoreCriteria(name, value, operator) {
565
- return new TestStoreCriteria(name, value, operator);
566
- }
567
- }
568
- class TestStoreProvider {
569
- constructor() {
570
- this.data = new Map();
571
- }
572
- getOrCreateArray(name) {
573
- let ret = this.data.get(name);
574
- if (ret == null) {
575
- ret = new Map();
576
- this.data.set(name, ret);
577
- }
578
- return ret;
579
- }
580
- extractKey(value, create) {
581
- if (value._id != null)
582
- return value._id; // ManagedData key
583
- else if (value.id != null)
584
- return value.id;
585
- else {
586
- if (create === true) {
587
- const newId = Math.random().toString(36).substring(2, 8);
588
- value._id = newId;
589
- return newId;
590
- }
591
- return value.toString();
592
- }
593
- }
594
- storeEntity(name, entity) {
595
- this.getOrCreateArray(name).set(this.extractKey(entity, true), entity);
596
- return Promise.resolve(entity);
597
- }
598
- safeLoadEntity(name, key) {
599
- const ret = this.getOrCreateArray(name).get(key);
600
- if (ret == null) {
601
- throw new Error("No entity named " + name + " with key " + key);
602
- }
603
- return Promise.resolve(ret);
604
- }
605
- loadEntity(name, key) {
606
- return Promise.resolve(this.getOrCreateArray(name).get(key));
607
- }
608
- deleteEntity(name, key) {
609
- return Promise.resolve(this.getOrCreateArray(name).delete(key));
610
- }
611
- searchEntities(name, ...criteria) {
612
- // No criteria defined, just send the full list
613
- const ret = new Array();
614
- if ((criteria == null) || (criteria.length == 0)) {
615
- for (const toAdd of this.getOrCreateArray(name).values()) {
616
- ret.push(toAdd);
617
- }
618
- }
619
- else {
620
- for (const toAdd of this.getOrCreateArray(name).values()) {
621
- let canAdd = true;
622
- for (const criter of criteria) {
623
- if (!criter.filter(toAdd)) {
624
- canAdd = false;
625
- break;
626
- }
627
- }
628
- if (canAdd)
629
- ret.push(toAdd);
630
- }
631
- }
632
- return from([ret]);
633
- }
634
- searchAndPrepareEntities(name, sort, groupBy, transformer, ...criteria) {
635
- throw new Error('Method not implemented.');
636
- }
637
- canStoreDocument() {
638
- return true;
639
- }
640
- storeDocument(toStore) {
641
- const ret = new TestDocumentInfo(toStore.name, true, URL.createObjectURL(toStore));
642
- return Promise.resolve(ret);
643
- }
644
- storeDocuments(toStore) {
645
- throw new Error('Method not implemented.');
646
- }
647
- }
648
- class TestDocumentInfo {
649
- constructor(documentName, isUrl, documentId) {
650
- this.documentId = documentId;
651
- this.documentName = documentName;
652
- this.isUrl = isUrl;
653
- }
654
- }
655
- class TestStoreCriteria {
656
- constructor(name, value, operator) {
657
- this.name = name;
658
- this.value = value;
659
- if (!operator)
660
- this.operator = '=';
661
- else {
662
- this.operator = operator;
663
- }
664
- }
665
- filter(toFilter) {
666
- const testValue = toFilter[this.name];
667
- switch (this.operator) {
668
- case '=':
669
- return testValue == this.value;
670
- case '<':
671
- return testValue < this.value;
672
- case '<=':
673
- return testValue < this.value;
674
- default:
675
- return true;
676
- }
677
- }
678
- }
679
-
680
740
  class StoreSupport {
741
+ /**
742
+ * Checks if a store manager is available (either test or global)
743
+ * @returns true if a store manager is available
744
+ */
681
745
  static isStoreManagerAvailable() {
682
746
  if (this.testStoreManager != null)
683
747
  return true;
684
748
  return (globalThis.xtStoreManager != undefined);
685
749
  }
750
+ /**
751
+ * Gets the current store manager (test or global)
752
+ * @returns The IStoreManager instance
753
+ */
686
754
  static getStoreManager() {
687
755
  return this.testStoreManager ?? (globalThis.xtStoreManager());
688
756
  }
757
+ /**
758
+ * Sets a test store manager for testing purposes
759
+ * @param testStoreManager - The test store manager to use
760
+ */
689
761
  static setTestStoreManager(testStoreManager) {
690
762
  StoreSupport.testStoreManager = testStoreManager;
691
763
  }
764
+ /**
765
+ * Creates a new store criteria for filtering
766
+ * @param name - The property name to filter on
767
+ * @param value - The value to filter by
768
+ * @param operator - The comparison operator (defaults to '=')
769
+ * @returns A new IStoreCriteria instance
770
+ */
692
771
  static newStoreCriteria(name, value, operator) {
693
- return new TestStoreCriteria(name, value, operator);
772
+ if (operator == null)
773
+ operator = '=';
774
+ return StoreSupport.getStoreManager().newStoreCriteria(name, value, operator);
694
775
  }
695
776
  }
777
+ /** Sort direction options */
778
+ var ISortByDirection;
779
+ (function (ISortByDirection) {
780
+ /** No sorting */
781
+ ISortByDirection["None"] = "None";
782
+ /** Sort in ascending order */
783
+ ISortByDirection["Ascending"] = "Ascending";
784
+ /** Sort in descending order */
785
+ ISortByDirection["Descending"] = "Descending";
786
+ })(ISortByDirection || (ISortByDirection = {}));
696
787
 
697
788
  /**
698
789
  * An all in one helper class, enabling manipulation of the context, with data and type associated with it.
699
790
  */
700
791
  class XtResolverService {
701
792
  constructor() {
793
+ /** Injected plugin registry for component and type registration. */
702
794
  this.pluginRegistry = inject(XT_REGISTRY_TOKEN);
795
+ /** Optional base resolver injected via DI token. */
703
796
  this.baseResolver = inject(XT_RESOLVER_TOKEN, { optional: true });
797
+ /** Optional base type resolver injected via DI token. */
704
798
  this.baseTypeResolver = inject(XT_TYPE_RESOLVER_TOKEN, { optional: true });
799
+ /** Computed signal that lists all registered components. */
705
800
  this.listComponents = computed(() => {
706
801
  return this.pluginRegistry.listComponents();
707
- }, ...(ngDevMode ? [{ debugName: "listComponents" }] : []));
802
+ }, ...(ngDevMode ? [{ debugName: "listComponents" }] : /* istanbul ignore next */ []));
803
+ /** Computed signal that lists all registered plugins. */
708
804
  this.listPlugins = computed(() => {
709
805
  return this.pluginRegistry.listPlugins();
710
- }, ...(ngDevMode ? [{ debugName: "listPlugins" }] : []));
806
+ }, ...(ngDevMode ? [{ debugName: "listPlugins" }] : /* istanbul ignore next */ []));
711
807
  if (this.baseTypeResolver == null) {
712
808
  this.typeResolver = xtTypeManager();
713
809
  }
@@ -719,6 +815,12 @@ class XtResolverService {
719
815
  else
720
816
  this.resolver = this.baseResolver;
721
817
  }
818
+ /**
819
+ * Finds the best matching component for the given context and optional sub-name.
820
+ * @param baseContext - The context to resolve against
821
+ * @param subName - Optional sub-name for nested resolution
822
+ * @returns The resolved component descriptor
823
+ */
722
824
  findBestComponent(baseContext, subName) {
723
825
  const ret = this.resolver.resolve(baseContext, subName);
724
826
  if (ret != null)
@@ -726,10 +828,24 @@ class XtResolverService {
726
828
  else
727
829
  throw new Error("No components found for this context " + baseContext.toString());
728
830
  }
831
+ /**
832
+ * Resolves the type name for the given context and optional sub-name.
833
+ * @param baseContext - The context to query
834
+ * @param subName - Optional sub-name for nested type resolution
835
+ * @param value - Optional value for type inference
836
+ * @returns The type name, or null/undefined
837
+ */
729
838
  findTypeOf(baseContext, subName, value) {
730
839
  const ret = this.typeResolver.findTypeName(baseContext.valueType, subName, value);
731
840
  return ret;
732
841
  }
842
+ /**
843
+ * Finds the type handler for the given context, resolving references if necessary.
844
+ * @param baseContext - The context to query
845
+ * @param subName - Optional sub-name for nested resolution
846
+ * @param value - Optional value for handler inference
847
+ * @returns An object with the type name and its handler
848
+ */
733
849
  findTypeHandlerOf(baseContext, subName, value) {
734
850
  let ret = { typeName: undefined, handler: undefined };
735
851
  if (baseContext.isReference()) {
@@ -740,16 +856,37 @@ class XtResolverService {
740
856
  }
741
857
  return ret;
742
858
  }
859
+ /**
860
+ * Lists the sub-names defined by the type of the given context.
861
+ * @param baseContext - The context to query
862
+ * @param value - Optional value for type inference
863
+ * @returns An array of sub-name strings
864
+ */
743
865
  listSubNamesOf(baseContext, value) {
744
866
  return this.typeResolver.listSubNames(baseContext.valueType, value);
745
867
  }
868
+ /**
869
+ * Lists the sub-names defined by a given value type string.
870
+ * @param valueType - The type string to query
871
+ * @param value - Optional value for type inference
872
+ * @returns An array of sub-name strings
873
+ */
746
874
  listSubNamesOfType(valueType, value) {
747
875
  return this.typeResolver.listSubNames(valueType, value);
748
876
  }
877
+ /**
878
+ * Registers a full plugin including its types and type handlers.
879
+ * @param info - The plugin information to register
880
+ */
749
881
  registerPlugin(info) {
750
882
  this.pluginRegistry.registerPlugin(info);
751
883
  this.registerTypes(info.types, info.typeHandlers);
752
884
  }
885
+ /**
886
+ * Registers type definitions and optional type handlers with the type resolver.
887
+ * @param types - The type definitions to register
888
+ * @param handlers - Optional type handler definitions
889
+ */
753
890
  registerTypes(types, handlers) {
754
891
  if ((types != null) && (this.typeResolver.canUpdate())) {
755
892
  for (const newType in types) {
@@ -809,6 +946,12 @@ class XtResolverService {
809
946
  }
810
947
  }
811
948
  }
949
+ /**
950
+ * Checks if a handler is defined for the given type among the provided handler infos.
951
+ * @param newType - The type to check
952
+ * @param handlers - The handler definitions to search
953
+ * @returns A handler instance or null
954
+ */
812
955
  handlerDefinedFor(newType, handlers) {
813
956
  for (const handler of handlers ?? []) {
814
957
  if (handler.typesHandled.includes(newType)) {
@@ -817,9 +960,19 @@ class XtResolverService {
817
960
  }
818
961
  return null;
819
962
  }
963
+ /**
964
+ * Gets the resolved component info for a given component type.
965
+ * @param type - The component type class
966
+ * @returns The resolved component descriptor
967
+ */
820
968
  getComponentInfo(type) {
821
969
  return XtResolvedComponent.from(this.pluginRegistry.getComponentInfo(type));
822
970
  }
971
+ /**
972
+ * Finds the resolved component info for a given component type, returning null if not found.
973
+ * @param type - The component type class
974
+ * @returns The resolved component descriptor or null
975
+ */
823
976
  findComponentInfo(type) {
824
977
  const ret = this.pluginRegistry.findComponentInfo(type);
825
978
  if (ret == null)
@@ -830,25 +983,20 @@ class XtResolverService {
830
983
  /**
831
984
  * Dynamically load a register a plugin from the given url
832
985
  * The plugin must export at least a Register entrypoint that will be called right after loading..
833
- * @param url
834
986
  * @returns a Promise with the module loaded and already registered.
987
+ * @param module
835
988
  */
836
- loadPlugin(url) {
837
- return loadRemoteModule({
838
- remoteEntry: url.toString(),
839
- exposedModule: './Register'
840
- }).then((module) => {
841
- const pluginName = module.registerPlugin(this);
842
- // Transform the configured Uris to real urls
843
- const pluginConfig = this.pluginRegistry.pluginRegistry.get(pluginName);
844
- if (pluginConfig?.uriLogo != null) {
845
- let urlString = url.toString();
846
- const lastSlash = urlString.lastIndexOf('/');
847
- urlString = urlString.substring(0, lastSlash + 1) + pluginConfig?.uriLogo;
848
- pluginConfig.uriLogo = urlString;
849
- }
850
- return module;
851
- });
989
+ registerPluginModule(module, url) {
990
+ const pluginName = module.registerPlugin(this);
991
+ // Transform the configured Uris to real urls
992
+ const pluginConfig = this.pluginRegistry.pluginRegistry.get(pluginName);
993
+ if (pluginConfig?.uriLogo != null) {
994
+ let urlString = url.toString();
995
+ const lastSlash = urlString.lastIndexOf('/');
996
+ urlString = urlString.substring(0, lastSlash + 1) + pluginConfig?.uriLogo;
997
+ pluginConfig.uriLogo = urlString;
998
+ }
999
+ return true;
852
1000
  }
853
1001
  /**
854
1002
  * Based on the type & value of the element, find which property is on its type and returns it's value
@@ -881,6 +1029,13 @@ class XtResolverService {
881
1029
  }
882
1030
  return structuredClone(value);
883
1031
  }
1032
+ /**
1033
+ * Resolves a mapping helper from the context's value type to a target type.
1034
+ * @param context - The source context
1035
+ * @param targetType - The target type to map to
1036
+ * @param value - Optional value for handler inference
1037
+ * @returns A MappingHelper if a mapping exists, otherwise undefined
1038
+ */
884
1039
  resolveMappingOf(context, targetType, value) {
885
1040
  if (context.valueType != null) {
886
1041
  const typeHandler = this.typeResolver.findTypeHandler(targetType, false, undefined, value);
@@ -893,6 +1048,12 @@ class XtResolverService {
893
1048
  }
894
1049
  return undefined;
895
1050
  }
1051
+ /**
1052
+ * Resolves the referenced value for a context that holds a type reference, using the provided store manager.
1053
+ * @param context - The context with a reference to resolve
1054
+ * @param storeMgr - The store manager for entity lookup
1055
+ * @returns The resolved referenced value(s), or null/undefined
1056
+ */
896
1057
  async resolveReferencedValue(context, storeMgr) {
897
1058
  if (!context.isReference())
898
1059
  return undefined;
@@ -914,6 +1075,9 @@ class XtResolverService {
914
1075
  }
915
1076
  return undefined;
916
1077
  }
1078
+ /**
1079
+ * Resolves all pending type references in the type resolver.
1080
+ */
917
1081
  resolvePendingReferences() {
918
1082
  this.typeResolver.resolveAllTypeReferences();
919
1083
  }
@@ -928,16 +1092,17 @@ class XtResolverService {
928
1092
  const store = StoreSupport.getStoreManager().getProviderSafe(reference.toType);
929
1093
  return store.searchEntities(reference.toType);
930
1094
  }
931
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtResolverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
932
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtResolverService, providedIn: 'root' }); }
1095
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtResolverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1096
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtResolverService, providedIn: 'root' }); }
933
1097
  }
934
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtResolverService, decorators: [{
1098
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtResolverService, decorators: [{
935
1099
  type: Injectable,
936
1100
  args: [{
937
1101
  providedIn: 'root'
938
1102
  }]
939
1103
  }], ctorParameters: () => [] });
940
1104
 
1105
+ /** Default implementation of XtComponentOutput */
941
1106
  class XtBaseOutput {
942
1107
  }
943
1108
 
@@ -948,19 +1113,31 @@ class XtBaseOutput {
948
1113
  */
949
1114
  class XtRenderComponent {
950
1115
  constructor() {
1116
+ /** Injected resolver service for finding the best component */
951
1117
  this.resolverService = inject(XtResolverService);
952
- this.componentType = input(...(ngDevMode ? [undefined, { debugName: "componentType" }] : []));
953
- this.displayMode = input.required(...(ngDevMode ? [{ debugName: "displayMode" }] : []));
954
- this.valueType = input(...(ngDevMode ? [undefined, { debugName: "valueType" }] : []));
955
- // Either we set the value directly
956
- this.value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : []));
957
- // Or we are inside a Form
958
- this.formGroup = input(...(ngDevMode ? [undefined, { debugName: "formGroup" }] : []));
959
- this.subName = input(...(ngDevMode ? [undefined, { debugName: "subName" }] : []));
1118
+ /** Optional explicit component type to render */
1119
+ this.componentType = input(...(ngDevMode ? [undefined, { debugName: "componentType" }] : /* istanbul ignore next */ []));
1120
+ /** The display mode (e.g. EDIT, VIEW, etc.) */
1121
+ this.displayMode = input.required(...(ngDevMode ? [{ debugName: "displayMode" }] : /* istanbul ignore next */ []));
1122
+ /** The value type identifier used for component resolution */
1123
+ this.valueType = input(...(ngDevMode ? [undefined, { debugName: "valueType" }] : /* istanbul ignore next */ []));
1124
+ /** The value to display/edit (used when not inside a form) */
1125
+ this.value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
1126
+ /** The parent form group (used when inside a form) */
1127
+ this.formGroup = input(...(ngDevMode ? [undefined, { debugName: "formGroup" }] : /* istanbul ignore next */ []));
1128
+ /** The sub-name within the form group */
1129
+ this.subName = input(...(ngDevMode ? [undefined, { debugName: "subName" }] : /* istanbul ignore next */ []));
1130
+ /** Object holding the output emitters from the rendered component */
960
1131
  this.outputsObject = new XtBaseOutput();
961
- this.inputs = input(...(ngDevMode ? [undefined, { debugName: "inputs" }] : []));
1132
+ /** Inputs to pass through to the rendered component */
1133
+ this.inputs = input(...(ngDevMode ? [undefined, { debugName: "inputs" }] : /* istanbul ignore next */ []));
1134
+ /** Emits the outputs from the rendered component */
962
1135
  this.outputs = output();
1136
+ /** Model signals to pass through to the rendered component */
1137
+ this.models = input(...(ngDevMode ? [undefined, { debugName: "models" }] : /* istanbul ignore next */ []));
1138
+ /** Reference to the NgComponentOutlet used to dynamically render the component */
963
1139
  this.outlet = viewChild.required(NgComponentOutlet);
1140
+ /** Computed context derived from display mode, form group, value, and value type */
964
1141
  this.context = computed(() => {
965
1142
  let form = this.formGroup();
966
1143
  const ret = new XtBaseContext(this.displayMode(), this.subName(), form);
@@ -980,14 +1157,16 @@ class XtRenderComponent {
980
1157
  }
981
1158
  }
982
1159
  return ret;
983
- }, ...(ngDevMode ? [{ debugName: "context" }] : []));
1160
+ }, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
1161
+ /** Computed context that resolves references (if any) */
984
1162
  this.realContext = computed(() => {
985
1163
  let ret = this.context();
986
1164
  /*if ((ret.isReference())&& (ret.referencedContext!=null)) {
987
1165
  ret = ret.referencedContext;
988
1166
  }*/
989
1167
  return ret;
990
- }, ...(ngDevMode ? [{ debugName: "realContext" }] : []));
1168
+ }, ...(ngDevMode ? [{ debugName: "realContext" }] : /* istanbul ignore next */ []));
1169
+ /** Computed component type to render, resolved from the context if not explicitly set */
991
1170
  this.type = computed(() => {
992
1171
  //console.debug("Calculating type in XtRenderSubComponent");
993
1172
  let type = this.componentType();
@@ -1001,7 +1180,7 @@ class XtRenderComponent {
1001
1180
  type = compFound.componentClass;
1002
1181
  }
1003
1182
  return type ?? null;
1004
- }, ...(ngDevMode ? [{ debugName: "type" }] : []));
1183
+ }, ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
1005
1184
  }
1006
1185
  /**
1007
1186
  * Transfers the input and outputs from the host to the rendered component
@@ -1025,16 +1204,16 @@ class XtRenderComponent {
1025
1204
  }
1026
1205
  }
1027
1206
  }
1028
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtRenderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1029
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.9", type: XtRenderComponent, isStandalone: true, selector: "xt-render", inputs: { componentType: { classPropertyName: "componentType", publicName: "componentType", isSignal: true, isRequired: false, transformFunction: null }, displayMode: { classPropertyName: "displayMode", publicName: "displayMode", isSignal: true, isRequired: true, transformFunction: null }, valueType: { classPropertyName: "valueType", publicName: "valueType", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: false, transformFunction: null }, subName: { classPropertyName: "subName", publicName: "subName", isSignal: true, isRequired: false, transformFunction: null }, inputs: { classPropertyName: "inputs", publicName: "inputs", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", outputs: "outputs" }, viewQueries: [{ propertyName: "outlet", first: true, predicate: NgComponentOutlet, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *ngComponentOutlet=\"type(); inputs: {context:context ()}\" />\n", styles: [""], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: ReactiveFormsModule }] }); }
1207
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtRenderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1208
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.7", type: XtRenderComponent, isStandalone: true, selector: "xt-render", inputs: { componentType: { classPropertyName: "componentType", publicName: "componentType", isSignal: true, isRequired: false, transformFunction: null }, displayMode: { classPropertyName: "displayMode", publicName: "displayMode", isSignal: true, isRequired: true, transformFunction: null }, valueType: { classPropertyName: "valueType", publicName: "valueType", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: false, transformFunction: null }, subName: { classPropertyName: "subName", publicName: "subName", isSignal: true, isRequired: false, transformFunction: null }, inputs: { classPropertyName: "inputs", publicName: "inputs", isSignal: true, isRequired: false, transformFunction: null }, models: { classPropertyName: "models", publicName: "models", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", outputs: "outputs" }, viewQueries: [{ propertyName: "outlet", first: true, predicate: NgComponentOutlet, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *ngComponentOutlet=\"type(); inputs: {context:context (), models:models()}\" />\n", styles: [""], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: ReactiveFormsModule }] }); }
1030
1209
  }
1031
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtRenderComponent, decorators: [{
1210
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtRenderComponent, decorators: [{
1032
1211
  type: Component,
1033
1212
  args: [{ selector: 'xt-render', standalone: true, imports: [
1034
1213
  NgComponentOutlet,
1035
1214
  ReactiveFormsModule
1036
- ], template: "<ng-container *ngComponentOutlet=\"type(); inputs: {context:context ()}\" />\n" }]
1037
- }], ctorParameters: () => [], propDecorators: { componentType: [{ type: i0.Input, args: [{ isSignal: true, alias: "componentType", required: false }] }], displayMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayMode", required: true }] }], valueType: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueType", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], formGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "formGroup", required: false }] }], subName: [{ type: i0.Input, args: [{ isSignal: true, alias: "subName", required: false }] }], inputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputs", required: false }] }], outputs: [{ type: i0.Output, args: ["outputs"] }], outlet: [{ type: i0.ViewChild, args: [i0.forwardRef(() => NgComponentOutlet), { isSignal: true }] }] } });
1215
+ ], template: "<ng-container *ngComponentOutlet=\"type(); inputs: {context:context (), models:models()}\" />\n" }]
1216
+ }], ctorParameters: () => [], propDecorators: { componentType: [{ type: i0.Input, args: [{ isSignal: true, alias: "componentType", required: false }] }], displayMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayMode", required: true }] }], valueType: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueType", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], formGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "formGroup", required: false }] }], subName: [{ type: i0.Input, args: [{ isSignal: true, alias: "subName", required: false }] }], inputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputs", required: false }] }], outputs: [{ type: i0.Output, args: ["outputs"] }], models: [{ type: i0.Input, args: [{ isSignal: true, alias: "models", required: false }] }], outlet: [{ type: i0.ViewChild, args: [i0.forwardRef(() => NgComponentOutlet), { isSignal: true }] }] } });
1038
1217
 
1039
1218
  /**
1040
1219
  * Dynamically render a component that will display the given subValue.
@@ -1042,20 +1221,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
1042
1221
  */
1043
1222
  class XtRenderSubComponent {
1044
1223
  constructor() {
1045
- this.context = input.required(...(ngDevMode ? [{ debugName: "context" }] : []));
1046
- this.componentType = input(...(ngDevMode ? [undefined, { debugName: "componentType" }] : []));
1224
+ /** The context containing display mode, form group, and value for the sub-component */
1225
+ this.context = input.required(...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
1226
+ /** Optional explicit component type to render */
1227
+ this.componentType = input(...(ngDevMode ? [undefined, { debugName: "componentType" }] : /* istanbul ignore next */ []));
1228
+ /** Object holding the output emitters from the rendered sub-component */
1047
1229
  this.outputsObject = new XtBaseOutput();
1048
- this.inputs = input(...(ngDevMode ? [undefined, { debugName: "inputs" }] : []));
1230
+ /** Inputs to pass through to the rendered sub-component */
1231
+ this.inputs = input(...(ngDevMode ? [undefined, { debugName: "inputs" }] : /* istanbul ignore next */ []));
1232
+ /** Emits the outputs from the rendered sub-component */
1049
1233
  this.outputs = output();
1234
+ /** Model signals to pass through to the rendered sub-component */
1235
+ this.models = input(...(ngDevMode ? [undefined, { debugName: "models" }] : /* istanbul ignore next */ []));
1236
+ /** Reference to the NgComponentOutlet used to dynamically render the sub-component */
1050
1237
  this.outlet = viewChild.required(NgComponentOutlet);
1238
+ /** Injected resolver service for finding the best component */
1051
1239
  this.resolverService = inject(XtResolverService);
1240
+ /** Computed context that resolves references (if any) */
1052
1241
  this.realContext = computed(() => {
1053
1242
  let ret = this.context();
1054
1243
  /*if ((ret.isReference()) && (ret.referencedContext!=null)) {
1055
1244
  ret = ret.referencedContext;
1056
1245
  }*/
1057
1246
  return ret;
1058
- }, ...(ngDevMode ? [{ debugName: "realContext" }] : []));
1247
+ }, ...(ngDevMode ? [{ debugName: "realContext" }] : /* istanbul ignore next */ []));
1248
+ /** Computed component type to render, resolved from the context if not explicitly set */
1059
1249
  this.type = computed(() => {
1060
1250
  //console.debug("Calculating type in XtRenderSubComponent");
1061
1251
  let type = this.componentType();
@@ -1069,7 +1259,7 @@ class XtRenderSubComponent {
1069
1259
  type = compFound.componentClass;
1070
1260
  }
1071
1261
  return type ?? null;
1072
- }, ...(ngDevMode ? [{ debugName: "type" }] : []));
1262
+ }, ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
1073
1263
  }
1074
1264
  /**
1075
1265
  * Transfers the input and outputs from the host to the rendered component
@@ -1093,17 +1283,18 @@ class XtRenderSubComponent {
1093
1283
  }
1094
1284
  }
1095
1285
  }
1096
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtRenderSubComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1097
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.9", type: XtRenderSubComponent, isStandalone: true, selector: "xt-render-sub", inputs: { context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: true, transformFunction: null }, componentType: { classPropertyName: "componentType", publicName: "componentType", isSignal: true, isRequired: false, transformFunction: null }, inputs: { classPropertyName: "inputs", publicName: "inputs", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { outputs: "outputs" }, viewQueries: [{ propertyName: "outlet", first: true, predicate: NgComponentOutlet, descendants: true, isSignal: true }], ngImport: i0, template: "{{componentType()}}\n<ng-container *ngComponentOutlet=\"type(); inputs: {context:realContext ()}\" />\n", styles: [""], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: ReactiveFormsModule }] }); }
1286
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtRenderSubComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1287
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.7", type: XtRenderSubComponent, isStandalone: true, selector: "xt-render-sub", inputs: { context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: true, transformFunction: null }, componentType: { classPropertyName: "componentType", publicName: "componentType", isSignal: true, isRequired: false, transformFunction: null }, inputs: { classPropertyName: "inputs", publicName: "inputs", isSignal: true, isRequired: false, transformFunction: null }, models: { classPropertyName: "models", publicName: "models", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { outputs: "outputs" }, viewQueries: [{ propertyName: "outlet", first: true, predicate: NgComponentOutlet, descendants: true, isSignal: true }], ngImport: i0, template: "{{componentType()}}\n<ng-container *ngComponentOutlet=\"type(); inputs: {context:realContext (), models:models()}\" />\n", styles: [""], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: ReactiveFormsModule }] }); }
1098
1288
  }
1099
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtRenderSubComponent, decorators: [{
1289
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtRenderSubComponent, decorators: [{
1100
1290
  type: Component,
1101
1291
  args: [{ selector: 'xt-render-sub', standalone: true, imports: [
1102
1292
  NgComponentOutlet,
1103
1293
  ReactiveFormsModule
1104
- ], template: "{{componentType()}}\n<ng-container *ngComponentOutlet=\"type(); inputs: {context:realContext ()}\" />\n" }]
1105
- }], propDecorators: { context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: true }] }], componentType: [{ type: i0.Input, args: [{ isSignal: true, alias: "componentType", required: false }] }], inputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputs", required: false }] }], outputs: [{ type: i0.Output, args: ["outputs"] }], outlet: [{ type: i0.ViewChild, args: [i0.forwardRef(() => NgComponentOutlet), { isSignal: true }] }] } });
1294
+ ], template: "{{componentType()}}\n<ng-container *ngComponentOutlet=\"type(); inputs: {context:realContext (), models:models()}\" />\n" }]
1295
+ }], propDecorators: { context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: true }] }], componentType: [{ type: i0.Input, args: [{ isSignal: true, alias: "componentType", required: false }] }], inputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputs", required: false }] }], outputs: [{ type: i0.Output, args: ["outputs"] }], models: [{ type: i0.Input, args: [{ isSignal: true, alias: "models", required: false }] }], outlet: [{ type: i0.ViewChild, args: [i0.forwardRef(() => NgComponentOutlet), { isSignal: true }] }] } });
1106
1296
 
1297
+ /** Default implementation of XtComponentInput */
1107
1298
  class XtBaseInput {
1108
1299
  }
1109
1300
 
@@ -1113,59 +1304,77 @@ class XtBaseInput {
1113
1304
  */
1114
1305
  class XtSimpleComponent {
1115
1306
  constructor() {
1116
- this.context = input.required(...(ngDevMode ? [{ debugName: "context" }] : []));
1307
+ /** Required input signal providing the XtContext for this component. */
1308
+ this.context = input.required(...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
1309
+ /** Object holding the base output definitions for this component. */
1117
1310
  this.outputsObject = new XtBaseOutput();
1311
+ /** Object holding the base input definitions for this component. */
1118
1312
  this.inputsObject = new XtBaseInput();
1313
+ /** Output emitter that publishes the component's output map. */
1119
1314
  this.outputs = output();
1315
+ /** Input signal accepting model bindings for two-way data flow. */
1316
+ this.models = input(...(ngDevMode ? [undefined, { debugName: "models" }] : /* istanbul ignore next */ []));
1317
+ /** Computed signal indicating whether this component is inside a reactive form. */
1120
1318
  this.isInForm = computed(() => {
1121
1319
  return this.context()?.isInForm() ?? false;
1122
- }, ...(ngDevMode ? [{ debugName: "isInForm" }] : []));
1320
+ }, ...(ngDevMode ? [{ debugName: "isInForm" }] : /* istanbul ignore next */ []));
1321
+ /** Computed signal returning the form control name, if any. */
1123
1322
  this.formControlNameIfAny = computed(() => {
1124
1323
  return this.context()?.subName;
1125
- }, ...(ngDevMode ? [{ debugName: "formControlNameIfAny" }] : []));
1324
+ }, ...(ngDevMode ? [{ debugName: "formControlNameIfAny" }] : /* istanbul ignore next */ []));
1325
+ /** Computed signal returning the form group if one exists, or undefined. */
1126
1326
  this.formGroupIfAny = computed(() => {
1127
1327
  return this.context()?.formGroup();
1128
- }, ...(ngDevMode ? [{ debugName: "formGroupIfAny" }] : []));
1328
+ }, ...(ngDevMode ? [{ debugName: "formGroupIfAny" }] : /* istanbul ignore next */ []));
1329
+ /** Computed signal returning the form group, throwing if none exists. */
1129
1330
  this.formGroup = computed(() => {
1130
1331
  const ret = this.context()?.formGroup();
1131
1332
  if (ret == null)
1132
1333
  throw new Error('No form groups in this component of type ' + this.componentDescriptor());
1133
1334
  return ret;
1134
- }, ...(ngDevMode ? [{ debugName: "formGroup" }] : []));
1335
+ }, ...(ngDevMode ? [{ debugName: "formGroup" }] : /* istanbul ignore next */ []));
1135
1336
  /**
1136
1337
  * Returns the component form name, which is for now the subName
1137
1338
  */
1138
1339
  this.componentNameInForm = computed(() => {
1139
1340
  return this.safelyGetSubName();
1140
- }, ...(ngDevMode ? [{ debugName: "componentNameInForm" }] : []));
1341
+ }, ...(ngDevMode ? [{ debugName: "componentNameInForm" }] : /* istanbul ignore next */ []));
1342
+ /** Computed signal that safely returns the sub-name, throwing if it is null. */
1141
1343
  this.safelyGetSubName = computed(() => {
1142
1344
  const ret = this.context()?.subName;
1143
1345
  if (ret == null)
1144
1346
  throw new Error('This component has no name in the form ' + this.componentDescriptor());
1145
1347
  return ret;
1146
- }, ...(ngDevMode ? [{ debugName: "safelyGetSubName" }] : []));
1348
+ }, ...(ngDevMode ? [{ debugName: "safelyGetSubName" }] : /* istanbul ignore next */ []));
1147
1349
  /**
1148
- * Returns the form control name and create a form control behind the scene
1350
+ * Computed signal returning the form control name for this component.
1149
1351
  */
1150
1352
  this.formControlName = computed(() => {
1151
1353
  const ret = this.safelyGetSubName();
1152
1354
  //this.manageFormControl<any>(ret); // Don't create anything at this point. It's a computed value.
1153
1355
  return ret;
1154
- }, ...(ngDevMode ? [{ debugName: "formControlName" }] : []));
1356
+ }, ...(ngDevMode ? [{ debugName: "formControlName" }] : /* istanbul ignore next */ []));
1357
+ /** Computed signal that retrieves the existing AbstractControl for this component's sub-name. */
1155
1358
  this.formControl = computed(() => {
1156
1359
  const subName = this.safelyGetSubName();
1157
1360
  const formControl = this.manageFormControl(subName, false);
1158
1361
  if (formControl == null)
1159
1362
  throw new Error("Calling formControl for subName " + subName + " when none exist.");
1160
1363
  return formControl;
1161
- }, ...(ngDevMode ? [{ debugName: "formControl" }] : []));
1364
+ }, ...(ngDevMode ? [{ debugName: "formControl" }] : /* istanbul ignore next */ []));
1365
+ /** Computed signal exposing the raw value from the context. */
1162
1366
  this.getValue = computed(() => {
1163
1367
  return this.context().value();
1164
- }, ...(ngDevMode ? [{ debugName: "getValue" }] : []));
1368
+ }, ...(ngDevMode ? [{ debugName: "getValue" }] : /* istanbul ignore next */ []));
1369
+ /** Computed signal exposing the display value from the context. */
1165
1370
  this.displayValue = computed(() => {
1166
1371
  return this.context().displayValue();
1167
- }, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
1372
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
1168
1373
  }
1374
+ /**
1375
+ * Angular lifecycle hook. Sets up input/output bindings and emits outputs
1376
+ * if any output objects have been defined.
1377
+ */
1169
1378
  ngOnInit() {
1170
1379
  this.setupInputOutput();
1171
1380
  if (Object.keys(this.outputsObject).length > 0) {
@@ -1173,6 +1382,13 @@ class XtSimpleComponent {
1173
1382
  this.outputs.emit(this.outputsObject);
1174
1383
  }
1175
1384
  }
1385
+ /**
1386
+ * Manages a form control within the component's form group, optionally creating it if it does not exist.
1387
+ * Can be safely called even when no form group is available.
1388
+ * @param ctrlName - The name of the form control
1389
+ * @param create - Whether to create the control if it does not exist (default true)
1390
+ * @returns The AbstractControl, or undefined if no form group is available
1391
+ */
1176
1392
  manageFormControl(ctrlName, create = true) {
1177
1393
  const formGroup = this.formGroupIfAny();
1178
1394
  if (formGroup == null) {
@@ -1189,6 +1405,10 @@ class XtSimpleComponent {
1189
1405
  return ctrl ?? undefined;
1190
1406
  }
1191
1407
  }
1408
+ /**
1409
+ * Returns a human-readable descriptor for this component.
1410
+ * @returns A string describing the component type and context
1411
+ */
1192
1412
  componentDescriptor() {
1193
1413
  return "Component with type " + this.constructor.name + " with context " + this.context().toString();
1194
1414
  }
@@ -1199,22 +1419,33 @@ class XtSimpleComponent {
1199
1419
  setupInputOutput() {
1200
1420
  // Nothing to do here
1201
1421
  }
1202
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtSimpleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1203
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.9", type: XtSimpleComponent, isStandalone: true, selector: "ng-component", inputs: { context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { outputs: "outputs" }, ngImport: i0, template: '', isInline: true }); }
1422
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtSimpleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1423
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.7", type: XtSimpleComponent, isStandalone: true, selector: "ng-component", inputs: { context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: true, transformFunction: null }, models: { classPropertyName: "models", publicName: "models", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { outputs: "outputs" }, ngImport: i0, template: '', isInline: true }); }
1204
1424
  }
1205
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtSimpleComponent, decorators: [{
1425
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtSimpleComponent, decorators: [{
1206
1426
  type: Component,
1207
1427
  args: [{
1208
1428
  standalone: true,
1209
1429
  imports: [],
1210
1430
  template: ''
1211
1431
  }]
1212
- }], ctorParameters: () => [], propDecorators: { context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: true }] }], outputs: [{ type: i0.Output, args: ["outputs"] }] } });
1432
+ }], ctorParameters: () => [], propDecorators: { context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: true }] }], outputs: [{ type: i0.Output, args: ["outputs"] }], models: [{ type: i0.Input, args: [{ isSignal: true, alias: "models", required: false }] }] } });
1213
1433
 
1434
+ /**
1435
+ * A composite XtComponent that manages a form group for its sub-elements.
1436
+ * Extends XtSimpleComponent to provide context-based form group management
1437
+ * and sub-context resolution for nested components.
1438
+ * Selector: not directly used (abstract base via template).
1439
+ */
1214
1440
  class XtCompositeComponent extends XtSimpleComponent {
1215
1441
  constructor() {
1216
1442
  super(...arguments);
1443
+ /** Injected service for resolving components and type information. */
1217
1444
  this.resolverService = inject(XtResolverService);
1445
+ /**
1446
+ * Computes the local form group for this composite, creating one from the parent form group if it does not exist.
1447
+ * Overrides the base implementation to manage a dedicated form group for sub-elements.
1448
+ */
1218
1449
  this.formGroupIfAny = computed(() => {
1219
1450
  const context = this.context();
1220
1451
  if (context == null)
@@ -1231,7 +1462,7 @@ class XtCompositeComponent extends XtSimpleComponent {
1231
1462
  ret = context.localFormGroup;
1232
1463
  }
1233
1464
  return ret;
1234
- }, ...(ngDevMode ? [{ debugName: "formGroupIfAny" }] : []));
1465
+ }, ...(ngDevMode ? [{ debugName: "formGroupIfAny" }] : /* istanbul ignore next */ []));
1235
1466
  /**
1236
1467
  * We need to create a new form group to manage the sub elements.
1237
1468
  */
@@ -1240,55 +1471,85 @@ class XtCompositeComponent extends XtSimpleComponent {
1240
1471
  if (ret == null)
1241
1472
  throw new Error('No form groups in this component of type ' + this.componentDescriptor());
1242
1473
  return ret;
1243
- }, ...(ngDevMode ? [{ debugName: "formGroup" }] : []));
1474
+ }, ...(ngDevMode ? [{ debugName: "formGroup" }] : /* istanbul ignore next */ []));
1244
1475
  }
1245
1476
  /**
1246
- * Helper function to calculate the sub context
1247
- * @param subName
1248
- * @param subType
1477
+ * Helper function to calculate the sub context for a named child element.
1478
+ * @param subName - The name of the sub-element
1479
+ * @param subType - Optional type hint for the sub-element
1480
+ * @returns The sub-context for the given child
1249
1481
  */
1250
1482
  subContext(subName, subType) {
1251
1483
  this.formGroupIfAny(); // Ensure the context is properly initialized
1252
1484
  return this.context().subContext(subName, subType, this.resolverService.typeResolver);
1253
1485
  }
1254
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtCompositeComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1255
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.9", type: XtCompositeComponent, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: '', isInline: true, styles: [""] }); }
1486
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtCompositeComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1487
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: XtCompositeComponent, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: '', isInline: true, styles: [""] }); }
1256
1488
  }
1257
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtCompositeComponent, decorators: [{
1489
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtCompositeComponent, decorators: [{
1258
1490
  type: Component,
1259
1491
  args: [{ standalone: true, imports: [], template: '' }]
1260
1492
  }] });
1261
1493
 
1494
+ /** Default implementation of XtComponentModel */
1495
+ class XtBaseModel {
1496
+ }
1497
+
1498
+ /** Service for handling error and warning messages throughout the application */
1262
1499
  class XtMessageHandler {
1500
+ /**
1501
+ * Handles an error occurrence by logging it to the console
1502
+ * @param error - The error object
1503
+ * @param errorMsg - Optional custom error message
1504
+ */
1263
1505
  errorOccurred(error, errorMsg) {
1264
1506
  console.error(errorMsg, error);
1265
1507
  }
1508
+ /**
1509
+ * Handles a warning occurrence by logging it to the console
1510
+ * @param warningMsg - Optional warning message
1511
+ */
1266
1512
  warningOccurred(warningMsg) {
1267
1513
  console.warn(warningMsg);
1268
1514
  }
1269
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtMessageHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1270
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtMessageHandler, providedIn: 'root' }); }
1515
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtMessageHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1516
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtMessageHandler, providedIn: 'root' }); }
1271
1517
  }
1272
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtMessageHandler, decorators: [{
1518
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: XtMessageHandler, decorators: [{
1273
1519
  type: Injectable,
1274
1520
  args: [{
1275
1521
  providedIn: 'root'
1276
1522
  }]
1277
1523
  }] });
1278
1524
 
1525
+ /**
1526
+ * Attaches a value to a form group by creating either a FormControl or nested FormGroup based on the value type
1527
+ * @param formGroup - The form group to attach to
1528
+ * @param controlName - The control name within the form group
1529
+ * @param value - The value to attach
1530
+ * @param valueType - Optional type identifier for type resolution
1531
+ * @param resolver - Optional type resolver for determining if the value is primitive
1532
+ */
1279
1533
  function attachToFormGroup(formGroup, controlName, value, valueType, resolver) {
1280
1534
  // If it's a single value, just create the control
1281
1535
  if (((value != null) && (isPrimitive(value))
1282
1536
  || (resolver?.isPrimitiveType(valueType)))) {
1283
1537
  const simpleControl = new FormControl(value);
1284
- formGroup.addControl(controlName, simpleControl);
1538
+ formGroup.setControl(controlName, simpleControl);
1285
1539
  }
1286
1540
  else {
1287
1541
  const complexGroup = new FormGroup({});
1288
1542
  updateFormGroupWithValue(complexGroup, value, valueType, resolver);
1289
- formGroup.addControl(controlName, complexGroup);
1543
+ formGroup.setControl(controlName, complexGroup);
1290
1544
  }
1291
1545
  }
1546
+ /**
1547
+ * Updates a form group with the properties from a value object, adding, updating, or removing controls as needed
1548
+ * @param formGroup - The form group to update
1549
+ * @param value - The value object containing the properties to sync
1550
+ * @param valueType - Optional type identifier for type resolution
1551
+ * @param resolver - Optional type resolver for determining property types
1552
+ */
1292
1553
  function updateFormGroupWithValue(formGroup, value, valueType, resolver) {
1293
1554
  const toDelete = new Set(Object.keys(formGroup.controls));
1294
1555
  // We merge the properties of the value if any, with the properties of the model
@@ -1353,7 +1614,27 @@ function updateFormGroupWithValue(formGroup, value, valueType, resolver) {
1353
1614
  }
1354
1615
  }
1355
1616
 
1617
+ /**
1618
+ * Helper class to ease unit testing
1619
+ */
1356
1620
  class XtUnitTestHelper {
1621
+ /* public static registerComponent<T> (name:string, compClass:T, type:string){
1622
+ this.resolverService.pluginRegistry.registerComponent({
1623
+ componentName:name,
1624
+ componentClass: compClass,
1625
+ typesHandled: [type]
1626
+ });
1627
+ }
1628
+ */
1629
+ /**
1630
+ * Asynchronously wait for test function to return true. By default try 20 times with a delay of 50ms
1631
+ * @param test
1632
+ * @param tries
1633
+ * @param timer
1634
+ */
1635
+ static async waitFor(test, tries = 20, timer = 50) {
1636
+ await lastValueFrom(range(0, tries).pipe(delay(timer), find(test)));
1637
+ }
1357
1638
  }
1358
1639
 
1359
1640
  /**
@@ -1362,14 +1643,17 @@ class XtUnitTestHelper {
1362
1643
  */
1363
1644
  class HostTestSimpleComponent {
1364
1645
  constructor() {
1365
- this.type = input.required(...(ngDevMode ? [{ debugName: "type" }] : []));
1366
- this.displayMode = input('FULL_VIEW', ...(ngDevMode ? [{ debugName: "displayMode" }] : []));
1367
- this.value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : []));
1368
- }
1369
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: HostTestSimpleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1370
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.9", type: HostTestSimpleComponent, isStandalone: true, selector: "test-host", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null }, displayMode: { classPropertyName: "displayMode", publicName: "displayMode", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '<h1>Test Simple Component</h1> <xt-render [componentType]="type()" [displayMode]="displayMode()" [value]="value()" ></xt-render> ', isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: XtRenderComponent, selector: "xt-render", inputs: ["componentType", "displayMode", "valueType", "value", "formGroup", "subName", "inputs"], outputs: ["valueChange", "outputs"] }] }); }
1646
+ /** Required input for the component type to render. */
1647
+ this.type = input.required(...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
1648
+ /** Input for the display mode. Defaults to FULL_VIEW. */
1649
+ this.displayMode = input('FULL_VIEW', ...(ngDevMode ? [{ debugName: "displayMode" }] : /* istanbul ignore next */ []));
1650
+ /** Input for the value to pass to the rendered component. */
1651
+ this.value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
1652
+ }
1653
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: HostTestSimpleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1654
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.7", type: HostTestSimpleComponent, isStandalone: true, selector: "test-host", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null }, displayMode: { classPropertyName: "displayMode", publicName: "displayMode", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '<h1>Test Simple Component</h1> <xt-render [componentType]="type()" [displayMode]="displayMode()" [value]="value()" ></xt-render> ', isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: XtRenderComponent, selector: "xt-render", inputs: ["componentType", "displayMode", "valueType", "value", "formGroup", "subName", "inputs", "models"], outputs: ["valueChange", "outputs"] }] }); }
1371
1655
  }
1372
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: HostTestSimpleComponent, decorators: [{
1656
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: HostTestSimpleComponent, decorators: [{
1373
1657
  type: Component,
1374
1658
  args: [{
1375
1659
  selector: 'test-host',
@@ -1385,48 +1669,61 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
1385
1669
  */
1386
1670
  class HostTestFormComponent {
1387
1671
  constructor() {
1672
+ /** Injected FormBuilder for creating form groups. */
1388
1673
  this.builder = inject(FormBuilder);
1389
- this.type = input.required(...(ngDevMode ? [{ debugName: "type" }] : []));
1390
- this.controlName = input.required(...(ngDevMode ? [{ debugName: "controlName" }] : []));
1391
- // You can send the description to be used in a FormBuilder to create the formgroup;
1392
- this.formDescription = input({}, ...(ngDevMode ? [{ debugName: "formDescription" }] : []));
1393
- // Or set the FormGroup directly
1394
- this.formGroup = input(...(ngDevMode ? [undefined, { debugName: "formGroup" }] : []));
1395
- // parentFormGroup = this.builder.group<{[keys:string]: AbstractControl}>({});
1396
- this.createdFormGroup = null;
1397
- }
1398
- computedFormGroup() {
1399
- if (this.createdFormGroup == null) {
1400
- const formGroup = this.formGroup();
1401
- this.createdFormGroup = formGroup ?? generateFormGroup(this.formDescription());
1402
- }
1403
- // this.parentFormGroup.addControl(this.controlName()??HostTestTypedFormComponent.CONTROL_NAME, this.createdFormGroup);
1404
- return this.createdFormGroup;
1674
+ /** Required input for the component type to render. */
1675
+ this.type = input.required(...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
1676
+ /** Required input for the form control name. */
1677
+ this.controlName = input.required(...(ngDevMode ? [{ debugName: "controlName" }] : /* istanbul ignore next */ []));
1678
+ /** Input for the form description object used to generate the form group. */
1679
+ this.formDescription = input({}, ...(ngDevMode ? [{ debugName: "formDescription" }] : /* istanbul ignore next */ []));
1680
+ /** Input to provide an existing FormGroup directly. */
1681
+ this.formGroup = input(...(ngDevMode ? [undefined, { debugName: "formGroup" }] : /* istanbul ignore next */ []));
1682
+ /** The parent FormGroup that contains the component's form group. */
1683
+ this.parentFormGroup = this.builder.group({});
1684
+ /** Computed signal returning the created form group. */
1685
+ this.createdFormGroup = computed(() => {
1686
+ return this.computeFormGroup();
1687
+ }, ...(ngDevMode ? [{ debugName: "createdFormGroup" }] : /* istanbul ignore next */ []));
1688
+ }
1689
+ /**
1690
+ * Computes the form group from the input, either using a provided form group or generating one from the form description.
1691
+ * @returns The created FormGroup
1692
+ */
1693
+ computeFormGroup() {
1694
+ const formGroup = this.formGroup();
1695
+ let createdFormGroup = formGroup ?? generateFormGroup(this.formDescription());
1696
+ this.parentFormGroup.addControl(this.controlName() ?? HostTestTypedFormComponent.CONTROL_NAME, createdFormGroup);
1697
+ return createdFormGroup;
1405
1698
  }
1699
+ /**
1700
+ * Patches the component's form control with a new value.
1701
+ * @param newVal - The value to patch
1702
+ */
1406
1703
  patchValue(newVal) {
1407
1704
  const patch = {};
1408
1705
  patch[this.controlName()] = newVal;
1409
- if (this.createdFormGroup != null)
1410
- this.createdFormGroup.patchValue(patch);
1411
- else
1412
- throw new Error("FormGroup not yet created. Did you set formGroup or formDescription property ?");
1706
+ const createdFormGroup = this.createdFormGroup();
1707
+ createdFormGroup.patchValue(patch);
1413
1708
  }
1709
+ /**
1710
+ * Retrieves the current value from the component's form control.
1711
+ * @returns The current form value
1712
+ */
1414
1713
  retrieveValue() {
1415
- if (this.createdFormGroup != null)
1416
- return this.createdFormGroup.value[this.controlName()];
1417
- else
1418
- throw new Error("FormGroup not yet created. Did you set formGroup or formDescription property ?");
1714
+ const createdFormGroup = this.createdFormGroup();
1715
+ return createdFormGroup.value[this.controlName()];
1419
1716
  }
1420
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: HostTestFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1421
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.9", type: HostTestFormComponent, isStandalone: true, selector: "test-form-host", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: true, transformFunction: null }, formDescription: { classPropertyName: "formDescription", publicName: "formDescription", isSignal: true, isRequired: false, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '<h1>Test Form Component</h1> <form [formGroup]="computedFormGroup()"> <xt-render [componentType]="type()" displayMode="FULL_EDITABLE" [subName]="controlName()" [formGroup]="computedFormGroup()"></xt-render></form>', isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: XtRenderComponent, selector: "xt-render", inputs: ["componentType", "displayMode", "valueType", "value", "formGroup", "subName", "inputs"], outputs: ["valueChange", "outputs"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] }); }
1717
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: HostTestFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1718
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.7", type: HostTestFormComponent, isStandalone: true, selector: "test-form-host", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: true, transformFunction: null }, formDescription: { classPropertyName: "formDescription", publicName: "formDescription", isSignal: true, isRequired: false, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '<h1>Test Form Component</h1> <form [formGroup]="parentFormGroup"> <xt-render [componentType]="type()" displayMode="FULL_EDITABLE" [subName]="controlName()" [formGroup]="createdFormGroup()"></xt-render></form>', isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: XtRenderComponent, selector: "xt-render", inputs: ["componentType", "displayMode", "valueType", "value", "formGroup", "subName", "inputs", "models"], outputs: ["valueChange", "outputs"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] }); }
1422
1719
  }
1423
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: HostTestFormComponent, decorators: [{
1720
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: HostTestFormComponent, decorators: [{
1424
1721
  type: Component,
1425
1722
  args: [{
1426
1723
  selector: 'test-form-host',
1427
1724
  standalone: true,
1428
1725
  imports: [CommonModule, XtRenderComponent, ReactiveFormsModule],
1429
- template: '<h1>Test Form Component</h1> <form [formGroup]="computedFormGroup()"> <xt-render [componentType]="type()" displayMode="FULL_EDITABLE" [subName]="controlName()" [formGroup]="computedFormGroup()"></xt-render></form>'
1726
+ template: '<h1>Test Form Component</h1> <form [formGroup]="parentFormGroup"> <xt-render [componentType]="type()" displayMode="FULL_EDITABLE" [subName]="controlName()" [formGroup]="createdFormGroup()"></xt-render></form>'
1430
1727
  }]
1431
1728
  }], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: true }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", required: true }] }], formDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "formDescription", required: false }] }], formGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "formGroup", required: false }] }] } });
1432
1729
  /**
@@ -1435,20 +1732,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
1435
1732
  */
1436
1733
  class HostTestTypedComponent {
1437
1734
  constructor() {
1438
- this.displayMode = input('FULL_VIEW', ...(ngDevMode ? [{ debugName: "displayMode" }] : []));
1439
- this.value = input(...(ngDevMode ? [undefined, { debugName: "value" }] : []));
1440
- this.valueType = input(...(ngDevMode ? [undefined, { debugName: "valueType" }] : []));
1735
+ /** Input for the display mode. Defaults to FULL_VIEW. */
1736
+ this.displayMode = input('FULL_VIEW', ...(ngDevMode ? [{ debugName: "displayMode" }] : /* istanbul ignore next */ []));
1737
+ /** Input for the value to display. */
1738
+ this.value = input(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
1739
+ /** Input for the value type string used for context creation. */
1740
+ this.valueType = input(...(ngDevMode ? [undefined, { debugName: "valueType" }] : /* istanbul ignore next */ []));
1741
+ /** Computed signal that creates the XtBaseContext from the input values. */
1441
1742
  this.context = computed(() => {
1442
1743
  const ret = new XtBaseContext(this.displayMode());
1443
1744
  ret.valueType = this.valueType();
1444
1745
  ret.setDisplayValue(this.value());
1445
1746
  return ret;
1446
- }, ...(ngDevMode ? [{ debugName: "context" }] : []));
1747
+ }, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
1447
1748
  }
1448
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: HostTestTypedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1449
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.9", type: HostTestTypedComponent, isStandalone: true, selector: "test-typed-host", inputs: { displayMode: { classPropertyName: "displayMode", publicName: "displayMode", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, valueType: { classPropertyName: "valueType", publicName: "valueType", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '<h1>Test Typed Component</h1> <xt-render-sub [context]="context()" ></xt-render-sub> ', isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: XtRenderSubComponent, selector: "xt-render-sub", inputs: ["context", "componentType", "inputs"], outputs: ["outputs"] }] }); }
1749
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: HostTestTypedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1750
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.7", type: HostTestTypedComponent, isStandalone: true, selector: "test-typed-host", inputs: { displayMode: { classPropertyName: "displayMode", publicName: "displayMode", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, valueType: { classPropertyName: "valueType", publicName: "valueType", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '<h1>Test Typed Component</h1> <xt-render-sub [context]="context()" ></xt-render-sub> ', isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: XtRenderSubComponent, selector: "xt-render-sub", inputs: ["context", "componentType", "inputs", "models"], outputs: ["outputs"] }] }); }
1450
1751
  }
1451
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: HostTestTypedComponent, decorators: [{
1752
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: HostTestTypedComponent, decorators: [{
1452
1753
  type: Component,
1453
1754
  args: [{
1454
1755
  selector: 'test-typed-host',
@@ -1464,64 +1765,84 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
1464
1765
  */
1465
1766
  class HostTestTypedFormComponent {
1466
1767
  constructor() {
1768
+ /** Injected FormBuilder for creating form groups. */
1467
1769
  this.builder = inject(FormBuilder);
1770
+ /** Injected resolver service for type resolution. */
1468
1771
  this.resolver = inject(XtResolverService);
1469
- this.valueType = input(...(ngDevMode ? [undefined, { debugName: "valueType" }] : []));
1470
- this.controlName = input(...(ngDevMode ? [undefined, { debugName: "controlName" }] : []));
1471
- // You can send the description to be used in a FormBuilder to create the formgroup;
1472
- this.formDescription = input(null, ...(ngDevMode ? [{ debugName: "formDescription" }] : []));
1473
- // Or set the FormGroup directly
1474
- this.formGroup = input(...(ngDevMode ? [undefined, { debugName: "formGroup" }] : []));
1772
+ /** Input for the value type string. */
1773
+ this.valueType = input(...(ngDevMode ? [undefined, { debugName: "valueType" }] : /* istanbul ignore next */ []));
1774
+ /** Input for the form control name. */
1775
+ this.controlName = input(...(ngDevMode ? [undefined, { debugName: "controlName" }] : /* istanbul ignore next */ []));
1776
+ /** Input for the form description used to generate the form group. */
1777
+ this.formDescription = input(null, ...(ngDevMode ? [{ debugName: "formDescription" }] : /* istanbul ignore next */ []));
1778
+ /** Input to provide an existing FormGroup directly. */
1779
+ this.formGroup = input(...(ngDevMode ? [undefined, { debugName: "formGroup" }] : /* istanbul ignore next */ []));
1780
+ /** The parent FormGroup that contains the component's form group. */
1475
1781
  this.parentFormGroup = this.builder.group({});
1476
- this.createdFormGroup = null;
1782
+ /** Computed signal returning the created form group. */
1783
+ this.createdFormGroup = computed(() => {
1784
+ return this.computeFormGroup();
1785
+ }, ...(ngDevMode ? [{ debugName: "createdFormGroup" }] : /* istanbul ignore next */ []));
1786
+ /** Computed signal that builds the XtBaseContext from form group and control name. */
1787
+ this.subContext = computed(() => {
1788
+ const ctrlName = this.controlName();
1789
+ const createdFormGroup = this.createdFormGroup();
1790
+ let ret = null;
1791
+ if (ctrlName == null) {
1792
+ ret = new XtBaseContext('FULL_EDITABLE', HostTestTypedFormComponent.CONTROL_NAME, this.parentFormGroup);
1793
+ }
1794
+ else {
1795
+ ret = new XtBaseContext('FULL_EDITABLE', ctrlName, createdFormGroup);
1796
+ }
1797
+ ret.valueType = this.valueType();
1798
+ return ret;
1799
+ }, ...(ngDevMode ? [{ debugName: "subContext" }] : /* istanbul ignore next */ []));
1477
1800
  }
1801
+ /** Default control name used if none is provided. */
1478
1802
  static { this.CONTROL_NAME = 'ForTest'; }
1479
1803
  ngOnInit() {
1480
- this.computeFormGroup();
1481
1804
  }
1805
+ /**
1806
+ * Computes the form group from inputs, generating one from description or type if needed.
1807
+ * @returns The created FormGroup
1808
+ */
1482
1809
  computeFormGroup() {
1483
- if (this.createdFormGroup == null) {
1484
- const formGroup = this.formGroup();
1485
- if (this.formDescription() != null) {
1486
- this.createdFormGroup = formGroup ?? generateFormGroup(this.formDescription());
1487
- }
1488
- else {
1489
- this.createdFormGroup = this.builder.group({});
1490
- if (this.valueType() != null) {
1491
- updateFormGroupWithValue(this.createdFormGroup, {}, this.valueType(), this.resolver.typeResolver);
1492
- }
1493
- }
1494
- this.parentFormGroup.addControl(this.controlName() ?? HostTestTypedFormComponent.CONTROL_NAME, this.createdFormGroup);
1495
- }
1496
- return this.createdFormGroup;
1497
- }
1498
- ;
1499
- subContext() {
1500
- this.computeFormGroup(); // Make sure the subformgroups are created
1501
- const ctrlName = this.controlName();
1502
- let ret = null;
1503
- if (ctrlName == null) {
1504
- ret = new XtBaseContext('FULL_EDITABLE', HostTestTypedFormComponent.CONTROL_NAME, this.parentFormGroup);
1810
+ let createdFormGroup = null;
1811
+ const formGroup = this.formGroup();
1812
+ if (this.formDescription() != null) {
1813
+ createdFormGroup = formGroup ?? generateFormGroup(this.formDescription());
1505
1814
  }
1506
1815
  else {
1507
- ret = new XtBaseContext('FULL_EDITABLE', ctrlName, this.createdFormGroup);
1816
+ createdFormGroup = this.builder.group({});
1817
+ if (this.valueType() != null) {
1818
+ updateFormGroupWithValue(createdFormGroup, {}, this.valueType(), this.resolver.typeResolver);
1819
+ }
1508
1820
  }
1509
- ret.valueType = this.valueType();
1510
- return ret;
1821
+ this.parentFormGroup.addControl(this.controlName() ?? HostTestTypedFormComponent.CONTROL_NAME, createdFormGroup);
1822
+ return createdFormGroup;
1511
1823
  }
1512
- ;
1824
+ /**
1825
+ * Patches a form control with a new value.
1826
+ * @param controlName - The name of the control to patch
1827
+ * @param newVal - The value to set
1828
+ */
1513
1829
  patchValue(controlName, newVal) {
1514
1830
  const patch = {};
1515
1831
  patch[controlName] = newVal;
1516
- this.computeFormGroup().patchValue(patch);
1832
+ this.createdFormGroup().patchValue(patch);
1517
1833
  }
1834
+ /**
1835
+ * Retrieves the current value from a form control.
1836
+ * @param controlName - The name of the control to read
1837
+ * @returns The current form value
1838
+ */
1518
1839
  retrieveValue(controlName) {
1519
- return this.computeFormGroup().value[controlName];
1840
+ return this.createdFormGroup().value[controlName];
1520
1841
  }
1521
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: HostTestTypedFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1522
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.9", type: HostTestTypedFormComponent, isStandalone: true, selector: "test-typed-form-host", inputs: { valueType: { classPropertyName: "valueType", publicName: "valueType", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, formDescription: { classPropertyName: "formDescription", publicName: "formDescription", isSignal: true, isRequired: false, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '<h1>Test Typed Form Component</h1> <form [formGroup]="parentFormGroup"> <xt-render-sub [context]="subContext()"></xt-render-sub></form>', isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: XtRenderSubComponent, selector: "xt-render-sub", inputs: ["context", "componentType", "inputs"], outputs: ["outputs"] }] }); }
1842
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: HostTestTypedFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1843
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.7", type: HostTestTypedFormComponent, isStandalone: true, selector: "test-typed-form-host", inputs: { valueType: { classPropertyName: "valueType", publicName: "valueType", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, formDescription: { classPropertyName: "formDescription", publicName: "formDescription", isSignal: true, isRequired: false, transformFunction: null }, formGroup: { classPropertyName: "formGroup", publicName: "formGroup", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '<h1>Test Typed Form Component</h1> <form [formGroup]="parentFormGroup"> <xt-render-sub [context]="subContext()"></xt-render-sub></form>', isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: XtRenderSubComponent, selector: "xt-render-sub", inputs: ["context", "componentType", "inputs", "models"], outputs: ["outputs"] }] }); }
1523
1844
  }
1524
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: HostTestTypedFormComponent, decorators: [{
1845
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: HostTestTypedFormComponent, decorators: [{
1525
1846
  type: Component,
1526
1847
  args: [{
1527
1848
  selector: 'test-typed-form-host',
@@ -1530,12 +1851,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
1530
1851
  template: '<h1>Test Typed Form Component</h1> <form [formGroup]="parentFormGroup"> <xt-render-sub [context]="subContext()"></xt-render-sub></form>'
1531
1852
  }]
1532
1853
  }], propDecorators: { valueType: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueType", required: false }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", required: false }] }], formDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "formDescription", required: false }] }], formGroup: [{ type: i0.Input, args: [{ isSignal: true, alias: "formGroup", required: false }] }] } });
1854
+ /**
1855
+ * Generates a FormGroup from a form description object.
1856
+ * @param formDescription - The description object used to build the form group
1857
+ * @returns A FormGroup instance
1858
+ */
1533
1859
  function generateFormGroup(formDescription) {
1534
1860
  if (typeof formDescription != 'object') {
1535
1861
  throw new Error('Form Description should be an object of values');
1536
1862
  }
1537
1863
  return generateFormControl(formDescription);
1538
1864
  }
1865
+ /**
1866
+ * Recursively generates an AbstractControl from a form description value.
1867
+ * Handles null, arrays, objects (as FormGroup), and primitive values (as FormControl).
1868
+ * @param formDescription - The value to convert into a form control
1869
+ * @returns An AbstractControl instance
1870
+ */
1539
1871
  function generateFormControl(formDescription) {
1540
1872
  if (formDescription == null) {
1541
1873
  return new FormControl(formDescription);
@@ -1557,6 +1889,265 @@ function generateFormControl(formDescription) {
1557
1889
  return new FormControl(formDescription);
1558
1890
  }
1559
1891
 
1892
+ /**
1893
+ * A very light and not 100% compatible storemanager in case you are not using xt-store.
1894
+ * It can emulate XtStoreManager to some extends for doing some tests
1895
+ */
1896
+ class StoreTestHelper {
1897
+ /**
1898
+ * Sets a TestStoreManager as the test store manager in StoreSupport.
1899
+ * Call this in test setup to use the in-memory store provider.
1900
+ */
1901
+ static ensureTestProviderOnly() {
1902
+ StoreSupport.setTestStoreManager(new TestStoreManager());
1903
+ }
1904
+ }
1905
+ /**
1906
+ * A test implementation of IStoreManager that always returns a single TestStoreProvider.
1907
+ * Used to emulate xt-store for unit testing purposes.
1908
+ */
1909
+ class TestStoreManager {
1910
+ constructor() {
1911
+ /** The default provider returned for all store operations. */
1912
+ this.defaultProvider = new TestStoreProvider();
1913
+ }
1914
+ /**
1915
+ * Returns the default test store provider.
1916
+ * @param name - Ignored; always returns the same provider
1917
+ * @returns The default TestStoreProvider
1918
+ */
1919
+ getProvider(name) {
1920
+ return this.defaultProvider;
1921
+ }
1922
+ /**
1923
+ * Returns the default test store provider (safe variant).
1924
+ * @param name - Ignored; always returns the same provider
1925
+ * @returns The default TestStoreProvider
1926
+ */
1927
+ getProviderSafe(name) {
1928
+ return this.defaultProvider;
1929
+ }
1930
+ /**
1931
+ * Returns the default test store provider.
1932
+ * @returns The default TestStoreProvider or undefined
1933
+ */
1934
+ getDefaultProvider() {
1935
+ return this.defaultProvider;
1936
+ }
1937
+ /**
1938
+ * Returns the default test store provider (safe variant).
1939
+ * @returns The default TestStoreProvider
1940
+ */
1941
+ getDefaultProviderSafe() {
1942
+ return this.defaultProvider;
1943
+ }
1944
+ /**
1945
+ * Creates a new TestStoreCriteria instance.
1946
+ * @param name - The field name for the criteria
1947
+ * @param value - The value to match
1948
+ * @param operator - The comparison operator
1949
+ * @returns A new TestStoreCriteria
1950
+ */
1951
+ newStoreCriteria(name, value, operator) {
1952
+ return new TestStoreCriteria(name, value, operator);
1953
+ }
1954
+ }
1955
+ /**
1956
+ * A test implementation of IStoreProvider that stores entities in an in-memory map.
1957
+ * Supports basic CRUD and filtering operations for unit testing.
1958
+ */
1959
+ class TestStoreProvider {
1960
+ constructor() {
1961
+ /** In-memory data store, keyed by entity type name then by entity key. */
1962
+ this.data = new Map();
1963
+ }
1964
+ /**
1965
+ * Gets or creates a named entity map within the data store.
1966
+ * @param name - The entity type name
1967
+ * @returns The map of entities for the given type
1968
+ */
1969
+ getOrCreateArray(name) {
1970
+ let ret = this.data.get(name);
1971
+ if (ret == null) {
1972
+ ret = new Map();
1973
+ this.data.set(name, ret);
1974
+ }
1975
+ return ret;
1976
+ }
1977
+ /**
1978
+ * Extracts a unique key from a value object, using _id, id, or generating a new one.
1979
+ * @param value - The value to extract the key from
1980
+ * @param create - Whether to generate a new key if none exists
1981
+ * @returns The extracted or generated key string
1982
+ */
1983
+ extractKey(value, create) {
1984
+ if (value._id != null)
1985
+ return value._id; // ManagedData key
1986
+ else if (value.id != null)
1987
+ return value.id;
1988
+ else {
1989
+ if (create === true) {
1990
+ const newId = Math.random().toString(36).substring(2, 8);
1991
+ value._id = newId;
1992
+ return newId;
1993
+ }
1994
+ return value.toString();
1995
+ }
1996
+ }
1997
+ /**
1998
+ * Stores an entity in the in-memory data store.
1999
+ * @param name - The entity type name
2000
+ * @param entity - The entity to store
2001
+ * @returns A promise resolving to the stored entity
2002
+ */
2003
+ storeEntity(name, entity) {
2004
+ this.getOrCreateArray(name).set(this.extractKey(entity, true), entity);
2005
+ return Promise.resolve(entity);
2006
+ }
2007
+ /**
2008
+ * Loads an entity by key, throwing if not found.
2009
+ * @param name - The entity type name
2010
+ * @param key - The entity key
2011
+ * @returns A promise resolving to the entity
2012
+ */
2013
+ safeLoadEntity(name, key) {
2014
+ const ret = this.getOrCreateArray(name).get(key);
2015
+ if (ret == null) {
2016
+ throw new Error("No entity named " + name + " with key " + key);
2017
+ }
2018
+ return Promise.resolve(ret);
2019
+ }
2020
+ /**
2021
+ * Loads an entity by key, returning undefined if not found.
2022
+ * @param name - The entity type name
2023
+ * @param key - The entity key
2024
+ * @returns A promise resolving to the entity or undefined
2025
+ */
2026
+ loadEntity(name, key) {
2027
+ return Promise.resolve(this.getOrCreateArray(name).get(key));
2028
+ }
2029
+ /**
2030
+ * Deletes an entity by key.
2031
+ * @param name - The entity type name
2032
+ * @param key - The entity key
2033
+ * @returns A promise resolving to true if deleted, false otherwise
2034
+ */
2035
+ deleteEntity(name, key) {
2036
+ return Promise.resolve(this.getOrCreateArray(name).delete(key));
2037
+ }
2038
+ /**
2039
+ * Searches entities by optional criteria. Returns all entities if no criteria provided.
2040
+ * @param name - The entity type name
2041
+ * @param criteria - Optional filter criteria
2042
+ * @returns An observable of matching entities
2043
+ */
2044
+ searchEntities(name, ...criteria) {
2045
+ // No criteria defined, just send the full list
2046
+ const ret = new Array();
2047
+ if ((criteria == null) || (criteria.length == 0)) {
2048
+ for (const toAdd of this.getOrCreateArray(name).values()) {
2049
+ ret.push(toAdd);
2050
+ }
2051
+ }
2052
+ else {
2053
+ for (const toAdd of this.getOrCreateArray(name).values()) {
2054
+ let canAdd = true;
2055
+ for (const criter of criteria) {
2056
+ if (!criter.filter(toAdd)) {
2057
+ canAdd = false;
2058
+ break;
2059
+ }
2060
+ }
2061
+ if (canAdd)
2062
+ ret.push(toAdd);
2063
+ }
2064
+ }
2065
+ return from([ret]);
2066
+ }
2067
+ /**
2068
+ * Search and prepare entities with sorting, grouping, and transformation (not implemented).
2069
+ */
2070
+ searchAndPrepareEntities(name, sort, groupBy, transformer, ...criteria) {
2071
+ throw new Error('Method not implemented.');
2072
+ }
2073
+ /**
2074
+ * Indicates that this provider supports document storage.
2075
+ * @returns True
2076
+ */
2077
+ canStoreDocument() {
2078
+ return true;
2079
+ }
2080
+ /**
2081
+ * Stores a document file and returns its document info.
2082
+ * @param toStore - The file to store
2083
+ * @returns A promise resolving to the document info
2084
+ */
2085
+ storeDocument(toStore) {
2086
+ const ret = new TestDocumentInfo(toStore.name, true, URL.createObjectURL(toStore));
2087
+ return Promise.resolve(ret);
2088
+ }
2089
+ /**
2090
+ * Stores multiple document files (not implemented).
2091
+ */
2092
+ storeDocuments(toStore) {
2093
+ throw new Error('Method not implemented.');
2094
+ }
2095
+ }
2096
+ /**
2097
+ * A test implementation of IDocumentInfo for testing document storage operations.
2098
+ */
2099
+ class TestDocumentInfo {
2100
+ /**
2101
+ * Creates a new TestDocumentInfo instance.
2102
+ * @param documentName - The document name
2103
+ * @param isUrl - Whether the document is a URL
2104
+ * @param documentId - Optional document identifier
2105
+ */
2106
+ constructor(documentName, isUrl, documentId) {
2107
+ this.documentId = documentId;
2108
+ this.documentName = documentName;
2109
+ this.isUrl = isUrl;
2110
+ }
2111
+ }
2112
+ /**
2113
+ * A test implementation of IStoreCriteria that filters entities by field comparison.
2114
+ */
2115
+ class TestStoreCriteria {
2116
+ /**
2117
+ * Creates a new TestStoreCriteria instance.
2118
+ * @param name - The field name to filter on
2119
+ * @param value - The value to compare against
2120
+ * @param operator - The comparison operator (default '=')
2121
+ */
2122
+ constructor(name, value, operator) {
2123
+ this.name = name;
2124
+ this.value = value;
2125
+ if (!operator)
2126
+ this.operator = '=';
2127
+ else {
2128
+ this.operator = operator;
2129
+ }
2130
+ }
2131
+ /**
2132
+ * Filters an entity by comparing its field value against the criteria value.
2133
+ * @param toFilter - The entity to test
2134
+ * @returns True if the entity matches the criteria
2135
+ */
2136
+ filter(toFilter) {
2137
+ const testValue = toFilter[this.name];
2138
+ switch (this.operator) {
2139
+ case '=':
2140
+ return testValue == this.value;
2141
+ case '<':
2142
+ return testValue < this.value;
2143
+ case '<=':
2144
+ return testValue < this.value;
2145
+ default:
2146
+ return true;
2147
+ }
2148
+ }
2149
+ }
2150
+
1560
2151
  /*
1561
2152
  * Public API Surface of xt-components
1562
2153
  */
@@ -1565,5 +2156,5 @@ function generateFormControl(formDescription) {
1565
2156
  * Generated bundle index. Do not edit.
1566
2157
  */
1567
2158
 
1568
- export { HostTestFormComponent, HostTestSimpleComponent, HostTestTypedComponent, HostTestTypedFormComponent, StoreSupport, StoreTestHelper, TestDocumentInfo, TestStoreCriteria, TestStoreManager, TestStoreProvider, XT_REGISTRY_TOKEN, XT_RESOLVER_TOKEN, XT_TYPE_RESOLVER_TOKEN, XtBaseContext, XtCompositeComponent, XtMessageHandler, XtPluginRegistry, XtRenderComponent, XtRenderSubComponent, XtResolvedComponent, XtResolverService, XtSimpleComponent, XtUnitTestHelper, attachToFormGroup, initXtPluginRegistry, updateFormGroupWithValue, xtPluginRegistry };
2159
+ export { HostTestFormComponent, HostTestSimpleComponent, HostTestTypedComponent, HostTestTypedFormComponent, ISortByDirection, StoreSupport, StoreTestHelper, TestDocumentInfo, TestStoreCriteria, TestStoreManager, TestStoreProvider, XT_REGISTRY_TOKEN, XT_RESOLVER_TOKEN, XT_TYPE_RESOLVER_TOKEN, XtBaseContext, XtBaseInput, XtBaseModel, XtBaseOutput, XtCompositeComponent, XtMessageHandler, XtPluginRegistry, XtRenderComponent, XtRenderSubComponent, XtResolvedComponent, XtResolverService, XtSimpleComponent, XtUnitTestHelper, attachToFormGroup, initXtPluginRegistry, updateFormGroupWithValue, xtPluginRegistry };
1569
2160
  //# sourceMappingURL=xt-components.mjs.map