xt-components 0.6.4 → 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.
- package/fesm2022/xt-components.mjs +866 -269
- package/fesm2022/xt-components.mjs.map +1 -1
- package/package.json +15 -14
- package/types/xt-components.d.ts +1367 -0
- package/index.d.ts +0 -670
|
@@ -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 {
|
|
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
|
-
|
|
17
|
-
this.
|
|
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
|
|
193
|
-
* @param parentGroup
|
|
194
|
-
* @param
|
|
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
|
-
|
|
466
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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: "
|
|
932
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
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: "
|
|
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
|
-
|
|
953
|
-
this.
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
this.
|
|
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
|
-
|
|
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: "
|
|
1029
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "
|
|
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: "
|
|
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
|
-
|
|
1046
|
-
this.
|
|
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
|
-
|
|
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: "
|
|
1097
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "
|
|
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: "
|
|
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
|
-
|
|
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
|
-
*
|
|
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: "
|
|
1203
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "
|
|
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: "
|
|
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,42 +1471,65 @@ 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: "
|
|
1255
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
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: "
|
|
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: "
|
|
1270
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
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: "
|
|
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))
|
|
@@ -1289,6 +1543,13 @@ function attachToFormGroup(formGroup, controlName, value, valueType, resolver) {
|
|
|
1289
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
|
-
|
|
1366
|
-
this.
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
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: "
|
|
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,38 +1669,55 @@ 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
|
-
|
|
1390
|
-
this.
|
|
1391
|
-
|
|
1392
|
-
this.
|
|
1393
|
-
|
|
1394
|
-
this.
|
|
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. */
|
|
1395
1683
|
this.parentFormGroup = this.builder.group({});
|
|
1684
|
+
/** Computed signal returning the created form group. */
|
|
1396
1685
|
this.createdFormGroup = computed(() => {
|
|
1397
1686
|
return this.computeFormGroup();
|
|
1398
|
-
}, ...(ngDevMode ? [{ debugName: "createdFormGroup" }] : []));
|
|
1687
|
+
}, ...(ngDevMode ? [{ debugName: "createdFormGroup" }] : /* istanbul ignore next */ []));
|
|
1399
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
|
+
*/
|
|
1400
1693
|
computeFormGroup() {
|
|
1401
1694
|
const formGroup = this.formGroup();
|
|
1402
1695
|
let createdFormGroup = formGroup ?? generateFormGroup(this.formDescription());
|
|
1403
1696
|
this.parentFormGroup.addControl(this.controlName() ?? HostTestTypedFormComponent.CONTROL_NAME, createdFormGroup);
|
|
1404
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
1706
|
const createdFormGroup = this.createdFormGroup();
|
|
1410
1707
|
createdFormGroup.patchValue(patch);
|
|
1411
1708
|
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Retrieves the current value from the component's form control.
|
|
1711
|
+
* @returns The current form value
|
|
1712
|
+
*/
|
|
1412
1713
|
retrieveValue() {
|
|
1413
1714
|
const createdFormGroup = this.createdFormGroup();
|
|
1414
1715
|
return createdFormGroup.value[this.controlName()];
|
|
1415
1716
|
}
|
|
1416
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1417
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "
|
|
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"] }] }); }
|
|
1418
1719
|
}
|
|
1419
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1720
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: HostTestFormComponent, decorators: [{
|
|
1420
1721
|
type: Component,
|
|
1421
1722
|
args: [{
|
|
1422
1723
|
selector: 'test-form-host',
|
|
@@ -1431,20 +1732,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
1431
1732
|
*/
|
|
1432
1733
|
class HostTestTypedComponent {
|
|
1433
1734
|
constructor() {
|
|
1434
|
-
|
|
1435
|
-
this.
|
|
1436
|
-
|
|
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. */
|
|
1437
1742
|
this.context = computed(() => {
|
|
1438
1743
|
const ret = new XtBaseContext(this.displayMode());
|
|
1439
1744
|
ret.valueType = this.valueType();
|
|
1440
1745
|
ret.setDisplayValue(this.value());
|
|
1441
1746
|
return ret;
|
|
1442
|
-
}, ...(ngDevMode ? [{ debugName: "context" }] : []));
|
|
1747
|
+
}, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
|
|
1443
1748
|
}
|
|
1444
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1445
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "
|
|
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"] }] }); }
|
|
1446
1751
|
}
|
|
1447
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1752
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: HostTestTypedComponent, decorators: [{
|
|
1448
1753
|
type: Component,
|
|
1449
1754
|
args: [{
|
|
1450
1755
|
selector: 'test-typed-host',
|
|
@@ -1460,18 +1765,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
1460
1765
|
*/
|
|
1461
1766
|
class HostTestTypedFormComponent {
|
|
1462
1767
|
constructor() {
|
|
1768
|
+
/** Injected FormBuilder for creating form groups. */
|
|
1463
1769
|
this.builder = inject(FormBuilder);
|
|
1770
|
+
/** Injected resolver service for type resolution. */
|
|
1464
1771
|
this.resolver = inject(XtResolverService);
|
|
1465
|
-
|
|
1466
|
-
this.
|
|
1467
|
-
|
|
1468
|
-
this.
|
|
1469
|
-
|
|
1470
|
-
this.
|
|
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. */
|
|
1471
1781
|
this.parentFormGroup = this.builder.group({});
|
|
1782
|
+
/** Computed signal returning the created form group. */
|
|
1472
1783
|
this.createdFormGroup = computed(() => {
|
|
1473
1784
|
return this.computeFormGroup();
|
|
1474
|
-
}, ...(ngDevMode ? [{ debugName: "createdFormGroup" }] : []));
|
|
1785
|
+
}, ...(ngDevMode ? [{ debugName: "createdFormGroup" }] : /* istanbul ignore next */ []));
|
|
1786
|
+
/** Computed signal that builds the XtBaseContext from form group and control name. */
|
|
1475
1787
|
this.subContext = computed(() => {
|
|
1476
1788
|
const ctrlName = this.controlName();
|
|
1477
1789
|
const createdFormGroup = this.createdFormGroup();
|
|
@@ -1484,11 +1796,16 @@ class HostTestTypedFormComponent {
|
|
|
1484
1796
|
}
|
|
1485
1797
|
ret.valueType = this.valueType();
|
|
1486
1798
|
return ret;
|
|
1487
|
-
}, ...(ngDevMode ? [{ debugName: "subContext" }] : []));
|
|
1799
|
+
}, ...(ngDevMode ? [{ debugName: "subContext" }] : /* istanbul ignore next */ []));
|
|
1488
1800
|
}
|
|
1801
|
+
/** Default control name used if none is provided. */
|
|
1489
1802
|
static { this.CONTROL_NAME = 'ForTest'; }
|
|
1490
1803
|
ngOnInit() {
|
|
1491
1804
|
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Computes the form group from inputs, generating one from description or type if needed.
|
|
1807
|
+
* @returns The created FormGroup
|
|
1808
|
+
*/
|
|
1492
1809
|
computeFormGroup() {
|
|
1493
1810
|
let createdFormGroup = null;
|
|
1494
1811
|
const formGroup = this.formGroup();
|
|
@@ -1504,18 +1821,28 @@ class HostTestTypedFormComponent {
|
|
|
1504
1821
|
this.parentFormGroup.addControl(this.controlName() ?? HostTestTypedFormComponent.CONTROL_NAME, createdFormGroup);
|
|
1505
1822
|
return createdFormGroup;
|
|
1506
1823
|
}
|
|
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
|
+
*/
|
|
1507
1829
|
patchValue(controlName, newVal) {
|
|
1508
1830
|
const patch = {};
|
|
1509
1831
|
patch[controlName] = newVal;
|
|
1510
1832
|
this.createdFormGroup().patchValue(patch);
|
|
1511
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
|
+
*/
|
|
1512
1839
|
retrieveValue(controlName) {
|
|
1513
1840
|
return this.createdFormGroup().value[controlName];
|
|
1514
1841
|
}
|
|
1515
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1516
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "
|
|
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"] }] }); }
|
|
1517
1844
|
}
|
|
1518
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1845
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: HostTestTypedFormComponent, decorators: [{
|
|
1519
1846
|
type: Component,
|
|
1520
1847
|
args: [{
|
|
1521
1848
|
selector: 'test-typed-form-host',
|
|
@@ -1524,12 +1851,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
1524
1851
|
template: '<h1>Test Typed Form Component</h1> <form [formGroup]="parentFormGroup"> <xt-render-sub [context]="subContext()"></xt-render-sub></form>'
|
|
1525
1852
|
}]
|
|
1526
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
|
+
*/
|
|
1527
1859
|
function generateFormGroup(formDescription) {
|
|
1528
1860
|
if (typeof formDescription != 'object') {
|
|
1529
1861
|
throw new Error('Form Description should be an object of values');
|
|
1530
1862
|
}
|
|
1531
1863
|
return generateFormControl(formDescription);
|
|
1532
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
|
+
*/
|
|
1533
1871
|
function generateFormControl(formDescription) {
|
|
1534
1872
|
if (formDescription == null) {
|
|
1535
1873
|
return new FormControl(formDescription);
|
|
@@ -1551,6 +1889,265 @@ function generateFormControl(formDescription) {
|
|
|
1551
1889
|
return new FormControl(formDescription);
|
|
1552
1890
|
}
|
|
1553
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
|
+
|
|
1554
2151
|
/*
|
|
1555
2152
|
* Public API Surface of xt-components
|
|
1556
2153
|
*/
|
|
@@ -1559,5 +2156,5 @@ function generateFormControl(formDescription) {
|
|
|
1559
2156
|
* Generated bundle index. Do not edit.
|
|
1560
2157
|
*/
|
|
1561
2158
|
|
|
1562
|
-
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 };
|
|
1563
2160
|
//# sourceMappingURL=xt-components.mjs.map
|