xt-components 0.5.5 → 0.6.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,11 +1,189 @@
1
+ import { isTypeReference, XtBaseTypeReference, xtTypeManager, ManagedDataHandler, isPrimitive } from 'xt-type';
1
2
  import * as i0 from '@angular/core';
2
3
  import { signal, computed, InjectionToken, inject, Injectable, input, model, output, viewChild, Component } from '@angular/core';
3
- import { xtTypeManager, ManagedDataHandler, isPrimitive } from 'xt-type';
4
4
  import { loadRemoteModule } from '@angular-architects/native-federation';
5
+ import { from, firstValueFrom } from 'rxjs';
5
6
  import { NgComponentOutlet, CommonModule } from '@angular/common';
6
7
  import * as i1 from '@angular/forms';
7
8
  import { ReactiveFormsModule, FormControl, FormGroup, FormBuilder, FormArray } from '@angular/forms';
8
- import { from } from 'rxjs';
9
+
10
+ class XtPluginRegistry {
11
+ constructor() {
12
+ this.pluginRegistry = new Map();
13
+ this.componentRegistry = new Map();
14
+ this.componentByTypeCache = new Map();
15
+ this.actionByTypeRegistry = new Map();
16
+ this.listComponents = signal(new Array(), ...(ngDevMode ? [{ debugName: "listComponents" }] : []));
17
+ this.listPlugins = signal(new Array(), ...(ngDevMode ? [{ debugName: "listPlugins" }] : []));
18
+ }
19
+ /**
20
+ * The component can manage any standard javascript primitives types. That's usually the default whenever we don't know any particular type
21
+ * string
22
+ * number
23
+ * bigint
24
+ * boolean
25
+ * undefined
26
+ * null
27
+ * symbol is not managed
28
+ * Date, while an object and not a primitive, is managed
29
+ */
30
+ static { this.ANY_PRIMITIVE_TYPE = "ANY_PRIMITIVE_TYPE"; }
31
+ /**
32
+ * 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
+ */
34
+ static { this.ANY_OBJECT_TYPE = "ANY_OBJECT_TYPE"; }
35
+ static { this.ANY_PRIMITIVE_SET = "ANY_PRIMITIVE_SET"; }
36
+ static { this.ANY_OBJECT_SET = "ANY_OBJECT_SET"; }
37
+ /**
38
+ * Whenever a component can handle any type of reference to a single entity or to multiple entities.
39
+ */
40
+ static { this.ANY_SINGLE_REFERENCE = "ANY_SINGLE_REFERENCE"; }
41
+ static { this.ANY_MULTIPLE_REFERENCE = "ANY_MULTIPLE_REFERENCE"; }
42
+ registerPlugin(info) {
43
+ this.pluginRegistry.set(info.name, info);
44
+ if (info.components != null) {
45
+ let updated = false;
46
+ for (const comp of info.components) {
47
+ updated = true;
48
+ this.registerComponent(comp);
49
+ }
50
+ if (updated)
51
+ this.componentByTypeCache.clear(); // Force recalculation of type
52
+ }
53
+ if (info.actionHandlers != null) {
54
+ for (const handler of info.actionHandlers) {
55
+ this.registerActionHandler(handler);
56
+ }
57
+ }
58
+ this.listPlugins.update((array) => {
59
+ let found = false;
60
+ for (let i = 0; i < array.length; i++) {
61
+ if (array[i].name == info.name) {
62
+ found = true;
63
+ array[i] = info;
64
+ }
65
+ }
66
+ if (!found)
67
+ array.push(info);
68
+ return [...array]; // You have to send another value, not just update the existing one.
69
+ });
70
+ }
71
+ registerComponent(info) {
72
+ this.componentRegistry.set(info.componentName, info);
73
+ this.listComponents.update((array) => {
74
+ let found = false;
75
+ for (let i = 0; i < array.length; i++) {
76
+ if (array[i].componentName == info.componentName) {
77
+ found = true;
78
+ array[i] = info;
79
+ }
80
+ }
81
+ if (!found)
82
+ array.push(info);
83
+ return array;
84
+ });
85
+ }
86
+ findComponentsForType(valueType, value) {
87
+ let originalType = valueType;
88
+ //console.debug('Finding type from '+valueType+' with value ',value);
89
+ // We don't know the value type, let's try to guess if it's a primitive or object based on the value
90
+ if (valueType == null) {
91
+ valueType = XtPluginRegistry.ANY_OBJECT_TYPE;
92
+ if ((value == null) || (typeof value != 'object')) {
93
+ valueType = XtPluginRegistry.ANY_PRIMITIVE_TYPE;
94
+ }
95
+ else if (value instanceof Date) {
96
+ valueType = XtPluginRegistry.ANY_PRIMITIVE_TYPE;
97
+ }
98
+ if (Array.isArray(value)) {
99
+ valueType = (valueType === XtPluginRegistry.ANY_PRIMITIVE_TYPE) ? XtPluginRegistry.ANY_PRIMITIVE_SET : XtPluginRegistry.ANY_OBJECT_SET;
100
+ }
101
+ }
102
+ else { // originalType has been defined.
103
+ if (Array.isArray(value)) {
104
+ valueType = valueType.endsWith('[]') ? valueType : valueType + '[]';
105
+ originalType = valueType;
106
+ }
107
+ }
108
+ //console.debug('Type found is '+valueType);
109
+ let ret = this.componentByTypeCache.get(valueType);
110
+ if (ret == null) {
111
+ ret = new Array();
112
+ for (const comp of this.componentRegistry) {
113
+ const info = comp[1];
114
+ if (info.typesHandled.includes(valueType)) {
115
+ ret.push(info);
116
+ }
117
+ }
118
+ if ((ret.length == 0) && (originalType != null)) {
119
+ // Couldn't find a specific component, let's try the generic ones, so we don't pass any type
120
+ ret = this.findComponentsForType(null, value);
121
+ // Cache the component only if we were able to assert its type.
122
+ // If no type has been given and value is null, then we cannot assess the real type
123
+ if (value != null) {
124
+ this.componentByTypeCache.set(originalType, ret);
125
+ }
126
+ }
127
+ else {
128
+ // Cache the component only if we were able to assert its type.
129
+ // If no type has been given and value is null, then we cannot assess the real type
130
+ if ((value != null) || (originalType != null)) {
131
+ this.componentByTypeCache.set(originalType ?? valueType, ret);
132
+ }
133
+ }
134
+ }
135
+ return ret;
136
+ }
137
+ static registry() {
138
+ return XT_REGISTRY;
139
+ }
140
+ findComponentInfo(type) {
141
+ // Search for the component registered with this class
142
+ for (const info of this.componentRegistry.values()) {
143
+ if (info.componentClass == type) {
144
+ return info;
145
+ }
146
+ }
147
+ return null;
148
+ }
149
+ getComponentInfo(type) {
150
+ const ret = this.findComponentInfo(type);
151
+ if (ret == null) {
152
+ throw new Error("No component found with class " + type);
153
+ }
154
+ return ret;
155
+ }
156
+ registerActionHandler(handlerInfo) {
157
+ for (const type of handlerInfo.types) {
158
+ const handlers = handlerInfo.actions;
159
+ for (const actionName of Object.keys(handlers)) {
160
+ let exist = this.actionByTypeRegistry.get(type);
161
+ if (exist == null) {
162
+ exist = new Map();
163
+ this.actionByTypeRegistry.set(type, exist);
164
+ }
165
+ exist.set(actionName, handlers[actionName]);
166
+ }
167
+ }
168
+ }
169
+ findActionInfo(type, actionName) {
170
+ const handlers = this.actionByTypeRegistry.get(type);
171
+ if (handlers != null) {
172
+ return handlers.get(actionName);
173
+ }
174
+ return undefined;
175
+ }
176
+ listActionInfos(type) {
177
+ const handlers = this.actionByTypeRegistry.get(type);
178
+ if (handlers != null) {
179
+ return Array.from(handlers.entries()).map(([name, info]) => {
180
+ return { name: name, info: info };
181
+ });
182
+ }
183
+ else
184
+ return [];
185
+ }
186
+ }
9
187
 
10
188
  class XtBaseContext {
11
189
  /**
@@ -17,6 +195,11 @@ class XtBaseContext {
17
195
  */
18
196
  constructor(displayMode, subName, parentGroup, parentContext) {
19
197
  this.displayMode = 'FULL_VIEW';
198
+ /**
199
+ * If it's a reference, we keep the context referenced
200
+ *
201
+ referencedContext?:XtContext<any>;*/
202
+ //subReferencesResolved = signal(false);
20
203
  /**
21
204
  * Keeps track of all the possible actions for this context
22
205
  * @protected
@@ -168,11 +351,18 @@ class XtBaseContext {
168
351
  if (!Array.isArray(this.value())) {
169
352
  throw new Error("The value must be an Array / Set to create a subElement context.");
170
353
  }
171
- const ret = new XtBaseContext(this.displayMode, undefined, undefined, this);
172
- ret.setDisplayValue(value[elementIndex]);
173
- if (this.valueType != null) {
174
- // Convert potential array type into single type
175
- ret.valueType = this.valueType.endsWith('[]') ? this.valueType.substring(0, this.valueType.length - 2) : this.valueType;
354
+ const indexKey = elementIndex.toString();
355
+ let ret = this.childContexts?.get(indexKey);
356
+ if (ret == null) {
357
+ ret = new XtBaseContext(this.displayMode, undefined, undefined, this);
358
+ ret.setDisplayValue(value[elementIndex]);
359
+ if (this.valueType != null) {
360
+ // Convert potential array type into single type
361
+ ret.valueType = this.valueType.endsWith('[]') ? this.valueType.substring(0, this.valueType.length - 2) : this.valueType;
362
+ }
363
+ if (this.childContexts == null)
364
+ this.childContexts = new Map();
365
+ this.childContexts?.set(indexKey, ret);
176
366
  }
177
367
  return ret;
178
368
  }
@@ -205,7 +395,25 @@ class XtBaseContext {
205
395
  ret.valueType = subType;
206
396
  }
207
397
  else if ((this.valueType != null) && (typeResolver != null)) {
208
- ret.valueType = typeResolver.findTypeName(this.valueType, subName, this.value()) ?? undefined;
398
+ const subType = typeResolver.findType(this.valueType, subName, this.value());
399
+ if (subType != null) {
400
+ if (isTypeReference(subType)) {
401
+ if (subType.type == XtBaseTypeReference.UNRESOLVED_TYPE)
402
+ throw new Error("You must resolve all reference types before using them in a context. Missing type " + subType.type + " for subName " + subName + " in valueType " + this.valueType + " of context " + this.toString());
403
+ ret.valueType = subType.toType;
404
+ ret.reference = subType;
405
+ if (this.displayMode == 'LIST_VIEW')
406
+ ret.displayMode = 'INLINE_VIEW'; // We display a reference as inline in a list
407
+ else if (this.displayMode == 'FULL_EDITABLE') {
408
+ // We don't edit directly references, we simply enable selection of them.
409
+ ret.valueType = (subType.referenceType == 'ONE-TO-MANY') ? XtPluginRegistry.ANY_MULTIPLE_REFERENCE : XtPluginRegistry.ANY_SINGLE_REFERENCE;
410
+ }
411
+ }
412
+ else {
413
+ ret.valueType = subType.type;
414
+ }
415
+ }
416
+ //ret.valueType=typeResolver.findTypeName(this.valueType, subName, this.value())??undefined;
209
417
  }
210
418
  if (this.childContexts == null)
211
419
  this.childContexts = new Map();
@@ -216,6 +424,28 @@ class XtBaseContext {
216
424
  formGroup() {
217
425
  return this.localFormGroup ?? this.parentFormGroup;
218
426
  }
427
+ isReference() {
428
+ return (this.reference != null);
429
+ }
430
+ setReferenceInfo(reference) {
431
+ this.reference = reference;
432
+ //this.subReferencesResolved.set(this.reference!=null);
433
+ }
434
+ /**
435
+ * creates the referencedContext by using this referenced value
436
+ * @param val
437
+ *
438
+ updateReferencedContext(val: any, valueType?:string): void {
439
+ if (!this.isReference()) throw new Error ('This context '+this.toString()+' is not a reference.');
440
+
441
+ if( this.referencedContext==null) {
442
+ let refDisplayMode = 'INLINE_VIEW' as XtDisplayMode;
443
+ if (this.displayMode=='FULL_VIEW') refDisplayMode = 'FULL_VIEW';
444
+ this.referencedContext = new XtBaseContext(refDisplayMode);
445
+ }
446
+ this.referencedContext.setDisplayValue(val);
447
+ if( valueType!=null) this.referencedContext.valueType=valueType;
448
+ }*/
219
449
  toString() {
220
450
  let ret = 'XtContext named ';
221
451
  ret += this.subName ?? 'None';
@@ -223,6 +453,10 @@ class XtBaseContext {
223
453
  ret += this.valueType ?? 'None';
224
454
  ret += ' with value ';
225
455
  ret += this.nonFormValue ? this.nonFormValue() : this.formControlValue();
456
+ if (this.isReference()) {
457
+ ret += ' referencing ';
458
+ ret += this.reference?.type;
459
+ }
226
460
  return ret;
227
461
  }
228
462
  }
@@ -242,225 +476,221 @@ class XtResolvedComponent {
242
476
  }
243
477
  }
244
478
 
245
- class XtPluginRegistry {
246
- constructor() {
247
- this.pluginRegistry = new Map();
248
- this.componentRegistry = new Map();
249
- this.componentByTypeCache = new Map();
250
- this.actionByTypeRegistry = new Map();
251
- this.listComponents = signal(new Array(), ...(ngDevMode ? [{ debugName: "listComponents" }] : []));
252
- this.listPlugins = signal(new Array(), ...(ngDevMode ? [{ debugName: "listPlugins" }] : []));
479
+ /**
480
+ * The global plugin registry.
481
+ * Plugins will register to this when loaded.
482
+ */
483
+ function initXtPluginRegistry() {
484
+ if (globalThis.XT_REGISTRY == null) {
485
+ globalThis.XT_REGISTRY = new XtPluginRegistry();
253
486
  }
254
- /**
255
- * The component can manage any standard javascript primitives types. That's usually the default whenever we don't know any particular type
256
- * string
257
- * number
258
- * bigint
259
- * boolean
260
- * undefined
261
- * null
262
- * symbol is not managed
263
- * Date, while an object and not a primitive, is managed
264
- */
265
- static { this.ANY_PRIMITIVE_TYPE = "ANY_PRIMITIVE_TYPE"; }
266
- /**
267
- * 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)
268
- */
269
- static { this.ANY_OBJECT_TYPE = "ANY_OBJECT_TYPE"; }
270
- static { this.ANY_PRIMITIVE_SET = "ANY_PRIMITIVE_SET"; }
271
- static { this.ANY_OBJECT_SET = "ANY_OBJECT_SET"; }
272
- registerPlugin(info) {
273
- this.pluginRegistry.set(info.name, info);
274
- if (info.components != null) {
275
- let updated = false;
276
- for (const comp of info.components) {
277
- updated = true;
278
- this.registerComponent(comp);
279
- }
280
- if (updated)
281
- this.componentByTypeCache.clear(); // Force recalculation of type
282
- }
283
- if (info.actionHandlers != null) {
284
- for (const handler of info.actionHandlers) {
285
- this.registerActionHandler(handler);
286
- }
287
- }
288
- this.listPlugins.update((array) => {
289
- let found = false;
290
- for (let i = 0; i < array.length; i++) {
291
- if (array[i].name == info.name) {
292
- found = true;
293
- array[i] = info;
294
- }
295
- }
296
- if (!found)
297
- array.push(info);
298
- return [...array]; // You have to send another value, not just update the existing one.
299
- });
487
+ }
488
+ function xtPluginRegistry() {
489
+ if (globalThis.XT_REGISTRY == null) {
490
+ initXtPluginRegistry();
300
491
  }
301
- registerComponent(info) {
302
- this.componentRegistry.set(info.componentName, info);
303
- this.listComponents.update((array) => {
304
- let found = false;
305
- for (let i = 0; i < array.length; i++) {
306
- if (array[i].componentName == info.componentName) {
307
- found = true;
308
- array[i] = info;
309
- }
310
- }
311
- if (!found)
312
- array.push(info);
313
- return array;
314
- });
492
+ return globalThis.XT_REGISTRY;
493
+ }
494
+
495
+ const XT_RESOLVER_TOKEN = new InjectionToken('Enable providing a custom component resolver.');
496
+ const XT_TYPE_RESOLVER_TOKEN = new InjectionToken('Enable providing a custom type resolver.');
497
+ const XT_REGISTRY_TOKEN = new InjectionToken("Injects the Plugin Registry right into your angular component", {
498
+ factory: () => {
499
+ return xtPluginRegistry();
315
500
  }
316
- findComponentsForType(valueType, value) {
317
- let originalType = valueType;
318
- //console.debug('Finding type from '+valueType+' with value ',value);
319
- // We don't know the value type, let's try to guess if it's a primitive or object based on the value
320
- if (valueType == null) {
321
- valueType = XtPluginRegistry.ANY_OBJECT_TYPE;
322
- if ((value == null) || (typeof value != 'object')) {
323
- valueType = XtPluginRegistry.ANY_PRIMITIVE_TYPE;
324
- }
325
- else if (value instanceof Date) {
326
- valueType = XtPluginRegistry.ANY_PRIMITIVE_TYPE;
501
+ });
502
+
503
+ class XtRegistryResolver {
504
+ constructor(registry, typeResolver) {
505
+ this.registry = registry;
506
+ this.typeResolver = typeResolver;
507
+ }
508
+ resolve(baseContext, subName) {
509
+ let typeToFind = baseContext.valueType;
510
+ const typeInfo = this.typeResolver.findType(baseContext.valueType, subName);
511
+ // If it's a type reference, we find the component of the referenced type
512
+ if (isTypeReference(typeInfo)) {
513
+ if (baseContext.displayMode == 'FULL_EDITABLE') {
514
+ typeToFind = XtPluginRegistry.ANY_SINGLE_REFERENCE;
327
515
  }
328
- if (Array.isArray(value)) {
329
- valueType = (valueType === XtPluginRegistry.ANY_PRIMITIVE_TYPE) ? XtPluginRegistry.ANY_PRIMITIVE_SET : XtPluginRegistry.ANY_OBJECT_SET;
516
+ else {
517
+ typeToFind = typeInfo.toType;
330
518
  }
331
519
  }
332
- else { // originalType has been defined.
333
- if (Array.isArray(value)) {
334
- valueType = valueType.endsWith('[]') ? valueType : valueType + '[]';
335
- originalType = valueType;
336
- }
520
+ const ret = this.registry.findComponentsForType(typeToFind, baseContext.subValue(subName));
521
+ if (ret != null && ret.length > 0) {
522
+ return XtResolvedComponent.from(ret[0]);
337
523
  }
338
- //console.debug('Type found is '+valueType);
339
- let ret = this.componentByTypeCache.get(valueType);
524
+ return null;
525
+ }
526
+ }
527
+
528
+ class XtAction {
529
+ constructor(name, info, enabled) {
530
+ this.enabled = signal(false, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
531
+ this.name = name;
532
+ this.info = info;
533
+ if (enabled != null) {
534
+ this.enabled.set(enabled);
535
+ }
536
+ }
537
+ }
538
+
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);
340
574
  if (ret == null) {
341
- ret = new Array();
342
- for (const comp of this.componentRegistry) {
343
- const info = comp[1];
344
- if (info.typesHandled.includes(valueType)) {
345
- ret.push(info);
346
- }
347
- }
348
- if ((ret.length == 0) && (originalType != null)) {
349
- // Couldn't find a specific component, let's try the generic ones, so we don't pass any type
350
- ret = this.findComponentsForType(null, value);
351
- // Cache the component only if we were able to assert its type.
352
- // If no type has been given and value is null, then we cannot assess the real type
353
- if (value != null) {
354
- this.componentByTypeCache.set(originalType, ret);
355
- }
356
- }
357
- else {
358
- // Cache the component only if we were able to assert its type.
359
- // If no type has been given and value is null, then we cannot assess the real type
360
- if ((value != null) || (originalType != null)) {
361
- this.componentByTypeCache.set(originalType ?? valueType, ret);
362
- }
363
- }
575
+ ret = new Map();
576
+ this.data.set(name, ret);
364
577
  }
365
578
  return ret;
366
579
  }
367
- static registry() {
368
- return XT_REGISTRY;
369
- }
370
- findComponentInfo(type) {
371
- // Search for the component registered with this class
372
- for (const info of this.componentRegistry.values()) {
373
- if (info.componentClass == type) {
374
- return info;
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;
375
590
  }
591
+ return value.toString();
376
592
  }
377
- return null;
378
593
  }
379
- getComponentInfo(type) {
380
- const ret = this.findComponentInfo(type);
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);
381
600
  if (ret == null) {
382
- throw new Error("No component found with class " + type);
601
+ throw new Error("No entity named " + name + " with key " + key);
383
602
  }
384
- return ret;
603
+ return Promise.resolve(ret);
385
604
  }
386
- registerActionHandler(handlerInfo) {
387
- for (const type of handlerInfo.types) {
388
- const handlers = handlerInfo.actions;
389
- for (const actionName of Object.keys(handlers)) {
390
- let exist = this.actionByTypeRegistry.get(type);
391
- if (exist == null) {
392
- exist = new Map();
393
- this.actionByTypeRegistry.set(type, exist);
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
+ }
394
627
  }
395
- exist.set(actionName, handlers[actionName]);
628
+ if (canAdd)
629
+ ret.push(toAdd);
396
630
  }
397
631
  }
632
+ return from([ret]);
398
633
  }
399
- findActionInfo(type, actionName) {
400
- const handlers = this.actionByTypeRegistry.get(type);
401
- if (handlers != null) {
402
- return handlers.get(actionName);
403
- }
404
- return undefined;
634
+ searchAndPrepareEntities(name, sort, groupBy, transformer, ...criteria) {
635
+ throw new Error('Method not implemented.');
405
636
  }
406
- listActionInfos(type) {
407
- const handlers = this.actionByTypeRegistry.get(type);
408
- if (handlers != null) {
409
- return Array.from(handlers.entries()).map(([name, info]) => {
410
- return { name: name, info: info };
411
- });
412
- }
413
- else
414
- return [];
637
+ canStoreDocument() {
638
+ return true;
415
639
  }
416
- }
417
-
418
- /**
419
- * The global plugin registry.
420
- * Plugins will register to this when loaded.
421
- */
422
- function initXtPluginRegistry() {
423
- if (globalThis.XT_REGISTRY == null) {
424
- globalThis.XT_REGISTRY = new XtPluginRegistry();
640
+ storeDocument(toStore) {
641
+ const ret = new TestDocumentInfo(toStore.name, true, URL.createObjectURL(toStore));
642
+ return Promise.resolve(ret);
425
643
  }
426
- }
427
- function xtPluginRegistry() {
428
- if (globalThis.XT_REGISTRY == null) {
429
- initXtPluginRegistry();
644
+ storeDocuments(toStore) {
645
+ throw new Error('Method not implemented.');
430
646
  }
431
- return globalThis.XT_REGISTRY;
432
647
  }
433
-
434
- const XT_RESOLVER_TOKEN = new InjectionToken('Enable providing a custom component resolver.');
435
- const XT_TYPE_RESOLVER_TOKEN = new InjectionToken('Enable providing a custom type resolver.');
436
- const XT_REGISTRY_TOKEN = new InjectionToken("Injects the Plugin Registry right into your angular component", {
437
- factory: () => {
438
- return xtPluginRegistry();
648
+ class TestDocumentInfo {
649
+ constructor(documentName, isUrl, documentId) {
650
+ this.documentId = documentId;
651
+ this.documentName = documentName;
652
+ this.isUrl = isUrl;
439
653
  }
440
- });
441
-
442
- class XtRegistryResolver {
443
- constructor(registry, typeResolver) {
444
- this.registry = registry;
445
- this.typeResolver = typeResolver;
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
+ }
446
664
  }
447
- resolve(baseContext, subName) {
448
- const ret = this.registry.findComponentsForType(this.typeResolver.findTypeName(baseContext.valueType, subName), baseContext.subValue(subName));
449
- if (ret != null && ret.length > 0) {
450
- return XtResolvedComponent.from(ret[0]);
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;
451
676
  }
452
- return null;
453
677
  }
454
678
  }
455
679
 
456
- class XtAction {
457
- constructor(name, info, enabled) {
458
- this.enabled = signal(false, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
459
- this.name = name;
460
- this.info = info;
461
- if (enabled != null) {
462
- this.enabled.set(enabled);
463
- }
680
+ class StoreSupport {
681
+ static isStoreManagerAvailable() {
682
+ if (this.testStoreManager != null)
683
+ return true;
684
+ return (globalThis.xtStoreManager != undefined);
685
+ }
686
+ static getStoreManager() {
687
+ return this.testStoreManager ?? (globalThis.xtStoreManager());
688
+ }
689
+ static setTestStoreManager(testStoreManager) {
690
+ StoreSupport.testStoreManager = testStoreManager;
691
+ }
692
+ static newStoreCriteria(name, value, operator) {
693
+ return new TestStoreCriteria(name, value, operator);
464
694
  }
465
695
  }
466
696
 
@@ -501,7 +731,13 @@ class XtResolverService {
501
731
  return ret;
502
732
  }
503
733
  findTypeHandlerOf(baseContext, subName, value) {
504
- const ret = this.typeResolver.findTypeHandler(baseContext.valueType, false, subName, value);
734
+ let ret = { typeName: undefined, handler: undefined };
735
+ if (baseContext.isReference()) {
736
+ ret = this.typeResolver.findTypeHandler(baseContext.reference.toType, false, undefined, value);
737
+ }
738
+ else {
739
+ ret = this.typeResolver.findTypeHandler(baseContext.valueType, false, subName, value);
740
+ }
505
741
  return ret;
506
742
  }
507
743
  listSubNamesOf(baseContext, value) {
@@ -657,6 +893,41 @@ class XtResolverService {
657
893
  }
658
894
  return undefined;
659
895
  }
896
+ async resolveReferencedValue(context, storeMgr) {
897
+ if (!context.isReference())
898
+ return undefined;
899
+ const ref = context.reference;
900
+ const storeProvider = storeMgr.getProvider(ref.type);
901
+ if (storeProvider == null) {
902
+ throw new Error('No Store provider found for type ' + ref.type);
903
+ }
904
+ const ret = await firstValueFrom(storeProvider.searchEntities(ref.toType, storeMgr.newStoreCriteria(ref.field, context.value(), '=')));
905
+ if (ret.length == 0)
906
+ return null;
907
+ if (ref.referenceType == 'MANY-TO-ONE') {
908
+ if (ret.length > 1)
909
+ throw new Error('Multiple values for many to one relation between ' + context.valueType + ' and ' + ref.type + ' with value ' + context.value());
910
+ return ret[0];
911
+ }
912
+ else if (ref.referenceType == 'ONE-TO-MANY') {
913
+ return ret;
914
+ }
915
+ return undefined;
916
+ }
917
+ resolvePendingReferences() {
918
+ this.typeResolver.resolveAllTypeReferences();
919
+ }
920
+ /**
921
+ * Calculates the values that can be referenced by the reference & value of this context
922
+ * @param context
923
+ */
924
+ findPossibleReferences(context) {
925
+ if (!context.isReference())
926
+ throw new Error('Cannot find possible references of this non reference context' + context.toString());
927
+ const reference = context.reference;
928
+ const store = StoreSupport.getStoreManager().getProviderSafe(reference.toType);
929
+ return store.searchEntities(reference.toType);
930
+ }
660
931
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtResolverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
661
932
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtResolverService, providedIn: 'root' }); }
662
933
  }
@@ -694,6 +965,10 @@ class XtRenderComponent {
694
965
  let form = this.formGroup();
695
966
  const ret = new XtBaseContext(this.displayMode(), this.subName(), form);
696
967
  ret.valueType = this.valueType();
968
+ const typeInfo = this.resolverService.typeResolver.findType(ret.valueType);
969
+ if (isTypeReference(typeInfo)) {
970
+ ret.setReferenceInfo(typeInfo);
971
+ }
697
972
  if (!ret.isInForm()) {
698
973
  const subName = this.subName();
699
974
  const value = this.value();
@@ -706,6 +981,13 @@ class XtRenderComponent {
706
981
  }
707
982
  return ret;
708
983
  }, ...(ngDevMode ? [{ debugName: "context" }] : []));
984
+ this.realContext = computed(() => {
985
+ let ret = this.context();
986
+ /*if ((ret.isReference())&& (ret.referencedContext!=null)) {
987
+ ret = ret.referencedContext;
988
+ }*/
989
+ return ret;
990
+ }, ...(ngDevMode ? [{ debugName: "realContext" }] : []));
709
991
  this.type = computed(() => {
710
992
  //console.debug("Calculating type in XtRenderSubComponent");
711
993
  let type = this.componentType();
@@ -714,7 +996,7 @@ class XtRenderComponent {
714
996
  //console.debug('XtRender, using component set '+ type);
715
997
  //compFound = this.resolverService.findComponentInfo (type);
716
998
  //} else {
717
- compFound = this.resolverService.findBestComponent(this.context());
999
+ compFound = this.resolverService.findBestComponent(this.realContext());
718
1000
  //console.debug('XtRender, found component ',compFound.componentName);
719
1001
  type = compFound.componentClass;
720
1002
  }
@@ -767,6 +1049,13 @@ class XtRenderSubComponent {
767
1049
  this.outputs = output();
768
1050
  this.outlet = viewChild.required(NgComponentOutlet);
769
1051
  this.resolverService = inject(XtResolverService);
1052
+ this.realContext = computed(() => {
1053
+ let ret = this.context();
1054
+ /*if ((ret.isReference()) && (ret.referencedContext!=null)) {
1055
+ ret = ret.referencedContext;
1056
+ }*/
1057
+ return ret;
1058
+ }, ...(ngDevMode ? [{ debugName: "realContext" }] : []));
770
1059
  this.type = computed(() => {
771
1060
  //console.debug("Calculating type in XtRenderSubComponent");
772
1061
  let type = this.componentType();
@@ -775,7 +1064,7 @@ class XtRenderSubComponent {
775
1064
  //console.debug('XtRender, using component set '+ type);
776
1065
  //compFound = this.resolverService.findComponentInfo (type);
777
1066
  //} else {
778
- compFound = this.resolverService.findBestComponent(this.context());
1067
+ compFound = this.resolverService.findBestComponent(this.realContext());
779
1068
  //console.debug('XtRender, found component ',compFound.componentName);
780
1069
  type = compFound.componentClass;
781
1070
  }
@@ -805,14 +1094,14 @@ class XtRenderSubComponent {
805
1094
  }
806
1095
  }
807
1096
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtRenderSubComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
808
- 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:context ()}\" />\n", styles: [""], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: ReactiveFormsModule }] }); }
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 }] }); }
809
1098
  }
810
1099
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: XtRenderSubComponent, decorators: [{
811
1100
  type: Component,
812
1101
  args: [{ selector: 'xt-render-sub', standalone: true, imports: [
813
1102
  NgComponentOutlet,
814
1103
  ReactiveFormsModule
815
- ], template: "{{componentType()}}\n<ng-container *ngComponentOutlet=\"type(); inputs: {context:context ()}\" />\n" }]
1104
+ ], template: "{{componentType()}}\n<ng-container *ngComponentOutlet=\"type(); inputs: {context:realContext ()}\" />\n" }]
816
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 }] }] } });
817
1106
 
818
1107
  class XtBaseInput {
@@ -987,20 +1276,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
987
1276
  }]
988
1277
  }] });
989
1278
 
990
- class StoreSupport {
991
- static isStoreManagerAvailable() {
992
- if (this.testStoreManager != null)
993
- return true;
994
- return (globalThis.xtStoreManager != undefined);
995
- }
996
- static getStoreManager() {
997
- return this.testStoreManager ?? (globalThis.xtStoreManager());
998
- }
999
- static setTestStoreManager(testStoreManager) {
1000
- StoreSupport.testStoreManager = testStoreManager;
1001
- }
1002
- }
1003
-
1004
1279
  function attachToFormGroup(formGroup, controlName, value, valueType, resolver) {
1005
1280
  // If it's a single value, just create the control
1006
1281
  if (((value != null) && (isPrimitive(value))
@@ -1026,13 +1301,14 @@ function updateFormGroupWithValue(formGroup, value, valueType, resolver) {
1026
1301
  }
1027
1302
  for (const valueKey of keySet) {
1028
1303
  const subValue = (value != null) ? value[valueKey] : null;
1029
- const subType = resolver?.findTypeName(valueType, valueKey, subValue) ?? undefined;
1030
- const primitive = (subType != null) ? resolver?.isPrimitiveType(subType, subValue) : isPrimitive(subValue);
1304
+ const subType = resolver?.findType(valueType, valueKey, subValue) ?? undefined;
1305
+ const subTypeName = isTypeReference(subType) ? subType.toType : subType?.type;
1306
+ const primitive = resolver?.isPrimitiveType(subType, subValue);
1031
1307
  if (toDelete.delete(valueKey)) {
1032
1308
  // Already a control
1033
1309
  const oldControl = formGroup.get(valueKey);
1034
1310
  // Is it the right type ?
1035
- if (primitive) {
1311
+ if ((primitive) || (isTypeReference(subType))) {
1036
1312
  // Must be an FormControl2
1037
1313
  if (oldControl.controls === undefined) {
1038
1314
  // It's ok, just set the value
@@ -1047,22 +1323,22 @@ function updateFormGroupWithValue(formGroup, value, valueType, resolver) {
1047
1323
  if (oldControl.controls === undefined) {
1048
1324
  const newFormGroup = new FormGroup({});
1049
1325
  formGroup.setControl(valueKey, newFormGroup);
1050
- updateFormGroupWithValue(newFormGroup, subValue, subType, resolver);
1326
+ updateFormGroupWithValue(newFormGroup, subValue, subTypeName, resolver);
1051
1327
  }
1052
1328
  else {
1053
1329
  // It was already a formgroup, so just update it
1054
- updateFormGroupWithValue(oldControl, subValue, subType, resolver);
1330
+ updateFormGroupWithValue(oldControl, subValue, subTypeName, resolver);
1055
1331
  }
1056
1332
  }
1057
1333
  }
1058
1334
  else {
1059
- if (primitive) {
1335
+ if ((primitive) || (isTypeReference(subType))) {
1060
1336
  formGroup.addControl(valueKey, new FormControl(subValue));
1061
1337
  }
1062
1338
  else {
1063
1339
  const newFormGroup = new FormGroup({});
1064
1340
  formGroup.addControl(valueKey, newFormGroup);
1065
- updateFormGroupWithValue(newFormGroup, subValue, subType, resolver);
1341
+ updateFormGroupWithValue(newFormGroup, subValue, subTypeName, resolver);
1066
1342
  }
1067
1343
  }
1068
1344
  }
@@ -1111,6 +1387,7 @@ class HostTestFormComponent {
1111
1387
  this.formDescription = input({}, ...(ngDevMode ? [{ debugName: "formDescription" }] : []));
1112
1388
  // Or set the FormGroup directly
1113
1389
  this.formGroup = input(...(ngDevMode ? [undefined, { debugName: "formGroup" }] : []));
1390
+ // parentFormGroup = this.builder.group<{[keys:string]: AbstractControl}>({});
1114
1391
  this.createdFormGroup = null;
1115
1392
  }
1116
1393
  computedFormGroup() {
@@ -1118,6 +1395,7 @@ class HostTestFormComponent {
1118
1395
  const formGroup = this.formGroup();
1119
1396
  this.createdFormGroup = formGroup ?? generateFormGroup(this.formDescription());
1120
1397
  }
1398
+ // this.parentFormGroup.addControl(this.controlName()??HostTestTypedFormComponent.CONTROL_NAME, this.createdFormGroup);
1121
1399
  return this.createdFormGroup;
1122
1400
  }
1123
1401
  patchValue(newVal) {
@@ -1274,108 +1552,6 @@ function generateFormControl(formDescription) {
1274
1552
  return new FormControl(formDescription);
1275
1553
  }
1276
1554
 
1277
- /**
1278
- * A very light and not 100% compatible storemanager in case you are not using xt-store.
1279
- * It can emulate XtStoreManager to some extends for doing some tests
1280
- */
1281
- class StoreTestHelper {
1282
- static ensureTestProviderOnly() {
1283
- StoreSupport.setTestStoreManager(new TestStoreManager());
1284
- }
1285
- }
1286
- class TestStoreManager {
1287
- constructor() {
1288
- this.defaultProvider = new TestStoreProvider();
1289
- }
1290
- getProvider(name) {
1291
- return this.defaultProvider;
1292
- }
1293
- getProviderSafe(name) {
1294
- return this.defaultProvider;
1295
- }
1296
- getDefaultProvider() {
1297
- return this.defaultProvider;
1298
- }
1299
- getDefaultProviderSafe() {
1300
- return this.defaultProvider;
1301
- }
1302
- }
1303
- class TestStoreProvider {
1304
- constructor() {
1305
- this.data = new Map();
1306
- }
1307
- getOrCreateArray(name) {
1308
- let ret = this.data.get(name);
1309
- if (ret == null) {
1310
- ret = new Map();
1311
- this.data.set(name, ret);
1312
- }
1313
- return ret;
1314
- }
1315
- extractKey(value, create) {
1316
- if (value._id != null)
1317
- return value._id; // ManagedData key
1318
- else if (value.id != null)
1319
- return value.id;
1320
- else {
1321
- if (create === true) {
1322
- const newId = new Date().getTime().toString();
1323
- value._id = newId;
1324
- return newId;
1325
- }
1326
- return value.toString();
1327
- }
1328
- }
1329
- storeEntity(name, entity) {
1330
- this.getOrCreateArray(name).set(this.extractKey(entity, true), entity);
1331
- return Promise.resolve(entity);
1332
- }
1333
- safeLoadEntity(name, key) {
1334
- const ret = this.getOrCreateArray(name).get(key);
1335
- if (ret == null) {
1336
- throw new Error("No entity named " + name + " with key " + key);
1337
- }
1338
- return Promise.resolve(ret);
1339
- }
1340
- loadEntity(name, key) {
1341
- return Promise.resolve(this.getOrCreateArray(name).get(key));
1342
- }
1343
- deleteEntity(name, key) {
1344
- return Promise.resolve(this.getOrCreateArray(name).delete(key));
1345
- }
1346
- searchEntities(name, ...criteria) {
1347
- if ((criteria != null) && (criteria.length > 0)) {
1348
- throw new Error('Method not implemented.');
1349
- }
1350
- // No criteria defined, just send the full list
1351
- const ret = new Array();
1352
- for (const toAdd of this.getOrCreateArray(name).values()) {
1353
- ret.push(toAdd);
1354
- }
1355
- return from([ret]);
1356
- }
1357
- searchAndPrepareEntities(name, sort, groupBy, transformer, ...criteria) {
1358
- throw new Error('Method not implemented.');
1359
- }
1360
- canStoreDocument() {
1361
- return true;
1362
- }
1363
- storeDocument(toStore) {
1364
- const ret = new TestDocumentInfo(toStore.name, true, URL.createObjectURL(toStore));
1365
- return Promise.resolve(ret);
1366
- }
1367
- storeDocuments(toStore) {
1368
- throw new Error('Method not implemented.');
1369
- }
1370
- }
1371
- class TestDocumentInfo {
1372
- constructor(documentName, isUrl, documentId) {
1373
- this.documentId = documentId;
1374
- this.documentName = documentName;
1375
- this.isUrl = isUrl;
1376
- }
1377
- }
1378
-
1379
1555
  /*
1380
1556
  * Public API Surface of xt-components
1381
1557
  */
@@ -1384,5 +1560,5 @@ class TestDocumentInfo {
1384
1560
  * Generated bundle index. Do not edit.
1385
1561
  */
1386
1562
 
1387
- export { HostTestFormComponent, HostTestSimpleComponent, HostTestTypedComponent, HostTestTypedFormComponent, StoreSupport, StoreTestHelper, TestDocumentInfo, 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 };
1563
+ 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 };
1388
1564
  //# sourceMappingURL=xt-components.mjs.map