@eherve/angular-material-entity-wizard 0.0.0-stage → 22.0.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 CHANGED
@@ -1,3 +1,20 @@
1
- # Temporary Holding Version
1
+ # Angular Material entity wizard
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Independent creation wizard using the shared forms session and renderers. This package has no dependency on edit-entity-dialog.
4
+
5
+ ```ts
6
+ import { CreateEntityWizardService, createEntityWizardOptions, CreateEntityWizardComponentOptions } from '@eherve/angular-material-entity-wizard';
7
+
8
+ const options = createEntityWizardOptions({
9
+ title: 'Create',
10
+ steps: [{ id: 'main', label: 'Main', displayOptions: { section: { id: 'main', label: '', cards: [] } } }],
11
+ service: { create: async value => ({ _id: 'created', ...value }) },
12
+ });
13
+ wizard.open(options);
14
+ ```
15
+
16
+ `CreateEntityWizardComponentOptions<TRecord>` extends the canonical `EditEntityComponentOptions<TRecord>` from `@eherve/angular-material-forms/core` with `wizard`. The normalization helper retains the established wizard configuration shape and options identity when opened. Standard edit options contain no wizard property.
17
+
18
+ Each wizard owns one shared `EntityFormSession`; changing a step does not replace the form. Its scoped save policy projects only current-step and always-in-upsert fields, preserves other-step drafts during server hydration, blocks navigation during saves or pending validation and accumulates created/updated results. Component-managed steps use the supplied `markUpdated` callback. The independent modal wrapper retains top-dialog keyboard and focused-input key ownership behavior.
19
+
20
+ Include `@eherve/angular-material-forms/styles.css` once. Public `EntityWizardComponent` supports page or modal display, and `CreateEntityWizardService` opens its own `EntityWizardDialog`. Version `22.0.0` requires `@eherve/angular-material-forms@^22.0.0` and has no dependency on edit-dialog.
@@ -0,0 +1,660 @@
1
+ import * as i2 from "@angular/forms";
2
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms";
3
+ import { EditEntityConditionalStateService, EditEntityFormBuilderService, EditEntityRenderService, EditEntitySubmitValueService, isNonEditable } from "@eherve/angular-material-forms/core";
4
+ import { cloneDeep, get, has, isEqual, set } from "lodash-es";
5
+ import * as i0 from "@angular/core";
6
+ import { ChangeDetectionStrategy, Component, Inject, Injectable, Input, Output, ViewChild, computed, inject, signal } from "@angular/core";
7
+ import { EntityDialogKeyboardHost, EntityDialogLauncher, EntityFormSession } from "@eherve/angular-material-forms/editor";
8
+ import * as i1 from "@angular/material/dialog";
9
+ import { MAT_DIALOG_DATA } from "@angular/material/dialog";
10
+ import * as i1$1 from "@angular/common";
11
+ import { CommonModule } from "@angular/common";
12
+ import * as i3 from "@angular/material/button";
13
+ import { MatButtonModule } from "@angular/material/button";
14
+ import * as i4 from "@angular/material/icon";
15
+ import { MatIconModule } from "@angular/material/icon";
16
+ import * as i5 from "@angular/material/progress-spinner";
17
+ import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
18
+ import * as i7 from "@angular/material/stepper";
19
+ import { MatStepper, MatStepperModule } from "@angular/material/stepper";
20
+ import * as i6 from "@angular/material/tooltip";
21
+ import { MatTooltipModule } from "@angular/material/tooltip";
22
+ import { FormContentComponent } from "@eherve/angular-material-forms/layout";
23
+ var CreateEntityWizardState = class {
24
+ valid(form, paths) {
25
+ return paths.every((path) => {
26
+ const control = form.getControl(path);
27
+ return !control || control.disabled || !control.pending && control.valid;
28
+ });
29
+ }
30
+ project(form, paths, changedOnly, always = []) {
31
+ const value = {};
32
+ for (const path of /* @__PURE__ */ new Set([...paths, ...always])) {
33
+ const control = form.getControl(path);
34
+ if (!control || isNonEditable(control)) continue;
35
+ const current = control.getRawValue();
36
+ const initial = this.read(form.initialValue, path);
37
+ if (!changedOnly || always.includes(path) || !isEqual(current, initial)) set(value, path, cloneDeep(current));
38
+ }
39
+ return value;
40
+ }
41
+ hydrate(form, record, submitted) {
42
+ const baseline = cloneDeep(form.initialValue);
43
+ const visit = (control, path) => {
44
+ if (control instanceof FormGroup) {
45
+ for (const [key, child] of Object.entries(control.controls)) visit(child, path ? `${path}.${key}` : key);
46
+ return;
47
+ }
48
+ const saved = submitted.some((name) => path === name || path.startsWith(`${name}.`));
49
+ const current = control.getRawValue();
50
+ const draft = !isEqual(current, this.read(baseline, path));
51
+ if (saved || !draft) {
52
+ if (has(record, path)) control.patchValue(cloneDeep(get(record, path)), { emitEvent: false });
53
+ if (Object.prototype.hasOwnProperty.call(form.controls, path)) baseline[path] = cloneDeep(control.getRawValue());
54
+ else set(baseline, path, cloneDeep(control.getRawValue()));
55
+ control.markAsPristine();
56
+ control.markAsUntouched();
57
+ }
58
+ };
59
+ visit(form, "");
60
+ form.setInitialValue(baseline);
61
+ }
62
+ read(value, path) {
63
+ return Object.prototype.hasOwnProperty.call(value, path) ? value[path] : get(value, path);
64
+ }
65
+ };
66
+ var EntityWizardComponent = class EntityWizardComponent {
67
+ options;
68
+ displayMode = "page";
69
+ session = inject(EntityFormSession);
70
+ result = this.session.result;
71
+ closeRequest = this.session.closeRequest;
72
+ ngOnInit() {
73
+ this.session.options = this.options;
74
+ this.session.displayMode = this.displayMode;
75
+ this.session.policy = {
76
+ valid: () => this.wizardValid,
77
+ value: (create) => this.wizardValue(create),
78
+ patch: (record, value) => this.patchWizardRecord(record, value),
79
+ saved: (record, value) => this.hydrateWizard(record, value),
80
+ hydrate: (record) => this.wizardState.hydrate(this.session.formGroup, record, []),
81
+ refresh: () => this.refreshWizardState(),
82
+ reset: () => {
83
+ this.wizardIndex.set(0);
84
+ this.wizardCompleted.set(this.options.wizard.steps.map(() => false));
85
+ this.wizardVisibleIndices.set([]);
86
+ this.wizardNavigationControls = [];
87
+ },
88
+ componentUpdated: () => this.completeWizardStep(),
89
+ beforeClose: () => this.session.formGroup.setInitialValue(this.session.formGroup.initialValue),
90
+ stickyResult: true
91
+ };
92
+ this.session.initialize();
93
+ }
94
+ ngOnChanges(changes) {
95
+ if (changes["options"] && !changes["options"].firstChange) {
96
+ this.session.options = this.options;
97
+ this.session.initialize();
98
+ }
99
+ }
100
+ ngOnDestroy() {
101
+ this.session.destroy();
102
+ }
103
+ requestClose() {
104
+ return this.session.requestClose();
105
+ }
106
+ refreshPresentation() {
107
+ this.session.refreshPresentation();
108
+ }
109
+ submitFromKeyboard() {
110
+ if (!this.options.validateOnEnter || this.session.loading || this.session.updating) return false;
111
+ this.wizardNext();
112
+ return true;
113
+ }
114
+ wizardIndex = signal(0, ...ngDevMode ? [{ debugName: "wizardIndex" }] : /* istanbul ignore next */ []);
115
+ wizardDrafts = signal([], ...ngDevMode ? [{ debugName: "wizardDrafts" }] : /* istanbul ignore next */ []);
116
+ wizardCompleted = signal([], ...ngDevMode ? [{ debugName: "wizardCompleted" }] : /* istanbul ignore next */ []);
117
+ wizardVisibleIndices = signal([], ...ngDevMode ? [{ debugName: "wizardVisibleIndices" }] : /* istanbul ignore next */ []);
118
+ wizardVisualIndex = computed(() => Math.max(0, this.wizardVisibleIndices().indexOf(this.wizardIndex())), ...ngDevMode ? [{ debugName: "wizardVisualIndex" }] : /* istanbul ignore next */ []);
119
+ wizardNavigationBlocked = signal(false, ...ngDevMode ? [{ debugName: "wizardNavigationBlocked" }] : /* istanbul ignore next */ []);
120
+ wizardLinear = signal(true, ...ngDevMode ? [{ debugName: "wizardLinear" }] : /* istanbul ignore next */ []);
121
+ wizardNavigationControls = [];
122
+ wizardStepper;
123
+ wizardState = new CreateEntityWizardState();
124
+ get wizardStep() {
125
+ return this.options?.wizard?.steps[this.wizardIndex()];
126
+ }
127
+ get wizardLast() {
128
+ return this.wizardIndex() === this.wizardVisibleIndices().at(-1);
129
+ }
130
+ get wizardPrimaryLabel() {
131
+ const wizard = this.options.wizard;
132
+ if (!this.options.record?._id) return wizard?.createNextLabel ?? this.session.intl.messages.wizardCreateNext;
133
+ return this.wizardLast ? wizard?.finishLabel ?? this.session.intl.messages.wizardFinish : wizard?.nextLabel ?? this.session.intl.messages.wizardNext;
134
+ }
135
+ get wizardExitLabel() {
136
+ return this.options.record?._id ? this.options.wizard?.finishLaterLabel ?? this.session.intl.messages.wizardFinishLater : this.session.cancelLabel;
137
+ }
138
+ selectWizardVisualStep(position) {
139
+ const index = this.wizardVisibleIndices()[position];
140
+ if (index !== void 0) this.selectWizardStep(index);
141
+ }
142
+ advanceWizard() {
143
+ const next = this.wizardVisibleIndices()[this.wizardVisualIndex() + 1];
144
+ if (next !== void 0) {
145
+ this.wizardIndex.set(next);
146
+ this.refreshWizardNavigation();
147
+ }
148
+ }
149
+ completeWizardStep() {
150
+ const completed = [...this.wizardCompleted()];
151
+ completed[this.wizardIndex()] = true;
152
+ this.wizardCompleted.set(completed);
153
+ this.refreshWizardNavigation();
154
+ }
155
+ refreshWizardNavigation() {
156
+ if (!this.options?.wizard) return;
157
+ const blocked = this.session.updating || this.wizardPending;
158
+ this.wizardNavigationBlocked.set(blocked);
159
+ const linear = !this.options.record?._id || blocked;
160
+ this.wizardLinear.set(linear);
161
+ if (this.wizardStepper) this.wizardStepper.linear = linear;
162
+ const count = this.options.wizard.steps.length;
163
+ if (this.wizardNavigationControls.length !== count) this.wizardNavigationControls = Array.from({ length: count }, () => new FormControl(false, {
164
+ nonNullable: true,
165
+ validators: Validators.requiredTrue
166
+ }));
167
+ for (const [index, control] of this.wizardNavigationControls.entries()) control.setValue(!blocked && (index !== 0 || !!this.options.record?._id), { emitEvent: false });
168
+ this.wizardStepper?.steps.forEach((step, position) => {
169
+ const index = this.wizardVisibleIndices()[position];
170
+ step.optional = false;
171
+ step.interacted = true;
172
+ step.completed = !blocked && !!this.wizardCompleted()[index];
173
+ step.editable = !blocked && (index === 0 || !!this.options.record?._id);
174
+ });
175
+ }
176
+ get wizardValid() {
177
+ return this.wizardState.valid(this.session.formGroup, [...this.wizardPaths(), ...this.wizardAlwaysPaths()]);
178
+ }
179
+ get wizardPending() {
180
+ return [...this.wizardPaths(), ...this.wizardAlwaysPaths()].some((path) => this.session.formGroup.getControl(path)?.pending);
181
+ }
182
+ get wizardUnsaved() {
183
+ return Object.keys(this.wizardState.project(this.session.formGroup, this.wizardPaths(), true)).length > 0;
184
+ }
185
+ selectWizardStep(index) {
186
+ if (this.session.updating || this.wizardPending || !this.options.wizard || index < 0 || index >= this.options.wizard.steps.length || !this.wizardVisibleIndices().includes(index)) return;
187
+ if (index > 0 && !this.options.record?._id) return;
188
+ this.wizardIndex.set(index);
189
+ this.refreshWizardNavigation();
190
+ }
191
+ skipWizardStep() {
192
+ if (this.session.updating || this.wizardPending || !this.wizardStep?.optional || !this.options.record?._id) return;
193
+ if (this.wizardLast) this.requestClose();
194
+ else this.advanceWizard();
195
+ }
196
+ async wizardNext() {
197
+ if (this.session.updating || !this.options.wizard) return;
198
+ const isCurrent = this.session.captureCurrentOperation();
199
+ if (!this.wizardStep?.componentManaged) {
200
+ for (const path of this.wizardPaths()) this.session.formGroup.getControl(path)?.markAllAsTouched();
201
+ if (!this.wizardValid) return;
202
+ const previous = this.session.savedResult;
203
+ if (!this.options.record?._id) await this.session.create();
204
+ else if (this.wizardUnsaved) await this.session.update();
205
+ else {
206
+ this.completeWizardStep();
207
+ if (this.wizardLast) await this.requestClose();
208
+ else this.advanceWizard();
209
+ return;
210
+ }
211
+ if (!isCurrent() || previous === this.session.savedResult) return;
212
+ }
213
+ if (this.wizardLast) await this.requestClose();
214
+ else this.advanceWizard();
215
+ }
216
+ wizardPaths(index = this.wizardIndex()) {
217
+ const display = this.options?.displayOptions;
218
+ const tab = display && "tabs" in display ? display.tabs[index] : void 0;
219
+ const names = tab ? this.session.renderService.collectFields(tab.displayOptions).flatMap((field) => field.kind === "separator" || field.kind === "metadata" || field.kind === "display" ? [] : [field.name]) : [];
220
+ return [.../* @__PURE__ */ new Set([...names, ...this.options.wizard?.steps[index]?.fields ?? []])];
221
+ }
222
+ wizardAlwaysPaths() {
223
+ return this.session.fields.flatMap((field) => field.kind !== "separator" && field.kind !== "metadata" && field.kind !== "display" && field.alwaysInUpsert ? [field.name] : []);
224
+ }
225
+ wizardValue(create) {
226
+ const paths = this.wizardPaths();
227
+ const always = this.wizardAlwaysPaths();
228
+ const value = this.wizardState.project(this.session.formGroup, paths, !create, always);
229
+ if (create) {
230
+ for (const path of [...paths, ...always]) if (!this.session.formGroup.getControl(path) && has(this.options.defaultValues, path)) set(value, path, cloneDeep(get(this.options.defaultValues, path)));
231
+ }
232
+ return value;
233
+ }
234
+ patchWizardRecord(record, value) {
235
+ const updated = cloneDeep(record);
236
+ for (const path of [...this.wizardPaths(), ...this.wizardAlwaysPaths()]) if (has(value, path)) set(updated, path, cloneDeep(get(value, path)));
237
+ return updated;
238
+ }
239
+ async hydrateWizard(record, value) {
240
+ const paths = [...this.wizardPaths(), ...this.wizardAlwaysPaths()].filter((path) => has(value, path));
241
+ this.wizardState.hydrate(this.session.formGroup, record, paths);
242
+ this.completeWizardStep();
243
+ this.session.updated = this.session.formGroup.isUpdated;
244
+ this.session.refreshAllowedActions();
245
+ this.session.refreshRuntimeState();
246
+ }
247
+ refreshWizardState() {
248
+ const visible = (this.session.renderState().tabs ?? []).flatMap((tab, index) => tab.isVisible ? [index] : []);
249
+ this.wizardVisibleIndices.set(visible);
250
+ if (!visible.includes(this.wizardIndex()) && visible.length) this.wizardIndex.set(visible[0]);
251
+ this.wizardDrafts.set(this.options.wizard.steps.map((_, index) => Object.keys(this.wizardState.project(this.session.formGroup, this.wizardPaths(index), true)).length > 0));
252
+ this.refreshWizardNavigation();
253
+ }
254
+ static ɵfac = i0.ɵɵngDeclareFactory({
255
+ minVersion: "12.0.0",
256
+ version: "22.2.1",
257
+ ngImport: i0,
258
+ type: EntityWizardComponent,
259
+ deps: [],
260
+ target: i0.ɵɵFactoryTarget.Component
261
+ });
262
+ static ɵcmp = i0.ɵɵngDeclareComponent({
263
+ minVersion: "17.0.0",
264
+ version: "22.2.1",
265
+ type: EntityWizardComponent,
266
+ isStandalone: true,
267
+ selector: "ame-entity-wizard",
268
+ inputs: {
269
+ options: "options",
270
+ displayMode: "displayMode"
271
+ },
272
+ outputs: {
273
+ result: "result",
274
+ closeRequest: "closeRequest"
275
+ },
276
+ providers: [
277
+ EntityFormSession,
278
+ EditEntityConditionalStateService,
279
+ EditEntityFormBuilderService,
280
+ EditEntityRenderService,
281
+ EditEntitySubmitValueService
282
+ ],
283
+ viewQueries: [{
284
+ propertyName: "wizardStepper",
285
+ first: true,
286
+ predicate: MatStepper,
287
+ descendants: true
288
+ }],
289
+ usesOnChanges: true,
290
+ ngImport: i0,
291
+ template: "<!-- @format -->\n\n@if (session.loading) {\n <div class=\"edit-entity-loading\">\n <mat-spinner [diameter]=\"72\" />\n <span>{{ session.loadingLabel }}</span>\n </div>\n} @else if (session.errorLabel) {\n <div class=\"edit-entity-empty-state\">\n <mat-icon>info</mat-icon>\n <span>{{ session.errorLabel }}</span>\n\n @if (displayMode === \"dialog\") {\n <button mat-stroked-button type=\"button\" (click)=\"requestClose()\">\n {{ session.closeLabel }}\n </button>\n }\n </div>\n} @else {\n @if (\n session.title ||\n session.subtitle ||\n displayMode === \"dialog\" ||\n session.getActions(\"top\").length\n ) {\n <header\n class=\"edit-entity-title-bar\"\n [class.edit-entity-wizard-header]=\"options.wizard\"\n >\n @if (options.wizard?.icon) {\n <mat-icon>{{ options.wizard?.icon }}</mat-icon>\n }\n <div class=\"edit-entity-title-content\">\n @if (session.title) {\n <h2 [innerHTML]=\"session.title\"></h2>\n }\n @if (session.subtitle) {\n <p [innerHTML]=\"session.subtitle\"></p>\n }\n </div>\n\n <div class=\"edit-entity-title-actions\">\n @for (\n action of session.getActions(\"top\");\n track session.trackRuntimeAction($index, action)\n ) {\n <ng-container\n *ngTemplateOutlet=\"actionTemplate; context: { action }\"\n ></ng-container>\n }\n\n @if (options.showJsonDataButton === true) {\n <button\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"session.intl.messages.showJson\"\n [attr.aria-label]=\"session.intl.messages.showJson\"\n (click)=\"session.openJsonDataDialog()\"\n >\n <mat-icon>file_json</mat-icon>\n </button>\n }\n\n @if (displayMode === \"dialog\") {\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"session.closeLabel\"\n (click)=\"requestClose()\"\n >\n <mat-icon>close</mat-icon>\n </button>\n }\n </div>\n </header>\n }\n\n <form class=\"edit-entity-form\" [formGroup]=\"session.formGroup\">\n <main class=\"edit-entity-content\">\n @if (options.wizard; as wizard) {\n <mat-stepper\n class=\"edit-entity-wizard\"\n orientation=\"horizontal\"\n labelPosition=\"end\"\n [linear]=\"wizardLinear()\"\n [selectedIndex]=\"wizardVisualIndex()\"\n (selectionChange)=\"selectWizardVisualStep($event.selectedIndex)\"\n >\n <!-- Material icon context.active also includes completed steps; selection follows the wizard index. -->\n <ng-template matStepperIcon=\"number\" let-index=\"index\">\n <ng-container\n *ngTemplateOutlet=\"\n wizardStateIcon;\n context: { index, active: index === wizardVisualIndex() }\n \"\n ></ng-container>\n </ng-template>\n <ng-template matStepperIcon=\"edit\" let-index=\"index\">\n <ng-container\n *ngTemplateOutlet=\"\n wizardStateIcon;\n context: { index, active: index === wizardVisualIndex() }\n \"\n ></ng-container>\n </ng-template>\n <ng-template matStepperIcon=\"done\" let-index=\"index\">\n <ng-container\n *ngTemplateOutlet=\"\n wizardStateIcon;\n context: { index, active: index === wizardVisualIndex() }\n \"\n ></ng-container>\n </ng-template>\n <ng-template #wizardStateIcon let-index=\"index\" let-active=\"active\">\n <span\n class=\"edit-entity-wizard-state-icon\"\n [class.edit-entity-wizard-state-icon--selected]=\"active\"\n [class.edit-entity-wizard-state-icon--completed]=\"\n !active && wizardCompleted()[wizardVisibleIndices()[index]]\n \"\n >\n @if (\n !active && wizardCompleted()[wizardVisibleIndices()[index]]\n ) {\n <mat-icon>check</mat-icon>\n } @else {\n {{ index + 1 }}\n }\n </span>\n </ng-template>\n @for (index of wizardVisibleIndices(); track index) {\n @let step = wizard.steps[index];\n <mat-step\n [optional]=\"false\"\n [stepControl]=\"wizardNavigationControls[index]\"\n [completed]=\"\n !wizardNavigationBlocked() && wizardCompleted()[index]\n \"\n [editable]=\"\n !wizardNavigationBlocked() &&\n (index === 0 || !!options.record?._id)\n \"\n >\n <ng-template matStepLabel>\n <div\n class=\"edit-entity-wizard-step-label\"\n [class.edit-entity-wizard-step-label--selected]=\"\n index === wizardIndex()\n \"\n >\n <span class=\"edit-entity-wizard-step-name\">{{\n step.label\n }}</span>\n @if (wizardDrafts()[index]) {\n <span\n class=\"edit-entity-wizard-unsaved\"\n [title]=\"\n wizard.unsavedLabel ||\n session.intl.messages.wizardUnsaved\n \"\n >{{\n wizard.unsavedLabel ||\n session.intl.messages.wizardUnsaved\n }}</span\n >\n }\n </div>\n </ng-template>\n <ng-template matStepContent>\n @if (index === wizardIndex()) {\n <header class=\"edit-entity-wizard-content-heading\">\n <div>\n <h3>{{ step.label }}</h3>\n @if (step.description) {\n <p>{{ step.description }}</p>\n }\n </div>\n @if (step.optional && index > 0) {\n <span class=\"edit-entity-wizard-optional\">{{\n wizard.optionalLabel ||\n session.intl.messages.wizardOptional\n }}</span>\n }\n </header>\n <ame-form-content\n [state]=\"\n session.renderState().tabs?.[index]?.renderState || {\n kind: 'empty',\n }\n \"\n [formGroup]=\"session.formGroup\"\n [options]=\"options\"\n [context]=\"session\"\n />\n }\n </ng-template>\n </mat-step>\n }\n </mat-stepper>\n }\n </main>\n </form>\n\n <footer\n class=\"edit-entity-actions\"\n [class.edit-entity-wizard-actions]=\"options.wizard\"\n >\n <div class=\"edit-entity-actions-left\">\n @for (\n action of session.getActions(\"bottom\");\n track session.trackRuntimeAction($index, action)\n ) {\n <ng-container\n *ngTemplateOutlet=\"actionTemplate; context: { action }\"\n ></ng-container>\n }\n </div>\n\n <div class=\"edit-entity-actions-right\">\n @for (\n action of session.getActions(\"bottom-end\");\n track session.trackRuntimeAction($index, action)\n ) {\n <ng-container\n *ngTemplateOutlet=\"actionTemplate; context: { action }\"\n ></ng-container>\n }\n\n @if (options.wizard; as wizard) {\n <button\n mat-stroked-button\n type=\"button\"\n [disabled]=\"session.updating\"\n (click)=\"requestClose()\"\n >\n {{ wizardExitLabel }}\n </button>\n @if (wizardStep?.optional && options.record?._id) {\n <button\n mat-raised-button\n class=\"edit-entity-wizard-skip\"\n type=\"button\"\n [disabled]=\"wizardNavigationBlocked()\"\n (click)=\"skipWizardStep()\"\n >\n {{ wizard.skipLabel || session.intl.messages.wizardSkip }}\n </button>\n }\n <button\n mat-raised-button\n color=\"secondary\"\n type=\"button\"\n [disabled]=\"\n session.updating || (!wizardStep?.componentManaged && !wizardValid)\n \"\n (click)=\"wizardNext()\"\n >\n {{ wizardPrimaryLabel }}\n </button>\n }\n </div>\n </footer>\n\n @if (session.updating) {\n <div class=\"edit-entity-updating\">\n <mat-spinner [diameter]=\"72\" />\n </div>\n }\n}\n\n<ng-template #actionTemplate let-action=\"action\">\n @if (session.isRuntimeButtonAction(action)) {\n @if (action.buttonType === \"raised\") {\n <button\n class=\"action-btn\"\n mat-raised-button\n type=\"button\"\n [style.--action-color]=\"action.contrastColor\"\n [style.--action-background-color]=\"action.color\"\n [disabled]=\"session.updating || action.isDisabled\"\n [matTooltip]=\"action.tooltip || ''\"\n (click)=\"session.handleAction(action)\"\n >\n @if (action.icon) {\n <mat-icon>{{ action.icon }}</mat-icon>\n }\n <span [innerHTML]=\"action.label\"></span>\n </button>\n } @else {\n <button\n class=\"action-btn\"\n mat-button\n type=\"button\"\n [style.--action-color]=\"action.color\"\n [disabled]=\"session.updating || action.isDisabled\"\n [matTooltip]=\"action.tooltip || ''\"\n (click)=\"session.handleAction(action)\"\n >\n @if (action.icon) {\n <mat-icon>{{ action.icon }}</mat-icon>\n }\n <span [innerHTML]=\"action.label\"></span>\n </button>\n }\n } @else if (session.isRuntimeComponentAction(action)) {\n <ng-container\n *ngComponentOutlet=\"action.component; inputs: action.componentInputs\"\n ></ng-container>\n }\n</ng-template>\n",
292
+ styles: [":host{--edit-entity-background: var( --ngx-mat-edit-entity-dialog-background, var(--ngx-mat-extended-surface, #f4f6f8) );--edit-entity-border: var( --ngx-mat-edit-entity-dialog-border, var(--ngx-mat-extended-border, rgba(14, 43, 67, .12)) );--edit-entity-text: var( --ngx-mat-edit-entity-dialog-text, var(--ngx-mat-extended-text, #102f49) );--edit-entity-muted: var( --ngx-mat-edit-entity-dialog-muted, var(--ngx-mat-extended-muted, #61718a) );--edit-entity-primary: var(--ngx-mat-edit-entity-dialog-primary, #0f334d);position:relative;display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden;color:var(--edit-entity-text)}.edit-entity-loading,.edit-entity-empty-state{display:flex;flex:1 1 auto;min-height:240px;align-items:center;justify-content:center;flex-direction:column;gap:var(--ngx-mat-extended-space-3, 16px);color:var(--edit-entity-primary)}.edit-entity-empty-state{padding:var(--ngx-mat-extended-space-4, 24px);color:var(--edit-entity-muted);text-align:center}.edit-entity-empty-state mat-icon{width:42px;height:42px;color:var(--edit-entity-primary);font-size:42px}.edit-entity-empty-state span,.edit-entity-empty-state strong{color:var(--edit-entity-text);font-size:15px;font-weight:700}.edit-entity-title-bar{display:flex;flex:0 0 auto;align-items:center;justify-content:start;gap:var(--ngx-mat-extended-space-3, 16px);min-height:48px;padding:var(--ngx-mat-extended-space-2, 8px);margin:0;border-bottom:1px solid var(--edit-entity-border);box-sizing:border-box;background:var(--ngx-mat-edit-entity-dialog-header-background, var(--ngx-mat-extended-surface, #0f334d));color:var(--ngx-mat-edit-entity-dialog-header-text, var(--ngx-mat-extended-text, #fff))}.edit-entity-title-bar h2{margin:0;font-size:var(--mat-sys-title-medium-size, 18px);font-weight:700;line-height:1.2}.edit-entity-title-bar p{margin:var(--ngx-mat-extended-space-1, 4px) 0 0;color:var(--ngx-mat-edit-entity-dialog-header-text, var(--ngx-mat-extended-text, #fff));font-size:var(--mat-sys-body-small-size, 13px)}.edit-entity-title-content{min-width:0;overflow:hidden}.edit-entity-title-content h2,.edit-entity-title-content p{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.edit-entity-title-actions{display:inline-flex;flex:0 0 auto;align-items:center;gap:var(--ngx-mat-extended-space-2, 8px);margin-left:auto}.edit-entity-title-actions .mat-mdc-icon-button{--mat-icon-button-icon-color: var(--ngx-mat-edit-entity-dialog-header-text, var(--ngx-mat-extended-text, #fff))}.edit-entity-form,.edit-entity-content{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;max-height:none;margin:0;overflow:hidden;padding:0;background:var(--edit-entity-background);box-sizing:border-box}.edit-entity-actions{display:flex;flex:0 0 auto;align-items:center;justify-content:space-between;gap:var(--ngx-mat-extended-space-2, 8px);min-height:54px;padding:var(--ngx-mat-extended-space-2, 8px);border-top:1px solid var(--edit-entity-border);background:var(--ngx-mat-edit-entity-dialog-background, var(--ngx-mat-extended-surface, #ffffff));box-sizing:border-box}.edit-entity-actions-left,.edit-entity-actions-right{display:inline-flex;align-items:center;gap:var(--ngx-mat-extended-space-2, 8px);min-width:0}.edit-entity-updating{position:absolute;inset:0;z-index:20;display:flex;align-items:center;justify-content:center;background:#fff9}.action-btn{--mat-button-text-label-text-color: var(--action-color);--mat-button-filled-label-text-color: var(--action-color);--mat-button-filled-container-color: var(--action-background-color);--mat-button-protected-label-text-color: var(--action-color);--mat-button-protected-container-color: var(--action-background-color)}:host ::ng-deep mat-form-field{--mat-form-field-outlined-disabled-input-text-color: var(--mat-form-field-outlined-input-text-color)}:host ::ng-deep mat-form-field .mat-mdc-chip.mat-mdc-chip-row{--mat-chip-disabled-container-opacity: 1;--mat-chip-elevated-disabled-container-color: var(--mat-chip-elevated-container-color)}@media(max-width:900px){.edit-entity-actions{align-items:stretch;flex-direction:column}.edit-entity-actions-left,.edit-entity-actions-right{justify-content:flex-end;width:100%}}.edit-entity-wizard-header{background:var(--edit-entity-primary);color:var(--ngx-mat-edit-entity-dialog-header-text, var(--mat-sys-on-primary))}.edit-entity-wizard{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;min-width:0;background:transparent;--mat-stepper-header-selected-state-icon-background-color: var(--ngx-mat-edit-entity-wizard-alert, var(--mat-sys-primary));--mat-stepper-header-selected-state-icon-foreground-color: var(--ngx-mat-edit-entity-wizard-on-alert, var(--mat-sys-on-primary));--mat-stepper-header-selected-state-label-text-color: var(--ngx-mat-edit-entity-wizard-toolbar-text, var(--edit-entity-text));--mat-stepper-header-done-state-icon-background-color: var(--ngx-mat-edit-entity-wizard-ok, var(--mat-sys-tertiary));--mat-stepper-header-done-state-icon-foreground-color: var(--ngx-mat-edit-entity-wizard-on-ok, var(--mat-sys-on-tertiary));--mat-stepper-header-edit-state-icon-background-color: var(--ngx-mat-edit-entity-wizard-ok, var(--mat-sys-tertiary));--mat-stepper-header-edit-state-icon-foreground-color: var(--ngx-mat-edit-entity-wizard-on-ok, var(--mat-sys-on-tertiary));--mat-stepper-header-label-text-color: var(--ngx-mat-edit-entity-wizard-toolbar-text, var(--edit-entity-text));--mat-stepper-line-color: transparent;--mat-stepper-container-color: transparent;--mat-stepper-header-height: 56px;--mat-stepper-header-focus-state-layer-shape: 0;--mat-stepper-header-hover-state-layer-shape: 0}.edit-entity-wizard-optional{display:block;align-self:center;padding:2px 5px;border-radius:4px;font-size:11px}.edit-entity-wizard-optional{background:var(--ngx-mat-edit-entity-dialog-secondary, var(--mat-sys-secondary));color:var(--mat-sys-on-secondary)}.edit-entity-wizard-unsaved{position:absolute;inset-inline:8px;bottom:1px;display:block;overflow:hidden;color:var(--edit-entity-muted);font-size:10px;line-height:10px;text-overflow:ellipsis;white-space:nowrap}.edit-entity-wizard-skip{--mat-button-protected-container-color: var(--ngx-mat-edit-entity-dialog-secondary, var(--mat-sys-secondary));--mat-button-protected-label-text-color: var(--mat-sys-on-secondary)}.edit-entity-wizard-step-label{display:flex;flex-direction:column;align-items:flex-start;gap:4px;width:112px;max-width:100%;white-space:normal;line-height:16px;text-align:start;color:var(--ngx-mat-edit-entity-wizard-toolbar-text, var(--edit-entity-text))}.edit-entity-wizard-step-label--selected{color:var(--ngx-mat-edit-entity-wizard-alert, var(--mat-sys-primary))}.edit-entity-wizard-step-name{overflow-wrap:anywhere}.edit-entity-wizard-actions{flex-wrap:wrap}.edit-entity-wizard-actions .edit-entity-actions-left:empty{display:none}.edit-entity-wizard-actions .edit-entity-actions-right{flex:1 1 auto;flex-wrap:wrap;justify-content:flex-end}.edit-entity-wizard-actions .edit-entity-actions-right button{max-width:100%;white-space:normal}:host ::ng-deep .edit-entity-wizard .mat-horizontal-stepper-wrapper,:host ::ng-deep .edit-entity-wizard .mat-horizontal-content-container{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;min-width:0}:host ::ng-deep .edit-entity-wizard .mat-horizontal-stepper-header-container{flex:0 0 auto;min-width:0;overflow-x:auto;overscroll-behavior-x:contain;border-bottom:1px solid var(--edit-entity-border);background:var(--ngx-mat-edit-entity-wizard-toolbar-background, var(--mat-sys-surface))}:host ::ng-deep .edit-entity-wizard .mat-horizontal-stepper-header{flex:1 0 160px;min-width:160px;padding-inline:8px;box-sizing:border-box}:host ::ng-deep .edit-entity-wizard .mat-stepper-horizontal-line{display:none}:host ::ng-deep .edit-entity-wizard .mat-horizontal-content-container{padding:0}:host ::ng-deep .edit-entity-wizard .mat-horizontal-stepper-content.mat-horizontal-stepper-content-current{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;overflow:hidden}.edit-entity-wizard-content-heading{display:flex;flex:0 0 auto;align-items:flex-start;justify-content:space-between;gap:8px;padding:8px}.edit-entity-wizard-content-heading>div{min-width:0}.edit-entity-wizard-content-heading h3{margin:0;color:var(--edit-entity-text);font-size:16px;line-height:24px}.edit-entity-wizard-content-heading p{margin:4px 0 0;color:var(--edit-entity-muted);font-size:13px;line-height:18px}.edit-entity-wizard-content-heading .edit-entity-wizard-optional{flex:0 0 auto;align-self:flex-start;margin-block-start:2px}.edit-entity-wizard-state-icon{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;box-sizing:border-box;border:1px solid var(--edit-entity-border);background:var(--ngx-mat-edit-entity-wizard-toolbar-background, var(--mat-sys-surface));color:var(--edit-entity-muted)}.edit-entity-wizard-state-icon mat-icon{font-size:18px;width:18px;height:18px}.edit-entity-wizard-state-icon--completed{border-color:transparent;background:var(--ngx-mat-edit-entity-wizard-ok, var(--mat-sys-tertiary));color:var(--ngx-mat-edit-entity-wizard-on-ok, var(--mat-sys-on-tertiary))}.edit-entity-wizard-state-icon--selected{border-color:transparent;background:var(--ngx-mat-edit-entity-wizard-alert, var(--mat-sys-primary));color:var(--ngx-mat-edit-entity-wizard-on-alert, var(--mat-sys-on-primary))}\n"],
293
+ dependencies: [
294
+ {
295
+ kind: "ngmodule",
296
+ type: CommonModule
297
+ },
298
+ {
299
+ kind: "directive",
300
+ type: i1$1.NgComponentOutlet,
301
+ selector: "[ngComponentOutlet]",
302
+ inputs: [
303
+ "ngComponentOutlet",
304
+ "ngComponentOutletInputs",
305
+ "ngComponentOutletInjector",
306
+ "ngComponentOutletEnvironmentInjector",
307
+ "ngComponentOutletContent",
308
+ "ngComponentOutletNgModule"
309
+ ],
310
+ exportAs: ["ngComponentOutlet"]
311
+ },
312
+ {
313
+ kind: "directive",
314
+ type: i1$1.NgTemplateOutlet,
315
+ selector: "[ngTemplateOutlet]",
316
+ inputs: [
317
+ "ngTemplateOutletContext",
318
+ "ngTemplateOutlet",
319
+ "ngTemplateOutletInjector"
320
+ ]
321
+ },
322
+ {
323
+ kind: "ngmodule",
324
+ type: ReactiveFormsModule
325
+ },
326
+ {
327
+ kind: "directive",
328
+ type: i2.ɵNgNoValidate,
329
+ selector: "form:not([ngNoForm]):not([ngNativeValidate])"
330
+ },
331
+ {
332
+ kind: "directive",
333
+ type: i2.NgControlStatusGroup,
334
+ selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]"
335
+ },
336
+ {
337
+ kind: "directive",
338
+ type: i2.FormGroupDirective,
339
+ selector: "[formGroup]",
340
+ inputs: ["formGroup"],
341
+ outputs: ["ngSubmit"],
342
+ exportAs: ["ngForm"]
343
+ },
344
+ {
345
+ kind: "ngmodule",
346
+ type: MatButtonModule
347
+ },
348
+ {
349
+ kind: "component",
350
+ type: i3.MatButton,
351
+ selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ",
352
+ inputs: ["matButton"],
353
+ exportAs: ["matButton", "matAnchor"]
354
+ },
355
+ {
356
+ kind: "component",
357
+ type: i3.MatIconButton,
358
+ selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]",
359
+ exportAs: ["matButton", "matAnchor"]
360
+ },
361
+ {
362
+ kind: "ngmodule",
363
+ type: MatIconModule
364
+ },
365
+ {
366
+ kind: "component",
367
+ type: i4.MatIcon,
368
+ selector: "mat-icon",
369
+ inputs: [
370
+ "color",
371
+ "inline",
372
+ "svgIcon",
373
+ "fontSet",
374
+ "fontIcon"
375
+ ],
376
+ exportAs: ["matIcon"]
377
+ },
378
+ {
379
+ kind: "ngmodule",
380
+ type: MatProgressSpinnerModule
381
+ },
382
+ {
383
+ kind: "component",
384
+ type: i5.MatProgressSpinner,
385
+ selector: "mat-progress-spinner, mat-spinner",
386
+ inputs: [
387
+ "color",
388
+ "mode",
389
+ "value",
390
+ "diameter",
391
+ "strokeWidth"
392
+ ],
393
+ exportAs: ["matProgressSpinner"]
394
+ },
395
+ {
396
+ kind: "ngmodule",
397
+ type: MatTooltipModule
398
+ },
399
+ {
400
+ kind: "directive",
401
+ type: i6.MatTooltip,
402
+ selector: "[matTooltip]",
403
+ inputs: [
404
+ "matTooltipPosition",
405
+ "matTooltipPositionAtOrigin",
406
+ "matTooltipDisabled",
407
+ "matTooltipShowDelay",
408
+ "matTooltipHideDelay",
409
+ "matTooltipTouchGestures",
410
+ "matTooltip",
411
+ "matTooltipClass"
412
+ ],
413
+ exportAs: ["matTooltip"]
414
+ },
415
+ {
416
+ kind: "component",
417
+ type: FormContentComponent,
418
+ selector: "ame-form-content",
419
+ inputs: [
420
+ "state",
421
+ "formGroup",
422
+ "options",
423
+ "context"
424
+ ]
425
+ },
426
+ {
427
+ kind: "ngmodule",
428
+ type: MatStepperModule
429
+ },
430
+ {
431
+ kind: "component",
432
+ type: i7.MatStep,
433
+ selector: "mat-step",
434
+ inputs: ["color"],
435
+ exportAs: ["matStep"]
436
+ },
437
+ {
438
+ kind: "directive",
439
+ type: i7.MatStepLabel,
440
+ selector: "[matStepLabel]"
441
+ },
442
+ {
443
+ kind: "component",
444
+ type: i7.MatStepper,
445
+ selector: "mat-stepper, mat-vertical-stepper, mat-horizontal-stepper, [matStepper]",
446
+ inputs: [
447
+ "disableRipple",
448
+ "color",
449
+ "labelPosition",
450
+ "headerPosition",
451
+ "aria-label",
452
+ "headerPrefix",
453
+ "animationDuration"
454
+ ],
455
+ outputs: ["animationDone"],
456
+ exportAs: [
457
+ "matStepper",
458
+ "matVerticalStepper",
459
+ "matHorizontalStepper"
460
+ ]
461
+ },
462
+ {
463
+ kind: "directive",
464
+ type: i7.MatStepperIcon,
465
+ selector: "ng-template[matStepperIcon]",
466
+ inputs: ["matStepperIcon"]
467
+ },
468
+ {
469
+ kind: "directive",
470
+ type: i7.MatStepContent,
471
+ selector: "ng-template[matStepContent]"
472
+ }
473
+ ],
474
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
475
+ });
476
+ };
477
+ i0.ɵɵngDeclareClassMetadata({
478
+ minVersion: "12.0.0",
479
+ version: "22.2.1",
480
+ ngImport: i0,
481
+ type: EntityWizardComponent,
482
+ decorators: [{
483
+ type: Component,
484
+ args: [{
485
+ selector: "ame-entity-wizard",
486
+ imports: [
487
+ CommonModule,
488
+ ReactiveFormsModule,
489
+ MatButtonModule,
490
+ MatIconModule,
491
+ MatProgressSpinnerModule,
492
+ MatTooltipModule,
493
+ FormContentComponent,
494
+ MatStepperModule
495
+ ],
496
+ providers: [
497
+ EntityFormSession,
498
+ EditEntityConditionalStateService,
499
+ EditEntityFormBuilderService,
500
+ EditEntityRenderService,
501
+ EditEntitySubmitValueService
502
+ ],
503
+ changeDetection: ChangeDetectionStrategy.OnPush,
504
+ template: "<!-- @format -->\n\n@if (session.loading) {\n <div class=\"edit-entity-loading\">\n <mat-spinner [diameter]=\"72\" />\n <span>{{ session.loadingLabel }}</span>\n </div>\n} @else if (session.errorLabel) {\n <div class=\"edit-entity-empty-state\">\n <mat-icon>info</mat-icon>\n <span>{{ session.errorLabel }}</span>\n\n @if (displayMode === \"dialog\") {\n <button mat-stroked-button type=\"button\" (click)=\"requestClose()\">\n {{ session.closeLabel }}\n </button>\n }\n </div>\n} @else {\n @if (\n session.title ||\n session.subtitle ||\n displayMode === \"dialog\" ||\n session.getActions(\"top\").length\n ) {\n <header\n class=\"edit-entity-title-bar\"\n [class.edit-entity-wizard-header]=\"options.wizard\"\n >\n @if (options.wizard?.icon) {\n <mat-icon>{{ options.wizard?.icon }}</mat-icon>\n }\n <div class=\"edit-entity-title-content\">\n @if (session.title) {\n <h2 [innerHTML]=\"session.title\"></h2>\n }\n @if (session.subtitle) {\n <p [innerHTML]=\"session.subtitle\"></p>\n }\n </div>\n\n <div class=\"edit-entity-title-actions\">\n @for (\n action of session.getActions(\"top\");\n track session.trackRuntimeAction($index, action)\n ) {\n <ng-container\n *ngTemplateOutlet=\"actionTemplate; context: { action }\"\n ></ng-container>\n }\n\n @if (options.showJsonDataButton === true) {\n <button\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"session.intl.messages.showJson\"\n [attr.aria-label]=\"session.intl.messages.showJson\"\n (click)=\"session.openJsonDataDialog()\"\n >\n <mat-icon>file_json</mat-icon>\n </button>\n }\n\n @if (displayMode === \"dialog\") {\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"session.closeLabel\"\n (click)=\"requestClose()\"\n >\n <mat-icon>close</mat-icon>\n </button>\n }\n </div>\n </header>\n }\n\n <form class=\"edit-entity-form\" [formGroup]=\"session.formGroup\">\n <main class=\"edit-entity-content\">\n @if (options.wizard; as wizard) {\n <mat-stepper\n class=\"edit-entity-wizard\"\n orientation=\"horizontal\"\n labelPosition=\"end\"\n [linear]=\"wizardLinear()\"\n [selectedIndex]=\"wizardVisualIndex()\"\n (selectionChange)=\"selectWizardVisualStep($event.selectedIndex)\"\n >\n <!-- Material icon context.active also includes completed steps; selection follows the wizard index. -->\n <ng-template matStepperIcon=\"number\" let-index=\"index\">\n <ng-container\n *ngTemplateOutlet=\"\n wizardStateIcon;\n context: { index, active: index === wizardVisualIndex() }\n \"\n ></ng-container>\n </ng-template>\n <ng-template matStepperIcon=\"edit\" let-index=\"index\">\n <ng-container\n *ngTemplateOutlet=\"\n wizardStateIcon;\n context: { index, active: index === wizardVisualIndex() }\n \"\n ></ng-container>\n </ng-template>\n <ng-template matStepperIcon=\"done\" let-index=\"index\">\n <ng-container\n *ngTemplateOutlet=\"\n wizardStateIcon;\n context: { index, active: index === wizardVisualIndex() }\n \"\n ></ng-container>\n </ng-template>\n <ng-template #wizardStateIcon let-index=\"index\" let-active=\"active\">\n <span\n class=\"edit-entity-wizard-state-icon\"\n [class.edit-entity-wizard-state-icon--selected]=\"active\"\n [class.edit-entity-wizard-state-icon--completed]=\"\n !active && wizardCompleted()[wizardVisibleIndices()[index]]\n \"\n >\n @if (\n !active && wizardCompleted()[wizardVisibleIndices()[index]]\n ) {\n <mat-icon>check</mat-icon>\n } @else {\n {{ index + 1 }}\n }\n </span>\n </ng-template>\n @for (index of wizardVisibleIndices(); track index) {\n @let step = wizard.steps[index];\n <mat-step\n [optional]=\"false\"\n [stepControl]=\"wizardNavigationControls[index]\"\n [completed]=\"\n !wizardNavigationBlocked() && wizardCompleted()[index]\n \"\n [editable]=\"\n !wizardNavigationBlocked() &&\n (index === 0 || !!options.record?._id)\n \"\n >\n <ng-template matStepLabel>\n <div\n class=\"edit-entity-wizard-step-label\"\n [class.edit-entity-wizard-step-label--selected]=\"\n index === wizardIndex()\n \"\n >\n <span class=\"edit-entity-wizard-step-name\">{{\n step.label\n }}</span>\n @if (wizardDrafts()[index]) {\n <span\n class=\"edit-entity-wizard-unsaved\"\n [title]=\"\n wizard.unsavedLabel ||\n session.intl.messages.wizardUnsaved\n \"\n >{{\n wizard.unsavedLabel ||\n session.intl.messages.wizardUnsaved\n }}</span\n >\n }\n </div>\n </ng-template>\n <ng-template matStepContent>\n @if (index === wizardIndex()) {\n <header class=\"edit-entity-wizard-content-heading\">\n <div>\n <h3>{{ step.label }}</h3>\n @if (step.description) {\n <p>{{ step.description }}</p>\n }\n </div>\n @if (step.optional && index > 0) {\n <span class=\"edit-entity-wizard-optional\">{{\n wizard.optionalLabel ||\n session.intl.messages.wizardOptional\n }}</span>\n }\n </header>\n <ame-form-content\n [state]=\"\n session.renderState().tabs?.[index]?.renderState || {\n kind: 'empty',\n }\n \"\n [formGroup]=\"session.formGroup\"\n [options]=\"options\"\n [context]=\"session\"\n />\n }\n </ng-template>\n </mat-step>\n }\n </mat-stepper>\n }\n </main>\n </form>\n\n <footer\n class=\"edit-entity-actions\"\n [class.edit-entity-wizard-actions]=\"options.wizard\"\n >\n <div class=\"edit-entity-actions-left\">\n @for (\n action of session.getActions(\"bottom\");\n track session.trackRuntimeAction($index, action)\n ) {\n <ng-container\n *ngTemplateOutlet=\"actionTemplate; context: { action }\"\n ></ng-container>\n }\n </div>\n\n <div class=\"edit-entity-actions-right\">\n @for (\n action of session.getActions(\"bottom-end\");\n track session.trackRuntimeAction($index, action)\n ) {\n <ng-container\n *ngTemplateOutlet=\"actionTemplate; context: { action }\"\n ></ng-container>\n }\n\n @if (options.wizard; as wizard) {\n <button\n mat-stroked-button\n type=\"button\"\n [disabled]=\"session.updating\"\n (click)=\"requestClose()\"\n >\n {{ wizardExitLabel }}\n </button>\n @if (wizardStep?.optional && options.record?._id) {\n <button\n mat-raised-button\n class=\"edit-entity-wizard-skip\"\n type=\"button\"\n [disabled]=\"wizardNavigationBlocked()\"\n (click)=\"skipWizardStep()\"\n >\n {{ wizard.skipLabel || session.intl.messages.wizardSkip }}\n </button>\n }\n <button\n mat-raised-button\n color=\"secondary\"\n type=\"button\"\n [disabled]=\"\n session.updating || (!wizardStep?.componentManaged && !wizardValid)\n \"\n (click)=\"wizardNext()\"\n >\n {{ wizardPrimaryLabel }}\n </button>\n }\n </div>\n </footer>\n\n @if (session.updating) {\n <div class=\"edit-entity-updating\">\n <mat-spinner [diameter]=\"72\" />\n </div>\n }\n}\n\n<ng-template #actionTemplate let-action=\"action\">\n @if (session.isRuntimeButtonAction(action)) {\n @if (action.buttonType === \"raised\") {\n <button\n class=\"action-btn\"\n mat-raised-button\n type=\"button\"\n [style.--action-color]=\"action.contrastColor\"\n [style.--action-background-color]=\"action.color\"\n [disabled]=\"session.updating || action.isDisabled\"\n [matTooltip]=\"action.tooltip || ''\"\n (click)=\"session.handleAction(action)\"\n >\n @if (action.icon) {\n <mat-icon>{{ action.icon }}</mat-icon>\n }\n <span [innerHTML]=\"action.label\"></span>\n </button>\n } @else {\n <button\n class=\"action-btn\"\n mat-button\n type=\"button\"\n [style.--action-color]=\"action.color\"\n [disabled]=\"session.updating || action.isDisabled\"\n [matTooltip]=\"action.tooltip || ''\"\n (click)=\"session.handleAction(action)\"\n >\n @if (action.icon) {\n <mat-icon>{{ action.icon }}</mat-icon>\n }\n <span [innerHTML]=\"action.label\"></span>\n </button>\n }\n } @else if (session.isRuntimeComponentAction(action)) {\n <ng-container\n *ngComponentOutlet=\"action.component; inputs: action.componentInputs\"\n ></ng-container>\n }\n</ng-template>\n",
505
+ styles: [":host{--edit-entity-background: var( --ngx-mat-edit-entity-dialog-background, var(--ngx-mat-extended-surface, #f4f6f8) );--edit-entity-border: var( --ngx-mat-edit-entity-dialog-border, var(--ngx-mat-extended-border, rgba(14, 43, 67, .12)) );--edit-entity-text: var( --ngx-mat-edit-entity-dialog-text, var(--ngx-mat-extended-text, #102f49) );--edit-entity-muted: var( --ngx-mat-edit-entity-dialog-muted, var(--ngx-mat-extended-muted, #61718a) );--edit-entity-primary: var(--ngx-mat-edit-entity-dialog-primary, #0f334d);position:relative;display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden;color:var(--edit-entity-text)}.edit-entity-loading,.edit-entity-empty-state{display:flex;flex:1 1 auto;min-height:240px;align-items:center;justify-content:center;flex-direction:column;gap:var(--ngx-mat-extended-space-3, 16px);color:var(--edit-entity-primary)}.edit-entity-empty-state{padding:var(--ngx-mat-extended-space-4, 24px);color:var(--edit-entity-muted);text-align:center}.edit-entity-empty-state mat-icon{width:42px;height:42px;color:var(--edit-entity-primary);font-size:42px}.edit-entity-empty-state span,.edit-entity-empty-state strong{color:var(--edit-entity-text);font-size:15px;font-weight:700}.edit-entity-title-bar{display:flex;flex:0 0 auto;align-items:center;justify-content:start;gap:var(--ngx-mat-extended-space-3, 16px);min-height:48px;padding:var(--ngx-mat-extended-space-2, 8px);margin:0;border-bottom:1px solid var(--edit-entity-border);box-sizing:border-box;background:var(--ngx-mat-edit-entity-dialog-header-background, var(--ngx-mat-extended-surface, #0f334d));color:var(--ngx-mat-edit-entity-dialog-header-text, var(--ngx-mat-extended-text, #fff))}.edit-entity-title-bar h2{margin:0;font-size:var(--mat-sys-title-medium-size, 18px);font-weight:700;line-height:1.2}.edit-entity-title-bar p{margin:var(--ngx-mat-extended-space-1, 4px) 0 0;color:var(--ngx-mat-edit-entity-dialog-header-text, var(--ngx-mat-extended-text, #fff));font-size:var(--mat-sys-body-small-size, 13px)}.edit-entity-title-content{min-width:0;overflow:hidden}.edit-entity-title-content h2,.edit-entity-title-content p{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.edit-entity-title-actions{display:inline-flex;flex:0 0 auto;align-items:center;gap:var(--ngx-mat-extended-space-2, 8px);margin-left:auto}.edit-entity-title-actions .mat-mdc-icon-button{--mat-icon-button-icon-color: var(--ngx-mat-edit-entity-dialog-header-text, var(--ngx-mat-extended-text, #fff))}.edit-entity-form,.edit-entity-content{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;max-height:none;margin:0;overflow:hidden;padding:0;background:var(--edit-entity-background);box-sizing:border-box}.edit-entity-actions{display:flex;flex:0 0 auto;align-items:center;justify-content:space-between;gap:var(--ngx-mat-extended-space-2, 8px);min-height:54px;padding:var(--ngx-mat-extended-space-2, 8px);border-top:1px solid var(--edit-entity-border);background:var(--ngx-mat-edit-entity-dialog-background, var(--ngx-mat-extended-surface, #ffffff));box-sizing:border-box}.edit-entity-actions-left,.edit-entity-actions-right{display:inline-flex;align-items:center;gap:var(--ngx-mat-extended-space-2, 8px);min-width:0}.edit-entity-updating{position:absolute;inset:0;z-index:20;display:flex;align-items:center;justify-content:center;background:#fff9}.action-btn{--mat-button-text-label-text-color: var(--action-color);--mat-button-filled-label-text-color: var(--action-color);--mat-button-filled-container-color: var(--action-background-color);--mat-button-protected-label-text-color: var(--action-color);--mat-button-protected-container-color: var(--action-background-color)}:host ::ng-deep mat-form-field{--mat-form-field-outlined-disabled-input-text-color: var(--mat-form-field-outlined-input-text-color)}:host ::ng-deep mat-form-field .mat-mdc-chip.mat-mdc-chip-row{--mat-chip-disabled-container-opacity: 1;--mat-chip-elevated-disabled-container-color: var(--mat-chip-elevated-container-color)}@media(max-width:900px){.edit-entity-actions{align-items:stretch;flex-direction:column}.edit-entity-actions-left,.edit-entity-actions-right{justify-content:flex-end;width:100%}}.edit-entity-wizard-header{background:var(--edit-entity-primary);color:var(--ngx-mat-edit-entity-dialog-header-text, var(--mat-sys-on-primary))}.edit-entity-wizard{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;min-width:0;background:transparent;--mat-stepper-header-selected-state-icon-background-color: var(--ngx-mat-edit-entity-wizard-alert, var(--mat-sys-primary));--mat-stepper-header-selected-state-icon-foreground-color: var(--ngx-mat-edit-entity-wizard-on-alert, var(--mat-sys-on-primary));--mat-stepper-header-selected-state-label-text-color: var(--ngx-mat-edit-entity-wizard-toolbar-text, var(--edit-entity-text));--mat-stepper-header-done-state-icon-background-color: var(--ngx-mat-edit-entity-wizard-ok, var(--mat-sys-tertiary));--mat-stepper-header-done-state-icon-foreground-color: var(--ngx-mat-edit-entity-wizard-on-ok, var(--mat-sys-on-tertiary));--mat-stepper-header-edit-state-icon-background-color: var(--ngx-mat-edit-entity-wizard-ok, var(--mat-sys-tertiary));--mat-stepper-header-edit-state-icon-foreground-color: var(--ngx-mat-edit-entity-wizard-on-ok, var(--mat-sys-on-tertiary));--mat-stepper-header-label-text-color: var(--ngx-mat-edit-entity-wizard-toolbar-text, var(--edit-entity-text));--mat-stepper-line-color: transparent;--mat-stepper-container-color: transparent;--mat-stepper-header-height: 56px;--mat-stepper-header-focus-state-layer-shape: 0;--mat-stepper-header-hover-state-layer-shape: 0}.edit-entity-wizard-optional{display:block;align-self:center;padding:2px 5px;border-radius:4px;font-size:11px}.edit-entity-wizard-optional{background:var(--ngx-mat-edit-entity-dialog-secondary, var(--mat-sys-secondary));color:var(--mat-sys-on-secondary)}.edit-entity-wizard-unsaved{position:absolute;inset-inline:8px;bottom:1px;display:block;overflow:hidden;color:var(--edit-entity-muted);font-size:10px;line-height:10px;text-overflow:ellipsis;white-space:nowrap}.edit-entity-wizard-skip{--mat-button-protected-container-color: var(--ngx-mat-edit-entity-dialog-secondary, var(--mat-sys-secondary));--mat-button-protected-label-text-color: var(--mat-sys-on-secondary)}.edit-entity-wizard-step-label{display:flex;flex-direction:column;align-items:flex-start;gap:4px;width:112px;max-width:100%;white-space:normal;line-height:16px;text-align:start;color:var(--ngx-mat-edit-entity-wizard-toolbar-text, var(--edit-entity-text))}.edit-entity-wizard-step-label--selected{color:var(--ngx-mat-edit-entity-wizard-alert, var(--mat-sys-primary))}.edit-entity-wizard-step-name{overflow-wrap:anywhere}.edit-entity-wizard-actions{flex-wrap:wrap}.edit-entity-wizard-actions .edit-entity-actions-left:empty{display:none}.edit-entity-wizard-actions .edit-entity-actions-right{flex:1 1 auto;flex-wrap:wrap;justify-content:flex-end}.edit-entity-wizard-actions .edit-entity-actions-right button{max-width:100%;white-space:normal}:host ::ng-deep .edit-entity-wizard .mat-horizontal-stepper-wrapper,:host ::ng-deep .edit-entity-wizard .mat-horizontal-content-container{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;min-width:0}:host ::ng-deep .edit-entity-wizard .mat-horizontal-stepper-header-container{flex:0 0 auto;min-width:0;overflow-x:auto;overscroll-behavior-x:contain;border-bottom:1px solid var(--edit-entity-border);background:var(--ngx-mat-edit-entity-wizard-toolbar-background, var(--mat-sys-surface))}:host ::ng-deep .edit-entity-wizard .mat-horizontal-stepper-header{flex:1 0 160px;min-width:160px;padding-inline:8px;box-sizing:border-box}:host ::ng-deep .edit-entity-wizard .mat-stepper-horizontal-line{display:none}:host ::ng-deep .edit-entity-wizard .mat-horizontal-content-container{padding:0}:host ::ng-deep .edit-entity-wizard .mat-horizontal-stepper-content.mat-horizontal-stepper-content-current{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;overflow:hidden}.edit-entity-wizard-content-heading{display:flex;flex:0 0 auto;align-items:flex-start;justify-content:space-between;gap:8px;padding:8px}.edit-entity-wizard-content-heading>div{min-width:0}.edit-entity-wizard-content-heading h3{margin:0;color:var(--edit-entity-text);font-size:16px;line-height:24px}.edit-entity-wizard-content-heading p{margin:4px 0 0;color:var(--edit-entity-muted);font-size:13px;line-height:18px}.edit-entity-wizard-content-heading .edit-entity-wizard-optional{flex:0 0 auto;align-self:flex-start;margin-block-start:2px}.edit-entity-wizard-state-icon{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;box-sizing:border-box;border:1px solid var(--edit-entity-border);background:var(--ngx-mat-edit-entity-wizard-toolbar-background, var(--mat-sys-surface));color:var(--edit-entity-muted)}.edit-entity-wizard-state-icon mat-icon{font-size:18px;width:18px;height:18px}.edit-entity-wizard-state-icon--completed{border-color:transparent;background:var(--ngx-mat-edit-entity-wizard-ok, var(--mat-sys-tertiary));color:var(--ngx-mat-edit-entity-wizard-on-ok, var(--mat-sys-on-tertiary))}.edit-entity-wizard-state-icon--selected{border-color:transparent;background:var(--ngx-mat-edit-entity-wizard-alert, var(--mat-sys-primary));color:var(--ngx-mat-edit-entity-wizard-on-alert, var(--mat-sys-on-primary))}\n"]
506
+ }]
507
+ }],
508
+ propDecorators: {
509
+ options: [{
510
+ type: Input,
511
+ args: [{ required: true }]
512
+ }],
513
+ displayMode: [{ type: Input }],
514
+ result: [{ type: Output }],
515
+ closeRequest: [{ type: Output }],
516
+ wizardStepper: [{
517
+ type: ViewChild,
518
+ args: [MatStepper]
519
+ }]
520
+ }
521
+ });
522
+ var EntityWizardDialog = class EntityWizardDialog extends EntityDialogKeyboardHost {
523
+ data;
524
+ editEntityComponent;
525
+ constructor(data, dialogRef, matDialog) {
526
+ super(dialogRef, matDialog);
527
+ this.data = data;
528
+ }
529
+ static ɵfac = i0.ɵɵngDeclareFactory({
530
+ minVersion: "12.0.0",
531
+ version: "22.2.1",
532
+ ngImport: i0,
533
+ type: EntityWizardDialog,
534
+ deps: [
535
+ { token: MAT_DIALOG_DATA },
536
+ { token: i1.MatDialogRef },
537
+ { token: i1.MatDialog }
538
+ ],
539
+ target: i0.ɵɵFactoryTarget.Component
540
+ });
541
+ static ɵcmp = i0.ɵɵngDeclareComponent({
542
+ minVersion: "14.0.0",
543
+ version: "22.2.1",
544
+ type: EntityWizardDialog,
545
+ isStandalone: true,
546
+ selector: "ame-entity-wizard-dialog",
547
+ viewQueries: [{
548
+ propertyName: "editEntityComponent",
549
+ first: true,
550
+ predicate: EntityWizardComponent,
551
+ descendants: true
552
+ }],
553
+ usesInheritance: true,
554
+ ngImport: i0,
555
+ template: "<ame-entity-wizard\n [options]=\"data\"\n displayMode=\"dialog\"\n (closeRequest)=\"close($event)\"\n/>\n",
556
+ styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden}app-edit-entity-component{display:flex;flex:1 1 auto;height:auto;min-height:0;overflow:hidden}\n"],
557
+ dependencies: [{
558
+ kind: "component",
559
+ type: EntityWizardComponent,
560
+ selector: "ame-entity-wizard",
561
+ inputs: ["options", "displayMode"],
562
+ outputs: ["result", "closeRequest"]
563
+ }],
564
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
565
+ });
566
+ };
567
+ i0.ɵɵngDeclareClassMetadata({
568
+ minVersion: "12.0.0",
569
+ version: "22.2.1",
570
+ ngImport: i0,
571
+ type: EntityWizardDialog,
572
+ decorators: [{
573
+ type: Component,
574
+ args: [{
575
+ selector: "ame-entity-wizard-dialog",
576
+ imports: [EntityWizardComponent],
577
+ changeDetection: ChangeDetectionStrategy.OnPush,
578
+ template: "<ame-entity-wizard\n [options]=\"data\"\n displayMode=\"dialog\"\n (closeRequest)=\"close($event)\"\n/>\n",
579
+ styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden}app-edit-entity-component{display:flex;flex:1 1 auto;height:auto;min-height:0;overflow:hidden}\n"]
580
+ }]
581
+ }],
582
+ ctorParameters: () => [
583
+ {
584
+ type: void 0,
585
+ decorators: [{
586
+ type: Inject,
587
+ args: [MAT_DIALOG_DATA]
588
+ }]
589
+ },
590
+ { type: i1.MatDialogRef },
591
+ { type: i1.MatDialog }
592
+ ],
593
+ propDecorators: { editEntityComponent: [{
594
+ type: ViewChild,
595
+ args: [EntityWizardComponent]
596
+ }] }
597
+ });
598
+ var CreateEntityWizardService = class CreateEntityWizardService {
599
+ launcher = inject(EntityDialogLauncher);
600
+ open(options, config = {}) {
601
+ if (!options.wizard?.steps.length || !("tabs" in options.displayOptions)) throw new Error("Use createEntityWizardOptions before opening a creation wizard");
602
+ return this.launcher.open(EntityWizardDialog, options, config);
603
+ }
604
+ static ɵfac = i0.ɵɵngDeclareFactory({
605
+ minVersion: "12.0.0",
606
+ version: "22.2.1",
607
+ ngImport: i0,
608
+ type: CreateEntityWizardService,
609
+ deps: [],
610
+ target: i0.ɵɵFactoryTarget.Injectable
611
+ });
612
+ static ɵprov = i0.ɵɵngDeclareInjectable({
613
+ minVersion: "12.0.0",
614
+ version: "22.2.1",
615
+ ngImport: i0,
616
+ type: CreateEntityWizardService,
617
+ providedIn: "root"
618
+ });
619
+ };
620
+ i0.ɵɵngDeclareClassMetadata({
621
+ minVersion: "12.0.0",
622
+ version: "22.2.1",
623
+ ngImport: i0,
624
+ type: CreateEntityWizardService,
625
+ decorators: [{
626
+ type: Injectable,
627
+ args: [{ providedIn: "root" }]
628
+ }]
629
+ });
630
+ function createEntityWizardOptions(options) {
631
+ if (!options.steps.length) throw new Error("A creation wizard requires at least one step");
632
+ const { steps, icon, nextLabel, createNextLabel, skipLabel, finishLaterLabel, finishLabel, optionalLabel, unsavedLabel, ...base } = options;
633
+ return {
634
+ ...base,
635
+ service: base.service ? {
636
+ ...base.service,
637
+ closeOnUpsert: false
638
+ } : void 0,
639
+ wizard: {
640
+ steps,
641
+ icon,
642
+ nextLabel,
643
+ createNextLabel,
644
+ skipLabel,
645
+ finishLaterLabel,
646
+ finishLabel,
647
+ optionalLabel,
648
+ unsavedLabel
649
+ },
650
+ displayOptions: { tabs: steps.map((step) => ({
651
+ id: step.id,
652
+ label: step.label,
653
+ icon: step.icon,
654
+ displayOptions: step.displayOptions
655
+ })) }
656
+ };
657
+ }
658
+ export { CreateEntityWizardService, CreateEntityWizardState, EntityWizardComponent, EntityWizardDialog, createEntityWizardOptions };
659
+
660
+ //# sourceMappingURL=eherve-angular-material-entity-wizard.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eherve-angular-material-entity-wizard.mjs","names":["i1"],"sources":["../../../projects/entity-wizard/src/lib/create-entity-wizard-state.ts","../../../projects/entity-wizard/src/lib/entity-wizard.component.ts","../../../projects/entity-wizard/src/lib/entity-wizard.component.html","../../../projects/entity-wizard/src/lib/entity-wizard-dialog.component.ts","../../../projects/entity-wizard/src/lib/entity-wizard-dialog.component.html","../../../projects/entity-wizard/src/lib/create-entity-wizard.service.ts","../../../projects/entity-wizard/src/lib/create-entity-wizard.types.ts"],"sourcesContent":["import { AbstractControl, FormGroup } from \"@angular/forms\";\nimport {\n EditEntityFormGroup,\n isNonEditable,\n} from \"@eherve/angular-material-forms/core\";\nimport { cloneDeep, get, has, isEqual, set } from \"lodash-es\";\n\n/** Scoped persistence shared by the wizard engine; no host-specific model assumptions. */\nexport class CreateEntityWizardState {\n valid(form: EditEntityFormGroup, paths: string[]): boolean {\n return paths.every((path) => {\n const control = form.getControl(path);\n return (\n !control || control.disabled || (!control.pending && control.valid)\n );\n });\n }\n\n project(\n form: EditEntityFormGroup,\n paths: string[],\n changedOnly: boolean,\n always: string[] = [],\n ): Record<string, unknown> {\n const value: Record<string, unknown> = {};\n for (const path of new Set([...paths, ...always])) {\n const control = form.getControl(path);\n if (!control || isNonEditable(control)) continue;\n const current = control.getRawValue();\n const initial = this.read(form.initialValue, path);\n if (!changedOnly || always.includes(path) || !isEqual(current, initial))\n set(value, path, cloneDeep(current));\n }\n return value;\n }\n\n hydrate(\n form: EditEntityFormGroup,\n record: object,\n submitted: string[],\n ): void {\n const baseline = cloneDeep(form.initialValue);\n const visit = (control: AbstractControl, path: string): void => {\n if (control instanceof FormGroup) {\n for (const [key, child] of Object.entries(control.controls))\n visit(child, path ? `${path}.${key}` : key);\n return;\n }\n const saved = submitted.some(\n (name) => path === name || path.startsWith(`${name}.`),\n );\n const current = control.getRawValue();\n const draft = !isEqual(current, this.read(baseline, path));\n if (saved || !draft) {\n if (has(record, path))\n control.patchValue(cloneDeep(get(record, path)), {\n emitEvent: false,\n });\n if (Object.prototype.hasOwnProperty.call(form.controls, path))\n baseline[path] = cloneDeep(control.getRawValue());\n else set(baseline, path, cloneDeep(control.getRawValue()));\n control.markAsPristine();\n control.markAsUntouched();\n }\n };\n visit(form, \"\");\n form.setInitialValue(baseline);\n }\n\n private read(value: Record<string, unknown>, path: string): unknown {\n return Object.prototype.hasOwnProperty.call(value, path)\n ? value[path]\n : get(value, path);\n }\n}\n","import { CdkStepper } from \"@angular/cdk/stepper\";\nimport { CommonModule } from \"@angular/common\";\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n Input,\n OnChanges,\n OnDestroy,\n OnInit,\n Output,\n signal,\n SimpleChanges,\n ViewChild,\n} from \"@angular/core\";\nimport { FormControl, ReactiveFormsModule, Validators } from \"@angular/forms\";\nimport { MatButtonModule } from \"@angular/material/button\";\nimport { MatIconModule } from \"@angular/material/icon\";\nimport { MatProgressSpinnerModule } from \"@angular/material/progress-spinner\";\nimport { MatStepper, MatStepperModule } from \"@angular/material/stepper\";\nimport { MatTooltipModule } from \"@angular/material/tooltip\";\nimport {\n EditEntityConditionalStateService,\n EditEntityFormBuilderService,\n EditEntityRecord,\n EditEntityRenderService,\n EditEntitySubmitValueService,\n} from \"@eherve/angular-material-forms/core\";\nimport { EntityFormSession } from \"@eherve/angular-material-forms/editor\";\nimport { FormContentComponent } from \"@eherve/angular-material-forms/layout\";\nimport { cloneDeep, get, has, set } from \"lodash-es\";\nimport { CreateEntityWizardState } from \"./create-entity-wizard-state\";\nimport {\n CreateEntityWizardComponentOptions,\n CreateEntityWizardStep,\n} from \"./create-entity-wizard.types\";\n@Component({\n selector: \"ame-entity-wizard\",\n imports: [\n CommonModule,\n ReactiveFormsModule,\n MatButtonModule,\n MatIconModule,\n MatProgressSpinnerModule,\n MatTooltipModule,\n FormContentComponent,\n MatStepperModule,\n ],\n providers: [\n EntityFormSession,\n EditEntityConditionalStateService,\n EditEntityFormBuilderService,\n EditEntityRenderService,\n EditEntitySubmitValueService,\n ],\n templateUrl: \"./entity-wizard.component.html\",\n styleUrl: \"./entity-wizard.component.scss\",\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class EntityWizardComponent<\n TRecord extends EditEntityRecord = EditEntityRecord,\n >\n implements OnInit, OnChanges, OnDestroy\n{\n @Input({ required: true })\n options!: CreateEntityWizardComponentOptions<TRecord>;\n @Input() displayMode: \"page\" | \"dialog\" = \"page\";\n readonly session = inject<EntityFormSession<TRecord>>(EntityFormSession);\n @Output() readonly result = this.session.result;\n @Output() readonly closeRequest = this.session.closeRequest;\n ngOnInit(): void {\n this.session.options = this.options;\n this.session.displayMode = this.displayMode;\n this.session.policy = {\n valid: () => this.wizardValid,\n value: (create) => this.wizardValue(create),\n patch: (record, value) => this.patchWizardRecord(record, value),\n saved: (record, value) => this.hydrateWizard(record, value),\n hydrate: (record) =>\n this.wizardState.hydrate(this.session.formGroup, record, []),\n refresh: () => this.refreshWizardState(),\n reset: () => {\n this.wizardIndex.set(0);\n this.wizardCompleted.set(this.options.wizard.steps.map(() => false));\n this.wizardVisibleIndices.set([]);\n this.wizardNavigationControls = [];\n },\n componentUpdated: () => this.completeWizardStep(),\n beforeClose: () =>\n this.session.formGroup.setInitialValue(\n this.session.formGroup.initialValue,\n ),\n stickyResult: true,\n };\n void this.session.initialize();\n }\n ngOnChanges(changes: SimpleChanges): void {\n if (changes[\"options\"] && !changes[\"options\"].firstChange) {\n this.session.options = this.options;\n void this.session.initialize();\n }\n }\n ngOnDestroy(): void {\n this.session.destroy();\n }\n requestClose(): Promise<void> {\n return this.session.requestClose();\n }\n refreshPresentation(): void {\n this.session.refreshPresentation();\n }\n submitFromKeyboard(): boolean {\n if (\n !this.options.validateOnEnter ||\n this.session.loading ||\n this.session.updating\n )\n return false;\n void this.wizardNext();\n return true;\n }\n readonly wizardIndex = signal(0);\n readonly wizardDrafts = signal<boolean[]>([]);\n readonly wizardCompleted = signal<boolean[]>([]);\n readonly wizardVisibleIndices = signal<number[]>([]);\n readonly wizardVisualIndex = computed(() =>\n Math.max(0, this.wizardVisibleIndices().indexOf(this.wizardIndex())),\n );\n readonly wizardNavigationBlocked = signal(false);\n readonly wizardLinear = signal(true);\n wizardNavigationControls: FormControl<boolean>[] = [];\n @ViewChild(MatStepper) wizardStepper?: CdkStepper;\n private readonly wizardState = new CreateEntityWizardState();\n get wizardStep(): CreateEntityWizardStep<TRecord> | undefined {\n return this.options?.wizard?.steps[this.wizardIndex()];\n }\n\n get wizardLast(): boolean {\n return this.wizardIndex() === this.wizardVisibleIndices().at(-1);\n }\n\n get wizardPrimaryLabel(): string {\n const wizard = this.options.wizard;\n if (!this.options.record?._id)\n return (\n wizard?.createNextLabel ?? this.session.intl.messages.wizardCreateNext\n );\n return this.wizardLast\n ? (wizard?.finishLabel ?? this.session.intl.messages.wizardFinish)\n : (wizard?.nextLabel ?? this.session.intl.messages.wizardNext);\n }\n\n get wizardExitLabel(): string {\n return this.options.record?._id\n ? (this.options.wizard?.finishLaterLabel ??\n this.session.intl.messages.wizardFinishLater)\n : this.session.cancelLabel;\n }\n\n selectWizardVisualStep(position: number): void {\n const index = this.wizardVisibleIndices()[position];\n if (index !== undefined) this.selectWizardStep(index);\n }\n\n private advanceWizard(): void {\n const next = this.wizardVisibleIndices()[this.wizardVisualIndex() + 1];\n if (next !== undefined) {\n this.wizardIndex.set(next);\n this.refreshWizardNavigation();\n }\n }\n\n private completeWizardStep(): void {\n const completed = [...this.wizardCompleted()];\n completed[this.wizardIndex()] = true;\n this.wizardCompleted.set(completed);\n this.refreshWizardNavigation();\n }\n\n private refreshWizardNavigation(): void {\n if (!this.options?.wizard) return;\n const blocked = this.session.updating || this.wizardPending;\n this.wizardNavigationBlocked.set(blocked);\n const linear = !this.options.record?._id || blocked;\n this.wizardLinear.set(linear);\n if (this.wizardStepper) this.wizardStepper.linear = linear;\n const count = this.options.wizard.steps.length;\n if (this.wizardNavigationControls.length !== count) {\n this.wizardNavigationControls = Array.from(\n { length: count },\n () =>\n new FormControl(false, {\n nonNullable: true,\n validators: Validators.requiredTrue,\n }),\n );\n }\n for (const [index, control] of this.wizardNavigationControls.entries()) {\n control.setValue(\n !blocked && (index !== 0 || !!this.options.record?._id),\n { emitEvent: false },\n );\n }\n // CDK checks these public step properties synchronously when a header is selected.\n // Synchronize immediately, including before Angular's next rendering pass.\n this.wizardStepper?.steps.forEach((step, position) => {\n const index = this.wizardVisibleIndices()[position];\n step.optional = false;\n step.interacted = true;\n step.completed = !blocked && !!this.wizardCompleted()[index];\n step.editable = !blocked && (index === 0 || !!this.options.record?._id);\n });\n }\n\n get wizardValid(): boolean {\n return this.wizardState.valid(this.session.formGroup, [\n ...this.wizardPaths(),\n ...this.wizardAlwaysPaths(),\n ]);\n }\n\n get wizardPending(): boolean {\n return [...this.wizardPaths(), ...this.wizardAlwaysPaths()].some(\n (path) => this.session.formGroup.getControl(path)?.pending,\n );\n }\n\n get wizardUnsaved(): boolean {\n return (\n Object.keys(\n this.wizardState.project(\n this.session.formGroup,\n this.wizardPaths(),\n true,\n ),\n ).length > 0\n );\n }\n\n selectWizardStep(index: number): void {\n if (\n this.session.updating ||\n this.wizardPending ||\n !this.options.wizard ||\n index < 0 ||\n index >= this.options.wizard.steps.length ||\n !this.wizardVisibleIndices().includes(index)\n )\n return;\n if (index > 0 && !this.options.record?._id) return;\n this.wizardIndex.set(index);\n this.refreshWizardNavigation();\n }\n\n skipWizardStep(): void {\n if (\n this.session.updating ||\n this.wizardPending ||\n !this.wizardStep?.optional ||\n !this.options.record?._id\n )\n return;\n if (this.wizardLast) void this.requestClose();\n else this.advanceWizard();\n }\n\n async wizardNext(): Promise<void> {\n if (this.session.updating || !this.options.wizard) return;\n const isCurrent = this.session.captureCurrentOperation();\n if (!this.wizardStep?.componentManaged) {\n for (const path of this.wizardPaths())\n this.session.formGroup.getControl(path)?.markAllAsTouched();\n if (!this.wizardValid) return;\n const previous = this.session.savedResult;\n if (!this.options.record?._id) await this.session.create();\n else if (this.wizardUnsaved) await this.session.update();\n else {\n this.completeWizardStep();\n if (this.wizardLast) await this.requestClose();\n else this.advanceWizard();\n return;\n }\n if (!isCurrent() || previous === this.session.savedResult) return;\n }\n if (this.wizardLast) await this.requestClose();\n else this.advanceWizard();\n }\n\n private wizardPaths(index = this.wizardIndex()): string[] {\n const display = this.options?.displayOptions;\n const tab = display && \"tabs\" in display ? display.tabs[index] : undefined;\n const names = tab\n ? this.session.renderService\n .collectFields(tab.displayOptions)\n .flatMap((field) =>\n field.kind === \"separator\" ||\n field.kind === \"metadata\" ||\n field.kind === \"display\"\n ? []\n : [field.name],\n )\n : [];\n return [\n ...new Set([\n ...names,\n ...(this.options.wizard?.steps[index]?.fields ?? []),\n ]),\n ];\n }\n\n private wizardAlwaysPaths(): string[] {\n return this.session.fields.flatMap((field) =>\n field.kind !== \"separator\" &&\n field.kind !== \"metadata\" &&\n field.kind !== \"display\" &&\n field.alwaysInUpsert\n ? [field.name]\n : [],\n );\n }\n\n private wizardValue(create: boolean): Record<string, unknown> {\n const paths = this.wizardPaths();\n const always = this.wizardAlwaysPaths();\n const value = this.wizardState.project(\n this.session.formGroup,\n paths,\n !create,\n always,\n );\n if (create)\n for (const path of [...paths, ...always]) {\n if (\n !this.session.formGroup.getControl(path) &&\n has(this.options.defaultValues, path)\n )\n set(value, path, cloneDeep(get(this.options.defaultValues, path)));\n }\n return value;\n }\n\n private patchWizardRecord(\n record: TRecord,\n value: Record<string, unknown>,\n ): TRecord {\n const updated = cloneDeep(record);\n for (const path of [...this.wizardPaths(), ...this.wizardAlwaysPaths()]) {\n if (has(value, path)) set(updated, path, cloneDeep(get(value, path)));\n }\n return updated;\n }\n\n private async hydrateWizard(\n record: TRecord,\n value: Record<string, unknown>,\n ): Promise<void> {\n const paths = [...this.wizardPaths(), ...this.wizardAlwaysPaths()].filter(\n (path) => has(value, path),\n );\n this.wizardState.hydrate(this.session.formGroup, record, paths);\n this.completeWizardStep();\n this.session.updated = this.session.formGroup.isUpdated;\n this.session.refreshAllowedActions();\n this.session.refreshRuntimeState();\n }\n\n private refreshWizardState(): void {\n const visible = (this.session.renderState().tabs ?? []).flatMap(\n (tab, index) => (tab.isVisible ? [index] : []),\n );\n this.wizardVisibleIndices.set(visible);\n if (!visible.includes(this.wizardIndex()) && visible.length)\n this.wizardIndex.set(visible[0]);\n this.wizardDrafts.set(\n this.options.wizard.steps.map(\n (_, index) =>\n Object.keys(\n this.wizardState.project(\n this.session.formGroup,\n this.wizardPaths(index),\n true,\n ),\n ).length > 0,\n ),\n );\n this.refreshWizardNavigation();\n }\n}\n","<!-- @format -->\n\n@if (session.loading) {\n <div class=\"edit-entity-loading\">\n <mat-spinner [diameter]=\"72\" />\n <span>{{ session.loadingLabel }}</span>\n </div>\n} @else if (session.errorLabel) {\n <div class=\"edit-entity-empty-state\">\n <mat-icon>info</mat-icon>\n <span>{{ session.errorLabel }}</span>\n\n @if (displayMode === \"dialog\") {\n <button mat-stroked-button type=\"button\" (click)=\"requestClose()\">\n {{ session.closeLabel }}\n </button>\n }\n </div>\n} @else {\n @if (\n session.title ||\n session.subtitle ||\n displayMode === \"dialog\" ||\n session.getActions(\"top\").length\n ) {\n <header\n class=\"edit-entity-title-bar\"\n [class.edit-entity-wizard-header]=\"options.wizard\"\n >\n @if (options.wizard?.icon) {\n <mat-icon>{{ options.wizard?.icon }}</mat-icon>\n }\n <div class=\"edit-entity-title-content\">\n @if (session.title) {\n <h2 [innerHTML]=\"session.title\"></h2>\n }\n @if (session.subtitle) {\n <p [innerHTML]=\"session.subtitle\"></p>\n }\n </div>\n\n <div class=\"edit-entity-title-actions\">\n @for (\n action of session.getActions(\"top\");\n track session.trackRuntimeAction($index, action)\n ) {\n <ng-container\n *ngTemplateOutlet=\"actionTemplate; context: { action }\"\n ></ng-container>\n }\n\n @if (options.showJsonDataButton === true) {\n <button\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"session.intl.messages.showJson\"\n [attr.aria-label]=\"session.intl.messages.showJson\"\n (click)=\"session.openJsonDataDialog()\"\n >\n <mat-icon>file_json</mat-icon>\n </button>\n }\n\n @if (displayMode === \"dialog\") {\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"session.closeLabel\"\n (click)=\"requestClose()\"\n >\n <mat-icon>close</mat-icon>\n </button>\n }\n </div>\n </header>\n }\n\n <form class=\"edit-entity-form\" [formGroup]=\"session.formGroup\">\n <main class=\"edit-entity-content\">\n @if (options.wizard; as wizard) {\n <mat-stepper\n class=\"edit-entity-wizard\"\n orientation=\"horizontal\"\n labelPosition=\"end\"\n [linear]=\"wizardLinear()\"\n [selectedIndex]=\"wizardVisualIndex()\"\n (selectionChange)=\"selectWizardVisualStep($event.selectedIndex)\"\n >\n <!-- Material icon context.active also includes completed steps; selection follows the wizard index. -->\n <ng-template matStepperIcon=\"number\" let-index=\"index\">\n <ng-container\n *ngTemplateOutlet=\"\n wizardStateIcon;\n context: { index, active: index === wizardVisualIndex() }\n \"\n ></ng-container>\n </ng-template>\n <ng-template matStepperIcon=\"edit\" let-index=\"index\">\n <ng-container\n *ngTemplateOutlet=\"\n wizardStateIcon;\n context: { index, active: index === wizardVisualIndex() }\n \"\n ></ng-container>\n </ng-template>\n <ng-template matStepperIcon=\"done\" let-index=\"index\">\n <ng-container\n *ngTemplateOutlet=\"\n wizardStateIcon;\n context: { index, active: index === wizardVisualIndex() }\n \"\n ></ng-container>\n </ng-template>\n <ng-template #wizardStateIcon let-index=\"index\" let-active=\"active\">\n <span\n class=\"edit-entity-wizard-state-icon\"\n [class.edit-entity-wizard-state-icon--selected]=\"active\"\n [class.edit-entity-wizard-state-icon--completed]=\"\n !active && wizardCompleted()[wizardVisibleIndices()[index]]\n \"\n >\n @if (\n !active && wizardCompleted()[wizardVisibleIndices()[index]]\n ) {\n <mat-icon>check</mat-icon>\n } @else {\n {{ index + 1 }}\n }\n </span>\n </ng-template>\n @for (index of wizardVisibleIndices(); track index) {\n @let step = wizard.steps[index];\n <mat-step\n [optional]=\"false\"\n [stepControl]=\"wizardNavigationControls[index]\"\n [completed]=\"\n !wizardNavigationBlocked() && wizardCompleted()[index]\n \"\n [editable]=\"\n !wizardNavigationBlocked() &&\n (index === 0 || !!options.record?._id)\n \"\n >\n <ng-template matStepLabel>\n <div\n class=\"edit-entity-wizard-step-label\"\n [class.edit-entity-wizard-step-label--selected]=\"\n index === wizardIndex()\n \"\n >\n <span class=\"edit-entity-wizard-step-name\">{{\n step.label\n }}</span>\n @if (wizardDrafts()[index]) {\n <span\n class=\"edit-entity-wizard-unsaved\"\n [title]=\"\n wizard.unsavedLabel ||\n session.intl.messages.wizardUnsaved\n \"\n >{{\n wizard.unsavedLabel ||\n session.intl.messages.wizardUnsaved\n }}</span\n >\n }\n </div>\n </ng-template>\n <ng-template matStepContent>\n @if (index === wizardIndex()) {\n <header class=\"edit-entity-wizard-content-heading\">\n <div>\n <h3>{{ step.label }}</h3>\n @if (step.description) {\n <p>{{ step.description }}</p>\n }\n </div>\n @if (step.optional && index > 0) {\n <span class=\"edit-entity-wizard-optional\">{{\n wizard.optionalLabel ||\n session.intl.messages.wizardOptional\n }}</span>\n }\n </header>\n <ame-form-content\n [state]=\"\n session.renderState().tabs?.[index]?.renderState || {\n kind: 'empty',\n }\n \"\n [formGroup]=\"session.formGroup\"\n [options]=\"options\"\n [context]=\"session\"\n />\n }\n </ng-template>\n </mat-step>\n }\n </mat-stepper>\n }\n </main>\n </form>\n\n <footer\n class=\"edit-entity-actions\"\n [class.edit-entity-wizard-actions]=\"options.wizard\"\n >\n <div class=\"edit-entity-actions-left\">\n @for (\n action of session.getActions(\"bottom\");\n track session.trackRuntimeAction($index, action)\n ) {\n <ng-container\n *ngTemplateOutlet=\"actionTemplate; context: { action }\"\n ></ng-container>\n }\n </div>\n\n <div class=\"edit-entity-actions-right\">\n @for (\n action of session.getActions(\"bottom-end\");\n track session.trackRuntimeAction($index, action)\n ) {\n <ng-container\n *ngTemplateOutlet=\"actionTemplate; context: { action }\"\n ></ng-container>\n }\n\n @if (options.wizard; as wizard) {\n <button\n mat-stroked-button\n type=\"button\"\n [disabled]=\"session.updating\"\n (click)=\"requestClose()\"\n >\n {{ wizardExitLabel }}\n </button>\n @if (wizardStep?.optional && options.record?._id) {\n <button\n mat-raised-button\n class=\"edit-entity-wizard-skip\"\n type=\"button\"\n [disabled]=\"wizardNavigationBlocked()\"\n (click)=\"skipWizardStep()\"\n >\n {{ wizard.skipLabel || session.intl.messages.wizardSkip }}\n </button>\n }\n <button\n mat-raised-button\n color=\"secondary\"\n type=\"button\"\n [disabled]=\"\n session.updating || (!wizardStep?.componentManaged && !wizardValid)\n \"\n (click)=\"wizardNext()\"\n >\n {{ wizardPrimaryLabel }}\n </button>\n }\n </div>\n </footer>\n\n @if (session.updating) {\n <div class=\"edit-entity-updating\">\n <mat-spinner [diameter]=\"72\" />\n </div>\n }\n}\n\n<ng-template #actionTemplate let-action=\"action\">\n @if (session.isRuntimeButtonAction(action)) {\n @if (action.buttonType === \"raised\") {\n <button\n class=\"action-btn\"\n mat-raised-button\n type=\"button\"\n [style.--action-color]=\"action.contrastColor\"\n [style.--action-background-color]=\"action.color\"\n [disabled]=\"session.updating || action.isDisabled\"\n [matTooltip]=\"action.tooltip || ''\"\n (click)=\"session.handleAction(action)\"\n >\n @if (action.icon) {\n <mat-icon>{{ action.icon }}</mat-icon>\n }\n <span [innerHTML]=\"action.label\"></span>\n </button>\n } @else {\n <button\n class=\"action-btn\"\n mat-button\n type=\"button\"\n [style.--action-color]=\"action.color\"\n [disabled]=\"session.updating || action.isDisabled\"\n [matTooltip]=\"action.tooltip || ''\"\n (click)=\"session.handleAction(action)\"\n >\n @if (action.icon) {\n <mat-icon>{{ action.icon }}</mat-icon>\n }\n <span [innerHTML]=\"action.label\"></span>\n </button>\n }\n } @else if (session.isRuntimeComponentAction(action)) {\n <ng-container\n *ngComponentOutlet=\"action.component; inputs: action.componentInputs\"\n ></ng-container>\n }\n</ng-template>\n","import {\n ChangeDetectionStrategy,\n Component,\n Inject,\n ViewChild,\n} from \"@angular/core\";\nimport {\n MAT_DIALOG_DATA,\n MatDialog,\n MatDialogRef,\n} from \"@angular/material/dialog\";\nimport {\n EditEntityRecord,\n EditEntityDialogResult,\n} from \"@eherve/angular-material-forms/core\";\nimport { EntityDialogKeyboardHost } from \"@eherve/angular-material-forms/editor\";\nimport { EntityWizardComponent } from \"./entity-wizard.component\";\nimport { CreateEntityWizardComponentOptions } from \"./create-entity-wizard.types\";\n@Component({\n selector: \"ame-entity-wizard-dialog\",\n imports: [EntityWizardComponent],\n templateUrl: \"./entity-wizard-dialog.component.html\",\n styleUrl: \"./entity-wizard-dialog.component.scss\",\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class EntityWizardDialog<\n TRecord extends EditEntityRecord = EditEntityRecord,\n> extends EntityDialogKeyboardHost<TRecord, EntityWizardDialog<TRecord>> {\n @ViewChild(EntityWizardComponent)\n protected override editEntityComponent?: EntityWizardComponent<TRecord>;\n constructor(\n @Inject(MAT_DIALOG_DATA)\n public readonly data: CreateEntityWizardComponentOptions<TRecord>,\n dialogRef: MatDialogRef<\n EntityWizardDialog<TRecord>,\n EditEntityDialogResult<TRecord>\n >,\n matDialog: MatDialog,\n ) {\n super(dialogRef, matDialog);\n }\n}\n","<ame-entity-wizard\n [options]=\"data\"\n displayMode=\"dialog\"\n (closeRequest)=\"close($event)\"\n/>\n","import { inject, Injectable } from \"@angular/core\";\nimport { MatDialogRef } from \"@angular/material/dialog\";\nimport {\n EditEntityDialogResult,\n EditEntityRecord,\n} from \"@eherve/angular-material-forms/core\";\nimport {\n EntityDialogLauncher,\n EntityDialogOpenConfig,\n} from \"@eherve/angular-material-forms/editor\";\nimport { CreateEntityWizardComponentOptions } from \"./create-entity-wizard.types\";\nimport { EntityWizardDialog } from \"./entity-wizard-dialog.component\";\nexport type CreateEntityWizardOpenConfig<TRecord extends EditEntityRecord> =\n EntityDialogOpenConfig<TRecord>;\n@Injectable({ providedIn: \"root\" })\nexport class CreateEntityWizardService {\n private readonly launcher = inject(EntityDialogLauncher);\n open<TRecord extends EditEntityRecord>(\n options: CreateEntityWizardComponentOptions<TRecord>,\n config: CreateEntityWizardOpenConfig<TRecord> = {},\n ): MatDialogRef<\n EntityWizardDialog<TRecord>,\n EditEntityDialogResult<TRecord>\n > {\n if (!options.wizard?.steps.length || !(\"tabs\" in options.displayOptions))\n throw new Error(\n \"Use createEntityWizardOptions before opening a creation wizard\",\n );\n return this.launcher.open(EntityWizardDialog<TRecord>, options, config);\n }\n}\n","import {\n EditEntityComponentOptions,\n EditEntityRecord,\n EditEntityTabDisplayOptions,\n} from \"@eherve/angular-material-forms/core\";\n\nexport interface CreateEntityWizardStep<\n TRecord extends EditEntityRecord = EditEntityRecord,\n> {\n id: string;\n label: string;\n description?: string;\n icon?: string;\n optional?: boolean;\n displayOptions: EditEntityTabDisplayOptions<TRecord>;\n /** Explicit control paths, including controls prepared by the host. Defaults to rendered fields. */\n fields?: string[];\n /** This step's components persist their own mutations; Continue never submits the form. */\n componentManaged?: boolean;\n}\n\nexport interface CreateEntityWizardConfiguration<\n TRecord extends EditEntityRecord = EditEntityRecord,\n> {\n steps: CreateEntityWizardStep<TRecord>[];\n icon?: string;\n nextLabel?: string;\n createNextLabel?: string;\n skipLabel?: string;\n finishLaterLabel?: string;\n finishLabel?: string;\n optionalLabel?: string;\n unsavedLabel?: string;\n}\n\nexport interface CreateEntityWizardComponentOptions<\n TRecord extends EditEntityRecord = EditEntityRecord,\n> extends EditEntityComponentOptions<TRecord> {\n wizard: CreateEntityWizardConfiguration<TRecord>;\n}\n\nexport type CreateEntityWizardOptions<\n TRecord extends EditEntityRecord = EditEntityRecord,\n> = Omit<\n EditEntityComponentOptions<TRecord>,\n \"displayOptions\" | \"record\" | \"wizard\" | \"navigation\"\n> &\n CreateEntityWizardConfiguration<TRecord>;\n\n/** Normalize before decorating access/fields. The opening service uses this exact options object. */\nexport function createEntityWizardOptions<TRecord extends EditEntityRecord>(\n options: CreateEntityWizardOptions<TRecord>,\n): CreateEntityWizardComponentOptions<TRecord> {\n if (!options.steps.length)\n throw new Error(\"A creation wizard requires at least one step\");\n const {\n steps,\n icon,\n nextLabel,\n createNextLabel,\n skipLabel,\n finishLaterLabel,\n finishLabel,\n optionalLabel,\n unsavedLabel,\n ...base\n } = options;\n return {\n ...base,\n service: base.service\n ? { ...base.service, closeOnUpsert: false }\n : undefined,\n wizard: {\n steps,\n icon,\n nextLabel,\n createNextLabel,\n skipLabel,\n finishLaterLabel,\n finishLabel,\n optionalLabel,\n unsavedLabel,\n },\n displayOptions: {\n tabs: steps.map((step) => ({\n id: step.id,\n label: step.label,\n icon: step.icon,\n displayOptions: step.displayOptions,\n })),\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "@eherve/angular-material-entity-wizard",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "22.0.0",
4
+ "author": "Eric Herve <eric.herve.95@gmail.com>",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/eherve/angular-material-extended.git"
8
+ },
9
+ "private": false,
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "peerDependencies": {
14
+ "@angular/animations": "^22.0.0",
15
+ "@angular/cdk": "^22.0.0",
16
+ "@angular/common": "^22.0.0",
17
+ "@angular/core": "^22.0.0",
18
+ "@angular/material": "^22.0.0",
19
+ "rxjs": "^7.8.0",
20
+ "@angular/forms": "^22.0.0",
21
+ "@eherve/angular-material-section-tabs": "^22.2.0",
22
+ "moment": "^2.30.1",
23
+ "dayjs": "^1.11.20",
24
+ "@angular/material-moment-adapter": "^22.0.0",
25
+ "@ng-matero/extensions": "^22.1.1",
26
+ "@ng-matero/extensions-moment-adapter": "^22.0.0",
27
+ "@eherve/angular-material-forms": "^22.0.0"
28
+ },
29
+ "dependencies": {
30
+ "tslib": "^2.3.0",
31
+ "lodash-es": "^4.17.21",
32
+ "subsink": "^1.0.2"
33
+ },
34
+ "module": "fesm2022/eherve-angular-material-entity-wizard.mjs",
35
+ "typings": "types/eherve-angular-material-entity-wizard.d.ts",
36
+ "exports": {
37
+ "./package.json": {
38
+ "default": "./package.json"
39
+ },
40
+ ".": {
41
+ "types": "./types/eherve-angular-material-entity-wizard.d.ts",
42
+ "default": "./fesm2022/eherve-angular-material-entity-wizard.mjs"
43
+ }
44
+ },
45
+ "sideEffects": false,
46
+ "type": "module"
6
47
  }
@@ -0,0 +1,114 @@
1
+ import { EditEntityComponentOptions, EditEntityDialogResult, EditEntityFormGroup, EditEntityRecord, EditEntityTabDisplayOptions } from "@eherve/angular-material-forms/core";
2
+ import { MatDialog, MatDialogRef } from "@angular/material/dialog";
3
+ import { EntityDialogKeyboardHost, EntityDialogOpenConfig, EntityFormSession } from "@eherve/angular-material-forms/editor";
4
+ import { CdkStepper } from "@angular/cdk/stepper";
5
+ import * as i0 from "@angular/core";
6
+ import { OnChanges, OnDestroy, OnInit, SimpleChanges } from "@angular/core";
7
+ import { FormControl } from "@angular/forms";
8
+ /** Scoped persistence shared by the wizard engine; no host-specific model assumptions. */
9
+ export declare class CreateEntityWizardState {
10
+ valid(form: EditEntityFormGroup, paths: string[]): boolean;
11
+ project(form: EditEntityFormGroup, paths: string[], changedOnly: boolean, always?: string[]): Record<string, unknown>;
12
+ hydrate(form: EditEntityFormGroup, record: object, submitted: string[]): void;
13
+ private read;
14
+ }
15
+ export interface CreateEntityWizardStep<TRecord extends EditEntityRecord = EditEntityRecord> {
16
+ id: string;
17
+ label: string;
18
+ description?: string;
19
+ icon?: string;
20
+ optional?: boolean;
21
+ displayOptions: EditEntityTabDisplayOptions<TRecord>;
22
+ /** Explicit control paths, including controls prepared by the host. Defaults to rendered fields. */
23
+ fields?: string[];
24
+ /** This step's components persist their own mutations; Continue never submits the form. */
25
+ componentManaged?: boolean;
26
+ }
27
+ export interface CreateEntityWizardConfiguration<TRecord extends EditEntityRecord = EditEntityRecord> {
28
+ steps: CreateEntityWizardStep<TRecord>[];
29
+ icon?: string;
30
+ nextLabel?: string;
31
+ createNextLabel?: string;
32
+ skipLabel?: string;
33
+ finishLaterLabel?: string;
34
+ finishLabel?: string;
35
+ optionalLabel?: string;
36
+ unsavedLabel?: string;
37
+ }
38
+ export interface CreateEntityWizardComponentOptions<TRecord extends EditEntityRecord = EditEntityRecord> extends EditEntityComponentOptions<TRecord> {
39
+ wizard: CreateEntityWizardConfiguration<TRecord>;
40
+ }
41
+ export type CreateEntityWizardOptions<TRecord extends EditEntityRecord = EditEntityRecord> = Omit<EditEntityComponentOptions<TRecord>, "displayOptions" | "record" | "wizard" | "navigation"> & CreateEntityWizardConfiguration<TRecord>;
42
+ /** Normalize before decorating access/fields. The opening service uses this exact options object. */
43
+ export declare function createEntityWizardOptions<TRecord extends EditEntityRecord>(options: CreateEntityWizardOptions<TRecord>): CreateEntityWizardComponentOptions<TRecord>;
44
+ export declare class EntityWizardComponent<TRecord extends EditEntityRecord = EditEntityRecord> implements OnInit, OnChanges, OnDestroy {
45
+ options: CreateEntityWizardComponentOptions<TRecord>;
46
+ displayMode: "page" | "dialog";
47
+ readonly session: EntityFormSession<TRecord>;
48
+ readonly result: import("@angular/core").EventEmitter<import("@eherve/angular-material-forms/core").EditEntityDialogResult<TRecord>>;
49
+ readonly closeRequest: import("@angular/core").EventEmitter<import("@eherve/angular-material-forms/core").EditEntityDialogResult<TRecord>>;
50
+ ngOnInit(): void;
51
+ ngOnChanges(changes: SimpleChanges): void;
52
+ ngOnDestroy(): void;
53
+ requestClose(): Promise<void>;
54
+ refreshPresentation(): void;
55
+ submitFromKeyboard(): boolean;
56
+ readonly wizardIndex: import("@angular/core").WritableSignal<number>;
57
+ readonly wizardDrafts: import("@angular/core").WritableSignal<boolean[]>;
58
+ readonly wizardCompleted: import("@angular/core").WritableSignal<boolean[]>;
59
+ readonly wizardVisibleIndices: import("@angular/core").WritableSignal<number[]>;
60
+ readonly wizardVisualIndex: import("@angular/core").Signal<number>;
61
+ readonly wizardNavigationBlocked: import("@angular/core").WritableSignal<boolean>;
62
+ readonly wizardLinear: import("@angular/core").WritableSignal<boolean>;
63
+ wizardNavigationControls: FormControl<boolean>[];
64
+ wizardStepper?: CdkStepper;
65
+ private readonly wizardState;
66
+ get wizardStep(): CreateEntityWizardStep<TRecord> | undefined;
67
+ get wizardLast(): boolean;
68
+ get wizardPrimaryLabel(): string;
69
+ get wizardExitLabel(): string;
70
+ selectWizardVisualStep(position: number): void;
71
+ private advanceWizard;
72
+ private completeWizardStep;
73
+ private refreshWizardNavigation;
74
+ get wizardValid(): boolean;
75
+ get wizardPending(): boolean;
76
+ get wizardUnsaved(): boolean;
77
+ selectWizardStep(index: number): void;
78
+ skipWizardStep(): void;
79
+ wizardNext(): Promise<void>;
80
+ private wizardPaths;
81
+ private wizardAlwaysPaths;
82
+ private wizardValue;
83
+ private patchWizardRecord;
84
+ private hydrateWizard;
85
+ private refreshWizardState;
86
+ static ɵfac: i0.ɵɵFactoryDeclaration<EntityWizardComponent<any>, never>;
87
+ static ɵcmp: i0.ɵɵComponentDeclaration<EntityWizardComponent<any>, "ame-entity-wizard", never, {
88
+ "options": {
89
+ "alias": "options";
90
+ "required": true;
91
+ };
92
+ "displayMode": {
93
+ "alias": "displayMode";
94
+ "required": false;
95
+ };
96
+ }, {
97
+ "result": "result";
98
+ "closeRequest": "closeRequest";
99
+ }, never, never, true, never>;
100
+ }
101
+ export declare class EntityWizardDialog<TRecord extends EditEntityRecord = EditEntityRecord> extends EntityDialogKeyboardHost<TRecord, EntityWizardDialog<TRecord>> {
102
+ readonly data: CreateEntityWizardComponentOptions<TRecord>;
103
+ protected editEntityComponent?: EntityWizardComponent<TRecord>;
104
+ constructor(data: CreateEntityWizardComponentOptions<TRecord>, dialogRef: MatDialogRef<EntityWizardDialog<TRecord>, EditEntityDialogResult<TRecord>>, matDialog: MatDialog);
105
+ static ɵfac: i0.ɵɵFactoryDeclaration<EntityWizardDialog<any>, never>;
106
+ static ɵcmp: i0.ɵɵComponentDeclaration<EntityWizardDialog<any>, "ame-entity-wizard-dialog", never, {}, {}, never, never, true, never>;
107
+ }
108
+ export type CreateEntityWizardOpenConfig<TRecord extends EditEntityRecord> = EntityDialogOpenConfig<TRecord>;
109
+ export declare class CreateEntityWizardService {
110
+ private readonly launcher;
111
+ open<TRecord extends EditEntityRecord>(options: CreateEntityWizardComponentOptions<TRecord>, config?: CreateEntityWizardOpenConfig<TRecord>): MatDialogRef<EntityWizardDialog<TRecord>, EditEntityDialogResult<TRecord>>;
112
+ static ɵfac: i0.ɵɵFactoryDeclaration<CreateEntityWizardService, never>;
113
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
114
+ }