ngx-dynamic-entity-builder 1.3.0 → 1.5.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/README.md +2 -1
- package/fesm2022/ngx-dynamic-entity-builder.mjs +521 -105
- package/fesm2022/ngx-dynamic-entity-builder.mjs.map +1 -1
- package/package.json +7 -7
- package/types/ngx-dynamic-entity-builder.d.ts +582 -0
- package/esm2022/lib/builder-store.service.mjs +0 -1057
- package/esm2022/lib/clone.mjs +0 -14
- package/esm2022/lib/components/entity-builder-canvas.component.mjs +0 -84
- package/esm2022/lib/components/entity-builder-tree-node.component.mjs +0 -192
- package/esm2022/lib/components/entity-reference-config.component.mjs +0 -405
- package/esm2022/lib/components/field-inspector.component.mjs +0 -139
- package/esm2022/lib/components/field-palette.component.mjs +0 -57
- package/esm2022/lib/components/field-rules-list.component.mjs +0 -224
- package/esm2022/lib/components/referenced-field-config.component.mjs +0 -235
- package/esm2022/lib/components/rule-form.component.mjs +0 -201
- package/esm2022/lib/components/tab-manager.component.mjs +0 -354
- package/esm2022/lib/entity-builder.component.mjs +0 -159
- package/esm2022/lib/field-catalog.mjs +0 -6
- package/esm2022/ngx-dynamic-entity-builder.mjs +0 -5
- package/esm2022/public-api.mjs +0 -16
- package/index.d.ts +0 -5
- package/lib/builder-store.service.d.ts +0 -206
- package/lib/clone.d.ts +0 -9
- package/lib/components/entity-builder-canvas.component.d.ts +0 -9
- package/lib/components/entity-builder-tree-node.component.d.ts +0 -17
- package/lib/components/entity-reference-config.component.d.ts +0 -29
- package/lib/components/field-inspector.component.d.ts +0 -33
- package/lib/components/field-palette.component.d.ts +0 -13
- package/lib/components/field-rules-list.component.d.ts +0 -19
- package/lib/components/referenced-field-config.component.d.ts +0 -24
- package/lib/components/rule-form.component.d.ts +0 -16
- package/lib/components/tab-manager.component.d.ts +0 -28
- package/lib/entity-builder.component.d.ts +0 -45
- package/lib/field-catalog.d.ts +0 -5
- package/public-api.d.ts +0 -12
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngx-dynamic-entity-builder",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Angular standalone visual builder for authoring dynamic-entity EntityConfig (fields, tabs, validators, RBAC).",
|
|
5
|
+
"//typings": "Local workspace resolution only: ng-packagr 21 writes types/ngx-dynamic-entity-builder.d.ts into dist/package.json. Do NOT add a 'main' field here — ng-packagr does not generate one, so it would be copied verbatim and resolve wrongly in the published package.",
|
|
6
|
+
"typings": "types/ngx-dynamic-entity-builder.d.ts",
|
|
5
7
|
"peerDependencies": {
|
|
6
8
|
"@angular/cdk": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
7
9
|
"@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
8
10
|
"@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
9
11
|
"@angular/forms": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
10
12
|
"@angular/material": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
11
|
-
"@dynamic-entity/core": "^1.
|
|
12
|
-
"ngx-dynamic-entity": "^1.
|
|
13
|
+
"@dynamic-entity/core": "^1.5.0",
|
|
14
|
+
"ngx-dynamic-entity": "^1.5.0",
|
|
13
15
|
"rxjs": "^7.0.0"
|
|
14
16
|
},
|
|
15
17
|
"keywords": [
|
|
@@ -33,18 +35,16 @@
|
|
|
33
35
|
},
|
|
34
36
|
"author": "Nizamudeen",
|
|
35
37
|
"module": "fesm2022/ngx-dynamic-entity-builder.mjs",
|
|
36
|
-
"typings": "index.d.ts",
|
|
37
38
|
"exports": {
|
|
38
39
|
"./package.json": {
|
|
39
40
|
"default": "./package.json"
|
|
40
41
|
},
|
|
41
42
|
".": {
|
|
42
|
-
"types": "./
|
|
43
|
-
"esm2022": "./esm2022/ngx-dynamic-entity-builder.mjs",
|
|
44
|
-
"esm": "./esm2022/ngx-dynamic-entity-builder.mjs",
|
|
43
|
+
"types": "./types/ngx-dynamic-entity-builder.d.ts",
|
|
45
44
|
"default": "./fesm2022/ngx-dynamic-entity-builder.mjs"
|
|
46
45
|
}
|
|
47
46
|
},
|
|
47
|
+
"type": "module",
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"tslib": "^2.3.0"
|
|
50
50
|
}
|
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { OnChanges, EventEmitter, SimpleChanges } from '@angular/core';
|
|
3
|
+
import { NestedTabConfig, NestedFieldConfig, EntityFormConfig, FormRule, EntityPermissions, RichFieldType, FlagValidator, ParamValidator, DropdownOption, EntityReferenceConfig, AutoPatchMapping, PatchOnTrueMapping, CommonModuleEntry, FieldTypeMeta, RuleOperator } from '@dynamic-entity/core';
|
|
4
|
+
export { AutoPatchConfig, AutoPatchMapping, DropdownOption, EntityFormConfig, EntityPermissions, EntityReferenceConfig, FIELD_TYPE_CATALOG, FieldTypeMeta, FlagValidator, FormRule, NestedFieldConfig, NestedTabConfig, ParamValidator, PatchOnTrueMapping, RichFieldType, RuleAction, RuleCondition, RuleOperator, createFieldConfig, getFieldTypeMeta, humanizeId } from '@dynamic-entity/core';
|
|
5
|
+
import * as ngx_dynamic_entity from 'ngx-dynamic-entity';
|
|
6
|
+
export { SYSTEM_DEFAULT_CAN_EDIT } from 'ngx-dynamic-entity';
|
|
7
|
+
import { CdkDragDrop } from '@angular/cdk/drag-drop';
|
|
8
|
+
|
|
9
|
+
interface BuilderProblem {
|
|
10
|
+
level: 'error' | 'warning';
|
|
11
|
+
message: string;
|
|
12
|
+
fieldId?: string;
|
|
13
|
+
}
|
|
14
|
+
/** One tab's own fields, for a canvas that has to reorder within the right tab. */
|
|
15
|
+
interface BuilderFieldGroup {
|
|
16
|
+
tabId: string;
|
|
17
|
+
label: NestedTabConfig['label'];
|
|
18
|
+
fields: NestedFieldConfig[];
|
|
19
|
+
}
|
|
20
|
+
declare class BuilderStore {
|
|
21
|
+
private readonly _config;
|
|
22
|
+
private readonly _selectedFieldId;
|
|
23
|
+
private readonly _activeLanguage;
|
|
24
|
+
/** Rules live beside the config: they are persisted per form, not inside `EntityFormConfig`. */
|
|
25
|
+
private readonly _rules;
|
|
26
|
+
/**
|
|
27
|
+
* Field ids the author owns — set by an explicit rename, or by loading a saved config
|
|
28
|
+
* whose ids are already live data keys. Ids outside this set are still derived from the
|
|
29
|
+
* field's label. Builder-session state, deliberately not part of the exported config.
|
|
30
|
+
*/
|
|
31
|
+
private readonly manualIds;
|
|
32
|
+
/**
|
|
33
|
+
* Fields whose `refererField` the config declared, rather than the builder stamping it.
|
|
34
|
+
*
|
|
35
|
+
* A declared path is a deliberate binding override and must survive a structural edit. A
|
|
36
|
+
* stamped one is just the field's address, so it has to be rewritten when the field moves.
|
|
37
|
+
* Without the distinction, restamping would destroy the override and not restamping would
|
|
38
|
+
* leave a moved field pointing at where it used to be.
|
|
39
|
+
*/
|
|
40
|
+
private readonly authoredRefs;
|
|
41
|
+
private readonly _isDirty;
|
|
42
|
+
/**
|
|
43
|
+
* Roles of the person using the builder — distinct from `availableRoles`, which is the
|
|
44
|
+
* vocabulary a schema may reference. Needed so the SYSTEM_DEFAULT_CAN_EDIT predicate can
|
|
45
|
+
* be asked a real question; it was previously called with a hardcoded empty array, which
|
|
46
|
+
* made any role-checking predicate answer false for everyone.
|
|
47
|
+
*/
|
|
48
|
+
private readonly _userRoles;
|
|
49
|
+
readonly config: _angular_core.Signal<EntityFormConfig>;
|
|
50
|
+
readonly selectedFieldId: _angular_core.Signal<string>;
|
|
51
|
+
readonly activeLanguage: _angular_core.Signal<string>;
|
|
52
|
+
readonly rules: _angular_core.Signal<FormRule[]>;
|
|
53
|
+
readonly isDirty: _angular_core.Signal<boolean>;
|
|
54
|
+
readonly userRoles: _angular_core.Signal<string[]>;
|
|
55
|
+
readonly tabs: _angular_core.Signal<NestedTabConfig[]>;
|
|
56
|
+
/**
|
|
57
|
+
* Every field the config declares, sub-tabs included.
|
|
58
|
+
*
|
|
59
|
+
* This used to stop at top-level tabs, which quietly broke three things at once: nine of
|
|
60
|
+
* `insuranceClaims`'s twenty-eight fields never appeared on the canvas, the entity-reference
|
|
61
|
+
* picker could not offer a nested field as a source, and the drift check looked a nested
|
|
62
|
+
* field up here, got nothing, and returned without checking. The store's structural
|
|
63
|
+
* operations already walked the whole tree — only this view did not.
|
|
64
|
+
*
|
|
65
|
+
* It deliberately does not descend into a field's own `children`: `group` and `array`
|
|
66
|
+
* children are rendered by the row that owns them, not as rows of their own.
|
|
67
|
+
*/
|
|
68
|
+
readonly fields: _angular_core.Signal<NestedFieldConfig[]>;
|
|
69
|
+
/**
|
|
70
|
+
* The same fields, kept in their owning tab.
|
|
71
|
+
*
|
|
72
|
+
* Drag-and-drop reorders by index, so the canvas needs to know which tab an index belongs
|
|
73
|
+
* to — `reorderField` has always taken a `tabId` for exactly this, and the canvas simply
|
|
74
|
+
* never passed one. Without the grouping a drag on any config with more than one tab
|
|
75
|
+
* reordered `tabs[0]` regardless of what was actually dragged.
|
|
76
|
+
*/
|
|
77
|
+
readonly fieldGroups: _angular_core.Signal<BuilderFieldGroup[]>;
|
|
78
|
+
readonly selectedField: _angular_core.Signal<NestedFieldConfig>;
|
|
79
|
+
readonly problems: _angular_core.Signal<BuilderProblem[]>;
|
|
80
|
+
readonly errors: _angular_core.Signal<BuilderProblem[]>;
|
|
81
|
+
readonly isValid: _angular_core.Signal<boolean>;
|
|
82
|
+
load(config: EntityFormConfig): void;
|
|
83
|
+
reset(entity?: string): void;
|
|
84
|
+
resetDirty(): void;
|
|
85
|
+
private emptyConfig;
|
|
86
|
+
setEntityName(entity: string): void;
|
|
87
|
+
setDefaultLanguage(language: string): void;
|
|
88
|
+
setMaskData(mask: boolean): void;
|
|
89
|
+
setPermission(action: keyof EntityPermissions, roles: string[]): void;
|
|
90
|
+
setActiveLanguage(language: string): void;
|
|
91
|
+
/** Roles of the person using the builder, for SYSTEM_DEFAULT_CAN_EDIT. */
|
|
92
|
+
setUserRoles(roles: string[]): void;
|
|
93
|
+
private getAllFields;
|
|
94
|
+
/**
|
|
95
|
+
* Every tab in the tree, flattened.
|
|
96
|
+
*
|
|
97
|
+
* The structural operations below used to iterate `draft.tabs` directly, which is only
|
|
98
|
+
* the top level. A field on a sub-tab could be selected and edited but never removed,
|
|
99
|
+
* duplicated, moved or reordered — the builder could author nested configs it was then
|
|
100
|
+
* unable to restructure, which is the worst possible state for an authoring tool.
|
|
101
|
+
*/
|
|
102
|
+
private flattenTabs;
|
|
103
|
+
/**
|
|
104
|
+
* Every field id in the config, including `group`/`array` children.
|
|
105
|
+
*
|
|
106
|
+
* Id uniqueness is a whole-config invariant, not a per-tab one: rules, `showWhen`
|
|
107
|
+
* conditions and `autoPatch` mappings all address fields by bare id, so two fields
|
|
108
|
+
* sharing an id anywhere in the tree interfere with each other. Uniqueness checks must
|
|
109
|
+
* therefore see deeper than `fields()`, which stops at a tab's own fields.
|
|
110
|
+
*/
|
|
111
|
+
private allFieldIds;
|
|
112
|
+
private findFieldInTabs;
|
|
113
|
+
addField(type: RichFieldType, targetTabId?: string): string;
|
|
114
|
+
updateField(id: string, patch: Partial<NestedFieldConfig>): void;
|
|
115
|
+
/**
|
|
116
|
+
* Rename a field explicitly, pinning the id so it is no longer derived from the label.
|
|
117
|
+
*
|
|
118
|
+
* Not reachable from the inspector — the id input is read-only by design. This exists for
|
|
119
|
+
* programmatic callers that must match an id to an existing data key.
|
|
120
|
+
*/
|
|
121
|
+
renameField(oldId: string, newId: string): void;
|
|
122
|
+
/** Whether this field's id is pinned (loaded from storage or explicitly renamed). */
|
|
123
|
+
hasManualId(id: string): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Move a field to a new id and repoint every id-based reference in the config at it.
|
|
126
|
+
*
|
|
127
|
+
* Ids are the wiring between fields — rules, cascades, patches and `showWhen` all address
|
|
128
|
+
* fields by id — so a rename that only touches `field.id` silently breaks that wiring.
|
|
129
|
+
* That was survivable while renaming was a rare manual act; label-derived ids make it
|
|
130
|
+
* routine, so the references move with it.
|
|
131
|
+
*/
|
|
132
|
+
private applyRename;
|
|
133
|
+
removeField(id: string): void;
|
|
134
|
+
duplicateField(id: string): string | null;
|
|
135
|
+
moveField(id: string, direction: -1 | 1): void;
|
|
136
|
+
/**
|
|
137
|
+
* Moves a field to another tab, keeping its id.
|
|
138
|
+
*
|
|
139
|
+
* The builder could add, remove, duplicate and reorder a field but never relocate one, so
|
|
140
|
+
* a field authored on the wrong tab had to be deleted and rebuilt — losing its validators,
|
|
141
|
+
* options and any rule that pointed at it.
|
|
142
|
+
*
|
|
143
|
+
* The field's `refererField` changes, because it is an address and the field now has a new one.
|
|
144
|
+
* `mutate` restamps it and repoints the rules that named it, so a move does not silently
|
|
145
|
+
* strand them.
|
|
146
|
+
*/
|
|
147
|
+
moveFieldToTab(id: string, targetTabId: string): boolean;
|
|
148
|
+
/** Safe reorder for drag & drop within active tab or flat list */
|
|
149
|
+
reorderField(fromIndex: number, toIndex: number, tabId?: string): void;
|
|
150
|
+
selectField(id: string | null): void;
|
|
151
|
+
/**
|
|
152
|
+
* Set a field's label, and derive the field's id from it: type "Employee Count", get
|
|
153
|
+
* `employeeCount`. The inspector renders the id read-only, so this is the only way a
|
|
154
|
+
* new field's id is set.
|
|
155
|
+
*
|
|
156
|
+
* Two things stop derivation: a config loaded from storage (its ids are live data keys
|
|
157
|
+
* that records are already stored under — see `load`), and an explicit `renameField`
|
|
158
|
+
* call, which remains available to programmatic callers.
|
|
159
|
+
*
|
|
160
|
+
* Only the config's default language drives the id; translating a label must not rename
|
|
161
|
+
* anything.
|
|
162
|
+
*/
|
|
163
|
+
setFieldLabel(id: string, language: string, value: string): void;
|
|
164
|
+
/** `derived`, or `derived_2`, `derived_3`… if another field already holds it. */
|
|
165
|
+
private availableId;
|
|
166
|
+
setFieldPlaceholder(id: string, language: string, value: string): void;
|
|
167
|
+
toggleFlagValidator(id: string, validator: FlagValidator, on: boolean): void;
|
|
168
|
+
setParamValidator(id: string, validator: ParamValidator, value: number | null): void;
|
|
169
|
+
getParamValidator(field: NestedFieldConfig, validator: ParamValidator): number | null;
|
|
170
|
+
hasFlagValidator(field: NestedFieldConfig, validator: FlagValidator): boolean;
|
|
171
|
+
/**
|
|
172
|
+
* Where a choice field's options come from.
|
|
173
|
+
*
|
|
174
|
+
* `entity` is absent on purpose: in this model an entity reference is a field *type*
|
|
175
|
+
* (`entity-ref`), not a data source a dropdown can switch to, so offering it here would mean
|
|
176
|
+
* mutating the field's type — and rebuilding its control — from a source picker. The
|
|
177
|
+
* exclusion that matters within a choice field is inline `options` vs `listName`.
|
|
178
|
+
*/
|
|
179
|
+
fieldDataSource(field: NestedFieldConfig | undefined): 'none' | 'manual' | 'lookup';
|
|
180
|
+
/**
|
|
181
|
+
* Switch a choice field's data source, clearing the one it is leaving.
|
|
182
|
+
*
|
|
183
|
+
* The reference makes these mutually exclusive (doc §4.4) and it has to stay that way here:
|
|
184
|
+
* `optionsFor` resolves inline options first, so a field left holding both would silently
|
|
185
|
+
* ignore its list.
|
|
186
|
+
*/
|
|
187
|
+
setFieldDataSource(fieldId: string, source: 'none' | 'manual' | 'lookup'): void;
|
|
188
|
+
/** Set the named list a choice field reads its options from. */
|
|
189
|
+
setListName(fieldId: string, listName: string): void;
|
|
190
|
+
/** Link a field to a field defined in another entity configuration (Phase 8). */
|
|
191
|
+
linkReferencedField(fieldId: string, sourceEntityKey: string, sourceField: NestedFieldConfig): void;
|
|
192
|
+
/** Unlink a referenced field back to an independent field. */
|
|
193
|
+
unlinkReferencedField(fieldId: string): void;
|
|
194
|
+
/** Sync a drifted referenced field with the updated source field definition. */
|
|
195
|
+
syncReferencedField(fieldId: string, currentSourceField: NestedFieldConfig): void;
|
|
196
|
+
/** Scan all referenced fields in the config and update their hasDrift state against current source configs. */
|
|
197
|
+
checkDrift(sourceConfigs: Record<string, EntityFormConfig>): void;
|
|
198
|
+
addOption(fieldId: string): void;
|
|
199
|
+
/** Merge language keys into an option. `setOptionLabel` is the usual single-language path. */
|
|
200
|
+
updateOption(fieldId: string, index: number, patch: DropdownOption): void;
|
|
201
|
+
/**
|
|
202
|
+
* Set an option's text for one language.
|
|
203
|
+
*
|
|
204
|
+
* An option is a language-keyed object and the displayed text **is** the stored value,
|
|
205
|
+
* so this is the only way to edit one — there is no separate value to set.
|
|
206
|
+
*/
|
|
207
|
+
setOptionLabel(fieldId: string, index: number, language: string, value: string): void;
|
|
208
|
+
removeOption(fieldId: string, index: number): void;
|
|
209
|
+
/** Merge a patch into a field's `entityReference` block, keeping it enabled. */
|
|
210
|
+
updateEntityReference(fieldId: string, patch: Partial<EntityReferenceConfig>): void;
|
|
211
|
+
addAutoPatchMapping(fieldId: string): void;
|
|
212
|
+
setAutoPatchTargetTab(fieldId: string, targetTab: string): void;
|
|
213
|
+
updateAutoPatchMapping(fieldId: string, index: number, patch: Partial<AutoPatchMapping>): void;
|
|
214
|
+
/** Removing the last mapping drops `autoPatch` entirely — no empty config in the export. */
|
|
215
|
+
removeAutoPatchMapping(fieldId: string, index: number): void;
|
|
216
|
+
addPatchOnTrueMapping(fieldId: string): void;
|
|
217
|
+
updatePatchOnTrueMapping(fieldId: string, index: number, patch: Partial<PatchOnTrueMapping>): void;
|
|
218
|
+
removePatchOnTrueMapping(fieldId: string, index: number): void;
|
|
219
|
+
/** Replace a field's `showWhen` map; an empty map removes the condition. */
|
|
220
|
+
setShowWhen(fieldId: string, showWhen: Record<string, unknown> | undefined): void;
|
|
221
|
+
/** Rules for the field currently selected in the inspector. */
|
|
222
|
+
/**
|
|
223
|
+
* Rules attached to the selected field, whichever way they name it.
|
|
224
|
+
*
|
|
225
|
+
* New rules are authored with the field's path — `[work.address]` — because a bare id
|
|
226
|
+
* cannot distinguish two fields that share one. Rules written before paths existed name it
|
|
227
|
+
* by id, and those must keep showing up here or editing an older config would look like it
|
|
228
|
+
* had lost its rules.
|
|
229
|
+
*/
|
|
230
|
+
readonly rulesForSelectedField: _angular_core.Signal<FormRule[]>;
|
|
231
|
+
loadRules(rules: FormRule[]): void;
|
|
232
|
+
addRule(rule: FormRule): string;
|
|
233
|
+
updateRule(id: string, patch: Partial<FormRule>): void;
|
|
234
|
+
removeRule(id: string): void;
|
|
235
|
+
toggleRule(id: string, enabled: boolean): void;
|
|
236
|
+
/** Move a rule in priority order. Priorities are renumbered 1..n so they stay contiguous. */
|
|
237
|
+
moveRule(id: string, direction: -1 | 1): void;
|
|
238
|
+
exportRules(): FormRule[];
|
|
239
|
+
addTab(): string;
|
|
240
|
+
/** Every tab id in the tree, at any depth. Tab ids must be globally unique — they are paths. */
|
|
241
|
+
private allTabIds;
|
|
242
|
+
addSubTab(parentId: string): string;
|
|
243
|
+
setPrimaryTab(tabId: string): void;
|
|
244
|
+
updateTab(id: string, patch: Partial<NestedTabConfig>): void;
|
|
245
|
+
setTabLabel(id: string, language: string, value: string): void;
|
|
246
|
+
removeTab(id: string): void;
|
|
247
|
+
moveTab(id: string, direction: -1 | 1): void;
|
|
248
|
+
exportConfig(): EntityFormConfig;
|
|
249
|
+
/**
|
|
250
|
+
* The single choke point for a structural edit, and therefore the one place refs are kept
|
|
251
|
+
* true.
|
|
252
|
+
*
|
|
253
|
+
* `refererField` is a field's address, so it changes whenever the structure around it does —
|
|
254
|
+
* moving a field to another tab, or into a group, gives it a new one. Rules address fields
|
|
255
|
+
* by ref, so a move that only restamped the field would leave every rule pointing at an
|
|
256
|
+
* address nothing occupies. Both happen here, together, or the config is inconsistent
|
|
257
|
+
* between them.
|
|
258
|
+
*/
|
|
259
|
+
private mutate;
|
|
260
|
+
/**
|
|
261
|
+
* Rewrites the address of every field the builder owns, leaving declared overrides alone.
|
|
262
|
+
*
|
|
263
|
+
* `assignFieldRefs` only fills in a missing path, which is right when a config arrives and
|
|
264
|
+
* wrong after a move: the field already has a path, and it is now the wrong one.
|
|
265
|
+
*/
|
|
266
|
+
private restampFieldPaths;
|
|
267
|
+
/** Field id → the refs it currently has. A duplicated id has more than one. */
|
|
268
|
+
private refsById;
|
|
269
|
+
/**
|
|
270
|
+
* Rewrites rules that pointed at a ref a move has just changed.
|
|
271
|
+
*
|
|
272
|
+
* A field is matched by its id: if exactly one ref for that id disappeared and exactly one
|
|
273
|
+
* appeared, that is the move and the rules follow it. Anything less clear-cut — two fields
|
|
274
|
+
* sharing an id where one moved — is left alone rather than guessed at, because rewriting
|
|
275
|
+
* the wrong rule is worse than leaving one to be repointed by hand.
|
|
276
|
+
*/
|
|
277
|
+
private repointRulesForMovedFields;
|
|
278
|
+
/**
|
|
279
|
+
* Replace one field, copying only the objects on the path to it.
|
|
280
|
+
*
|
|
281
|
+
* `mutate` deep-clones the entire config, which is right for a structural edit but wrong
|
|
282
|
+
* for the two setters bound to keystroke events: typing a label allocated a full copy of
|
|
283
|
+
* the config per character. Here every tab and field outside the target's chain is reused
|
|
284
|
+
* by reference, so the cost is proportional to nesting depth rather than config size —
|
|
285
|
+
* while the parts that did change are still fresh objects, so change detection and the
|
|
286
|
+
* signal both see a new identity.
|
|
287
|
+
*
|
|
288
|
+
* Returns without touching anything when the field is not found.
|
|
289
|
+
*/
|
|
290
|
+
private mutateField;
|
|
291
|
+
private uniqueId;
|
|
292
|
+
private validate;
|
|
293
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<BuilderStore, never>;
|
|
294
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<BuilderStore>;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* EntityBuilderComponent — the top-level visual builder for an EntityFormConfig.
|
|
299
|
+
*/
|
|
300
|
+
declare class EntityBuilderComponent implements OnChanges {
|
|
301
|
+
protected readonly store: BuilderStore;
|
|
302
|
+
/**
|
|
303
|
+
* Optional: only apps that registered a CONFIG_SOURCE have one. Used to drop the cached
|
|
304
|
+
* copy of a config the moment it is edited — see `doSave`.
|
|
305
|
+
*/
|
|
306
|
+
private readonly configSource;
|
|
307
|
+
/** Existing config to edit. When omitted, the builder starts blank. */
|
|
308
|
+
config?: EntityFormConfig;
|
|
309
|
+
/** Languages available for label/placeholder editing. First entry is the default. */
|
|
310
|
+
languages: string[];
|
|
311
|
+
/** Optional role list — enables multi-select role pickers for RBAC instead of free text. */
|
|
312
|
+
availableRoles: string[];
|
|
313
|
+
/**
|
|
314
|
+
* Roles of the person using the builder. Distinct from `availableRoles`, which is the
|
|
315
|
+
* vocabulary a schema may reference. Passed to the `SYSTEM_DEFAULT_CAN_EDIT` predicate so
|
|
316
|
+
* it can decide whether this user may edit system-default tabs.
|
|
317
|
+
*/
|
|
318
|
+
userRoles: string[];
|
|
319
|
+
/** Optional common-module definitions for module tabs; defaults to the built-in list from core. */
|
|
320
|
+
commonModules: readonly CommonModuleEntry[];
|
|
321
|
+
/** Emitted on every change to the working config. */
|
|
322
|
+
configChange: EventEmitter<EntityFormConfig>;
|
|
323
|
+
/** Emitted when the user clicks Save. Carries a clean, deep-cloned config. */
|
|
324
|
+
save: EventEmitter<EntityFormConfig>;
|
|
325
|
+
protected readonly rbacActions: (keyof EntityPermissions)[];
|
|
326
|
+
constructor();
|
|
327
|
+
ngOnChanges(changes: SimpleChanges): void;
|
|
328
|
+
protected doSave(): void;
|
|
329
|
+
protected get json(): string;
|
|
330
|
+
protected copyJson(): void;
|
|
331
|
+
protected rolesFor(action: keyof EntityPermissions): readonly string[];
|
|
332
|
+
protected setRoles(action: keyof EntityPermissions, roles: string[]): void;
|
|
333
|
+
/** Parse a comma-separated role string into a trimmed, de-duplicated array. */
|
|
334
|
+
protected setRolesFromText(action: keyof EntityPermissions, text: string): void;
|
|
335
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EntityBuilderComponent, never>;
|
|
336
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EntityBuilderComponent, "ngx-entity-builder", never, { "config": { "alias": "config"; "required": false; }; "languages": { "alias": "languages"; "required": false; }; "availableRoles": { "alias": "availableRoles"; "required": false; }; "userRoles": { "alias": "userRoles"; "required": false; }; "commonModules": { "alias": "commonModules"; "required": false; }; }, { "configChange": "configChange"; "save": "save"; }, never, ["[ngxBuilderPreview]"], true, never>;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* FieldPaletteComponent — the list of buildable field types (Angular Material).
|
|
341
|
+
*/
|
|
342
|
+
declare class FieldPaletteComponent {
|
|
343
|
+
readonly catalog: readonly FieldTypeMeta[];
|
|
344
|
+
pick: EventEmitter<RichFieldType>;
|
|
345
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FieldPaletteComponent, never>;
|
|
346
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FieldPaletteComponent, "ngx-field-palette", never, {}, { "pick": "pick"; }, never, never, true, never>;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** One choosable field: the reference stored, the label shown, and the path behind it. */
|
|
350
|
+
interface FieldPathOption {
|
|
351
|
+
value: string;
|
|
352
|
+
label: string;
|
|
353
|
+
path: string;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Every field in the config, offered by path.
|
|
357
|
+
*
|
|
358
|
+
* Four places in the builder name a field — `showWhen` keys, `patchOnTrue` mappings,
|
|
359
|
+
* `autoPatch` targets and rule triggers/targets — and each was a free-text box. Typing an id
|
|
360
|
+
* is the one way left to author an ambiguous reference: ids are unique per scope, so
|
|
361
|
+
* `address` names nothing in particular once two tabs have one. Choosing from this list
|
|
362
|
+
* always yields a path.
|
|
363
|
+
*
|
|
364
|
+
* `collectFieldScopes` rather than the tab-level view, so a field nested inside a `group` is
|
|
365
|
+
* offered too, and every entry carries the scope `refOf` needs.
|
|
366
|
+
*/
|
|
367
|
+
declare function fieldPathOptions(config: EntityFormConfig | null | undefined, language: string): FieldPathOption[];
|
|
368
|
+
/**
|
|
369
|
+
* The options plus whatever is already selected.
|
|
370
|
+
*
|
|
371
|
+
* A reference written before paths existed is a bare id, and one written against a field
|
|
372
|
+
* since deleted is a path nothing answers to. Neither appears in the list, and a `mat-select`
|
|
373
|
+
* silently drops a value it has no option for — so opening such a config and saving it would
|
|
374
|
+
* quietly erase the reference. They are kept, and shown as they are.
|
|
375
|
+
*/
|
|
376
|
+
declare function withExistingOptions(options: readonly FieldPathOption[], current: readonly (string | undefined)[]): FieldPathOption[];
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* FieldInspectorComponent — edits every property of the currently selected field.
|
|
380
|
+
*/
|
|
381
|
+
declare class FieldInspectorComponent {
|
|
382
|
+
protected readonly store: BuilderStore;
|
|
383
|
+
protected readonly field: _angular_core.Signal<NestedFieldConfig>;
|
|
384
|
+
protected readonly meta: _angular_core.Signal<FieldTypeMeta>;
|
|
385
|
+
protected lang(): string;
|
|
386
|
+
protected toNum(value: unknown): number | null;
|
|
387
|
+
protected labelValue(field: NestedFieldConfig): string;
|
|
388
|
+
protected placeholderValue(field: NestedFieldConfig): string;
|
|
389
|
+
protected optionLabel(option: DropdownOption): string;
|
|
390
|
+
/**
|
|
391
|
+
* Every tab a field can live on, sub-tabs included.
|
|
392
|
+
*
|
|
393
|
+
* `moveFieldToTab` existed on the store with nothing calling it, so a field authored on the
|
|
394
|
+
* wrong tab had to be deleted and rebuilt — losing its validators, options and every rule
|
|
395
|
+
* aimed at it.
|
|
396
|
+
*/
|
|
397
|
+
protected readonly tabOptions: _angular_core.Signal<{
|
|
398
|
+
id: string;
|
|
399
|
+
label: string;
|
|
400
|
+
}[]>;
|
|
401
|
+
/**
|
|
402
|
+
* The tab the selected field currently sits on.
|
|
403
|
+
*
|
|
404
|
+
* Looked up by identity rather than parsed out of the path: the scope of
|
|
405
|
+
* `incident.incidentDetails.incidentTime` is two segments and the field belongs to the
|
|
406
|
+
* *last* of them, while a field inside a group has the group id in that position and
|
|
407
|
+
* belongs to no tab of that name at all.
|
|
408
|
+
*/
|
|
409
|
+
protected currentTabId(field: NestedFieldConfig): string;
|
|
410
|
+
protected moveToTab(field: NestedFieldConfig, tabId: string): void;
|
|
411
|
+
/**
|
|
412
|
+
* Fields offered wherever this inspector names one — the `showWhen` key and both ends of a
|
|
413
|
+
* `patchOnTrue` mapping. All three were free text, which is the one way left to write a
|
|
414
|
+
* reference that names two fields at once.
|
|
415
|
+
*/
|
|
416
|
+
protected readonly fieldOptions: _angular_core.Signal<FieldPathOption[]>;
|
|
417
|
+
/** Options plus whatever is already selected, so an unknown reference is never dropped. */
|
|
418
|
+
protected optionsWith(...current: (string | undefined)[]): FieldPathOption[];
|
|
419
|
+
protected showWhenEntries(field: NestedFieldConfig): {
|
|
420
|
+
key: string;
|
|
421
|
+
display: string;
|
|
422
|
+
}[];
|
|
423
|
+
/**
|
|
424
|
+
* Seeds a new condition with the first field not already watched.
|
|
425
|
+
*
|
|
426
|
+
* It used to seed the literal string `field`, which is not a field id at all — so a new
|
|
427
|
+
* condition started out referencing nothing and silently hid the field until it was edited.
|
|
428
|
+
*/
|
|
429
|
+
protected addShowWhen(field: NestedFieldConfig): void;
|
|
430
|
+
protected renameShowWhen(field: NestedFieldConfig, oldKey: string, newKey: string): void;
|
|
431
|
+
protected setShowWhenValue(field: NestedFieldConfig, key: string, raw: string): void;
|
|
432
|
+
protected removeShowWhen(field: NestedFieldConfig, key: string): void;
|
|
433
|
+
/**
|
|
434
|
+
* `showWhen` compares with `!==`, so the authored type has to survive the round trip:
|
|
435
|
+
* "true"/"false" become booleans and numeric text becomes a number.
|
|
436
|
+
*/
|
|
437
|
+
private parseShowWhen;
|
|
438
|
+
private stringifyShowWhen;
|
|
439
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FieldInspectorComponent, never>;
|
|
440
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FieldInspectorComponent, "ngx-field-inspector", never, {}, {}, never, never, true, never>;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* TabManagerComponent — add / rename / reorder / remove tabs & sub-tabs with systemDefault protection.
|
|
445
|
+
*/
|
|
446
|
+
declare class TabManagerComponent {
|
|
447
|
+
protected readonly store: BuilderStore;
|
|
448
|
+
private readonly canEditSystemDefaultsFn;
|
|
449
|
+
protected lang(): string;
|
|
450
|
+
protected tabLabel(tab: {
|
|
451
|
+
label?: any;
|
|
452
|
+
}): string;
|
|
453
|
+
protected canEditTab(tab: NestedTabConfig): boolean;
|
|
454
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TabManagerComponent, never>;
|
|
455
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TabManagerComponent, "ngx-tab-manager", never, {}, {}, never, never, true, never>;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* RuleFormComponent — dialog/panel form for creating or editing a FormRule.
|
|
460
|
+
*/
|
|
461
|
+
declare class RuleFormComponent {
|
|
462
|
+
private readonly store;
|
|
463
|
+
rule: FormRule;
|
|
464
|
+
save: EventEmitter<FormRule>;
|
|
465
|
+
cancel: EventEmitter<void>;
|
|
466
|
+
readonly operators: RuleOperator[];
|
|
467
|
+
protected readonly fieldOptions: _angular_core.Signal<FieldPathOption[]>;
|
|
468
|
+
/**
|
|
469
|
+
* Methods, not `computed`. Both read `rule`, which is a plain `@Input` object mutated in
|
|
470
|
+
* place rather than a signal — a computed would cache the first evaluation and never see a
|
|
471
|
+
* different rule being edited.
|
|
472
|
+
*/
|
|
473
|
+
protected triggerOptions(): FieldPathOption[];
|
|
474
|
+
protected targetOptions(): FieldPathOption[];
|
|
475
|
+
private cachedTargets;
|
|
476
|
+
/**
|
|
477
|
+
* Field targets only — a rule may also target a tab, which this picker does not manage.
|
|
478
|
+
*
|
|
479
|
+
* The array identity is held stable while the contents are unchanged. This is bound through
|
|
480
|
+
* `[ngModel]` on a multi-select, and a fresh array on each call made `ngModel` see a new
|
|
481
|
+
* value on every change-detection pass: it wrote, which scheduled another pass, which built
|
|
482
|
+
* another array. Opening the rule editor locked the browser outright.
|
|
483
|
+
*/
|
|
484
|
+
protected targetValues(): string[];
|
|
485
|
+
/**
|
|
486
|
+
* Replaces the field targets, leaving any tab target untouched.
|
|
487
|
+
*
|
|
488
|
+
* Rebuilding `targets` wholesale from this picker would drop tab targets, which it never
|
|
489
|
+
* offered and so cannot know about.
|
|
490
|
+
*/
|
|
491
|
+
protected setTargets(ids: string[]): void;
|
|
492
|
+
addCondition(): void;
|
|
493
|
+
removeCondition(index: number): void;
|
|
494
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<RuleFormComponent, never>;
|
|
495
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<RuleFormComponent, "ngx-rule-form", never, { "rule": { "alias": "rule"; "required": false; }; }, { "save": "save"; "cancel": "cancel"; }, never, never, true, never>;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* FieldRulesListComponent — the rules attached to the selected field: view, reorder,
|
|
500
|
+
* enable/disable, edit, delete. `RuleFormComponent` authors one rule; this owns the set.
|
|
501
|
+
*/
|
|
502
|
+
declare class FieldRulesListComponent {
|
|
503
|
+
protected readonly store: BuilderStore;
|
|
504
|
+
/** The rule currently open in the editor — a new draft or a copy of an existing rule. */
|
|
505
|
+
protected readonly editing: _angular_core.WritableSignal<FormRule>;
|
|
506
|
+
protected startCreate(): void;
|
|
507
|
+
protected startEdit(rule: FormRule): void;
|
|
508
|
+
protected commit(rule: FormRule): void;
|
|
509
|
+
/** One-line human summary: `status EQUAL "archived" → hide`. */
|
|
510
|
+
protected summarize(rule: FormRule): string;
|
|
511
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FieldRulesListComponent, never>;
|
|
512
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FieldRulesListComponent, "ngx-field-rules-list", never, {}, {}, never, never, true, never>;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* EntityReferenceConfigComponent — authors the `entityReference` block of an entity-ref field:
|
|
517
|
+
* registry key, display fields, static filters, and the parent→child cascade.
|
|
518
|
+
*
|
|
519
|
+
* Also authors `autoPatch`, since the mappings only make sense against the record the
|
|
520
|
+
* selected reference returns.
|
|
521
|
+
*/
|
|
522
|
+
declare class EntityReferenceConfigComponent {
|
|
523
|
+
protected readonly store: BuilderStore;
|
|
524
|
+
protected readonly field: _angular_core.Signal<NestedFieldConfig>;
|
|
525
|
+
/** Sibling fields that can drive a cascade — anything but this field itself. */
|
|
526
|
+
protected readonly parentCandidates: _angular_core.Signal<NestedFieldConfig[]>;
|
|
527
|
+
private readonly fieldOptions;
|
|
528
|
+
/**
|
|
529
|
+
* Cascade parents, offered by path.
|
|
530
|
+
*
|
|
531
|
+
* The picker existed but its values were bare ids, which name a field only while no second
|
|
532
|
+
* scope reuses the id — so the one control that was already a list could still produce an
|
|
533
|
+
* ambiguous reference.
|
|
534
|
+
*/
|
|
535
|
+
protected parentOptions(): FieldPathOption[];
|
|
536
|
+
/** Options plus whatever is already selected, so an unknown reference is never dropped. */
|
|
537
|
+
protected optionsWith(...current: (string | undefined)[]): FieldPathOption[];
|
|
538
|
+
private readonly filtersParseError;
|
|
539
|
+
protected readonly filtersError: _angular_core.Signal<string>;
|
|
540
|
+
protected ref(field: NestedFieldConfig): ngx_dynamic_entity.EntityReferenceConfig;
|
|
541
|
+
protected patchRef(field: NestedFieldConfig, patch: Record<string, unknown>): void;
|
|
542
|
+
protected fieldLabel(field: NestedFieldConfig): string;
|
|
543
|
+
protected tabLabel(tabId: string): string;
|
|
544
|
+
protected displayFieldsText(field: NestedFieldConfig): string;
|
|
545
|
+
protected setDisplayFields(field: NestedFieldConfig, value: string): void;
|
|
546
|
+
protected filtersText(field: NestedFieldConfig): string;
|
|
547
|
+
/** Invalid JSON is reported inline and left unapplied rather than silently dropped. */
|
|
548
|
+
protected setFilters(field: NestedFieldConfig, value: string): void;
|
|
549
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EntityReferenceConfigComponent, never>;
|
|
550
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EntityReferenceConfigComponent, "ngx-entity-reference-config", never, {}, {}, never, never, true, never>;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
declare class EntityBuilderCanvasComponent {
|
|
554
|
+
protected readonly store: BuilderStore;
|
|
555
|
+
/**
|
|
556
|
+
* Headings only earn their space once the fields are actually split across tabs. A
|
|
557
|
+
* single-tab config — the common case, and every existing spec — renders exactly as before.
|
|
558
|
+
*/
|
|
559
|
+
protected showTabHeadings(): boolean;
|
|
560
|
+
protected groupLabel(group: BuilderFieldGroup): string;
|
|
561
|
+
protected onDrop(tabId: string, event: CdkDragDrop<unknown>): void;
|
|
562
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EntityBuilderCanvasComponent, never>;
|
|
563
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EntityBuilderCanvasComponent, "ngx-entity-builder-canvas", never, {}, {}, never, never, true, never>;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
declare class EntityBuilderTreeNodeComponent {
|
|
567
|
+
protected readonly store: BuilderStore;
|
|
568
|
+
field: NestedFieldConfig;
|
|
569
|
+
index: number;
|
|
570
|
+
totalCount: number;
|
|
571
|
+
protected fieldTypeLabel(type: string): string;
|
|
572
|
+
protected fieldTypeIcon(type: string): string;
|
|
573
|
+
protected fieldLabel(field: {
|
|
574
|
+
id: string;
|
|
575
|
+
label?: any;
|
|
576
|
+
}): string;
|
|
577
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EntityBuilderTreeNodeComponent, never>;
|
|
578
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EntityBuilderTreeNodeComponent, "ngx-entity-builder-tree-node", never, { "field": { "alias": "field"; "required": true; }; "index": { "alias": "index"; "required": true; }; "totalCount": { "alias": "totalCount"; "required": true; }; }, {}, never, never, true, never>;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
export { BuilderStore, EntityBuilderCanvasComponent, EntityBuilderComponent, EntityBuilderTreeNodeComponent, EntityReferenceConfigComponent, FieldInspectorComponent, FieldPaletteComponent, FieldRulesListComponent, RuleFormComponent, TabManagerComponent, fieldPathOptions, withExistingOptions };
|
|
582
|
+
export type { BuilderFieldGroup, BuilderProblem, FieldPathOption };
|