@mintplayer/ng-spark 22.18.0 → 22.19.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.
@@ -134,7 +134,7 @@ class SparkRetryActionModalComponent {
134
134
  </div>
135
135
  </div>
136
136
  </bs-modal>
137
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: BsModalHostComponent, selector: "bs-modal", inputs: ["isOpen", "closeOnEscape", "scrollable"], outputs: ["isOpenChange"] }, { kind: "directive", type: BsModalDirective, selector: "[bsModal]" }, { kind: "directive", type: BsModalHeaderDirective, selector: "[bsModalHeader]" }, { kind: "directive", type: BsModalBodyDirective, selector: "[bsModalBody]" }, { kind: "directive", type: BsModalFooterDirective, selector: "[bsModalFooter]" }, { kind: "directive", type: BsButtonTypeDirective, selector: "button[color],input[type=\"button\"][color],input[type=\"submit\"][color],a[color]", inputs: ["color"] }, { kind: "component", type: SparkPoFormComponent, selector: "spark-po-form", inputs: ["entityType", "formData", "validationErrors", "showButtons", "isSaving", "parentId", "parentType", "objectTypeId", "objectId", "refreshOverlay"], outputs: ["formDataChange", "save", "cancel", "refreshOverlayChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
137
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: BsModalHostComponent, selector: "bs-modal", inputs: ["isOpen", "closeOnEscape", "scrollable"], outputs: ["isOpenChange"] }, { kind: "directive", type: BsModalDirective, selector: "[bsModal]" }, { kind: "directive", type: BsModalHeaderDirective, selector: "[bsModalHeader]" }, { kind: "directive", type: BsModalBodyDirective, selector: "[bsModalBody]" }, { kind: "directive", type: BsModalFooterDirective, selector: "[bsModalFooter]" }, { kind: "directive", type: BsButtonTypeDirective, selector: "button[color],input[type=\"button\"][color],input[type=\"submit\"][color],a[color]", inputs: ["color"] }, { kind: "component", type: SparkPoFormComponent, selector: "spark-po-form", inputs: ["entityType", "formData", "validationErrors", "showButtons", "isSaving", "parentId", "parentType", "objectTypeId", "objectId", "triggerPathPrefix", "refreshOverlay"], outputs: ["formDataChange", "save", "cancel", "nestedTriggerRequested", "nestedTriggerBlurred", "refreshOverlayChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
138
138
  }
139
139
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SparkRetryActionModalComponent, decorators: [{
140
140
  type: Component,
@@ -1 +1 @@
1
- {"version":3,"file":"mintplayer-ng-spark-retry-action-modal.mjs","sources":["../../retry-action-modal/src/spark-retry-action-modal.component.ts","../../retry-action-modal/mintplayer-ng-spark-retry-action-modal.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, effect, inject, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective } from '@mintplayer/ng-bootstrap/modal';\nimport { BsButtonTypeDirective } from '@mintplayer/ng-bootstrap/button-type';\nimport { RetryActionService, SparkService } from '@mintplayer/ng-spark/services';\nimport { SparkPoFormComponent } from '@mintplayer/ng-spark/po-form';\nimport {\n dictToNestedPo,\n EntityAttributeDefinition,\n EntityType,\n EntityTypeResolver,\n nestedPoToDict,\n PersistentObject,\n PersistentObjectAttribute,\n} from '@mintplayer/ng-spark/models';\n\n/**\n * Renders a retry-action popup. Before PRD §3 this component rendered title / message /\n * option buttons only and silently forwarded the incoming <c>persistentObject</c> back to\n * the server on submit — meaning any <c>Retry.Action(..., persistentObject)</c> flow had\n * no UI to actually edit the PO. This component now embeds the shared PO form so every\n * scalar / Reference / AsDetail attribute on the scaffolded Virtual PO is a real form\n * field, and the values the user fills in flow back to the server via\n * <c>RetryResult.PersistentObject</c>.\n */\n@Component({\n selector: 'spark-retry-action-modal',\n imports: [CommonModule, BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective, BsButtonTypeDirective, SparkPoFormComponent],\n template: `\n <bs-modal [isOpen]=\"isOpen()\" (isOpenChange)=\"!$event && onOption('Cancel')\">\n <div *bsModal>\n <div bsModalHeader>\n <h5 class=\"modal-title\">{{ retryActionService.payload()?.title }}</h5>\n </div>\n <div bsModalBody>\n @if (retryActionService.payload()?.message; as message) {\n <p>{{ message }}</p>\n }\n @if (entityType(); as et) {\n <spark-po-form\n [entityType]=\"et\"\n [(formData)]=\"formData\"\n [showButtons]=\"false\">\n </spark-po-form>\n }\n </div>\n <div bsModalFooter>\n @for (option of retryActionService.payload()?.options; track option) {\n <button\n type=\"button\"\n [color]=\"option === 'Cancel' ? colors.secondary : colors.primary\"\n (click)=\"onOption(option)\">\n {{ option }}\n </button>\n }\n </div>\n </div>\n </bs-modal>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkRetryActionModalComponent {\n protected readonly retryActionService = inject(RetryActionService);\n private readonly sparkService = inject(SparkService);\n\n colors = Color;\n isOpen = computed(() => this.retryActionService.payload() !== null);\n\n /**\n * EntityType definition for the incoming PO — fetched lazily via SparkService so the\n * form knows which attributes are editable, their labels, rules, renderers, etc.\n * `null` when the payload has no persistentObject or its objectTypeId doesn't match\n * any registered entity type (renders the modal as a simple option picker).\n */\n entityType = signal<EntityType | null>(null);\n formData = signal<Record<string, any>>({});\n private allEntityTypes: EntityType[] = [];\n\n constructor() {\n // Reseed form state every time the retry service opens/closes the modal. Effect\n // cleanup isn't needed since `payload` is a signal and the component's own lifetime\n // is root-scoped.\n effect(() => {\n const payload = this.retryActionService.payload();\n if (!payload?.persistentObject) {\n this.entityType.set(null);\n this.formData.set({});\n return;\n }\n void this.seedForm(payload.persistentObject);\n });\n }\n\n private async seedForm(po: PersistentObject): Promise<void> {\n // Virtual POs used for retry prompts (e.g. ConfirmDeleteCar) typically have no\n // security.json grant — so `getEntityTypes()` filters them out for the current user\n // and the lookup-by-id would return null, leaving the form blank. The scaffolded PO\n // already carries full attribute metadata (label / dataType / rules / renderer / etc.),\n // so we synthesize an EntityType from the attributes directly and skip the HTTP\n // lookup altogether. `getEntityTypes()` is still fetched because the embedded\n // spark-po-form needs the full list to resolve nested AsDetail / Reference types the\n // retry PO might point at. The guard below is a COMPONENT-local cache and is the only\n // one there is: the service does not cache, so every caller that skips such a guard\n // issues another request.\n if (this.allEntityTypes.length === 0) {\n try { this.allEntityTypes = await this.sparkService.getEntityTypes(); }\n catch { this.allEntityTypes = []; }\n }\n this.entityType.set(entityTypeFromPo(po));\n // Flatten the nested PO into the Record<string, any> shape the shared form uses\n // throughout the rest of ng-spark — same transformation po-edit applies.\n this.formData.set(nestedPoToDict(po));\n }\n\n onOption(option: string): void {\n const payload = this.retryActionService.payload();\n if (!payload) return;\n\n const populated = this.populatedPersistentObject(payload.persistentObject);\n this.retryActionService.respond({\n step: payload.step,\n option,\n persistentObject: populated,\n });\n }\n\n /**\n * Builds the PO the server sees under <c>Retry.Result.PersistentObject</c>. If the\n * form resolved an EntityType, rebuild from the schema + formData (identical to the\n * po-edit save path — AsDetail recursion included). Otherwise forward the incoming\n * PO unmodified so pre-§3 flows without editable attributes keep working.\n */\n private populatedPersistentObject(incoming: PersistentObject | undefined): PersistentObject | undefined {\n if (!incoming) return undefined;\n const type = this.entityType();\n if (!type) return incoming;\n\n const resolver: EntityTypeResolver = (clrName) => this.allEntityTypes.find(t => t.clrType === clrName);\n const rebuilt = dictToNestedPo(this.formData(), type, resolver);\n const populated: PersistentObject = {\n ...incoming,\n attributes: mergeAttributeMetadata(incoming.attributes ?? [], rebuilt.attributes),\n };\n return populated;\n }\n}\n\n/**\n * Builds a synthetic <see cref=\"EntityType\"/> from the PO's own scaffolded attributes so\n * the embedded spark-po-form can render without having to locate the matching server-side\n * EntityType registration. Used for Virtual POs that are schema-registered but not\n * security-granted (retry-action popups).\n */\nfunction entityTypeFromPo(po: PersistentObject): EntityType {\n return {\n id: po.objectTypeId,\n name: po.name,\n clrType: '', // Not needed by the form's rendering path; the PO's attributes carry the schema.\n breadcrumb: undefined,\n tabs: [],\n groups: [],\n attributes: (po.attributes ?? []).map(attrToDefinition),\n queries: [],\n };\n}\n\nfunction attrToDefinition(attr: PersistentObjectAttribute): EntityAttributeDefinition {\n return {\n id: attr.id ?? '',\n name: attr.name,\n label: attr.label,\n dataType: attr.dataType,\n isArray: attr.isArray,\n isRequired: attr.isRequired,\n isVisible: attr.isVisible,\n isReadOnly: attr.isReadOnly,\n order: attr.order,\n query: attr.query,\n asDetailType: attr.asDetailType,\n showedOn: attr.showedOn,\n rules: attr.rules ?? [],\n group: attr.group,\n renderer: attr.renderer,\n rendererOptions: attr.rendererOptions,\n };\n}\n\n/**\n * Keeps the server-issued id + metadata on each attribute while overlaying the user's\n * values from the rebuilt PO. Prevents the modal from accidentally dropping server-only\n * fields (e.g. rules, renderer options) that the form didn't need to know about.\n */\nfunction mergeAttributeMetadata(\n incoming: PersistentObjectAttribute[],\n rebuilt: PersistentObjectAttribute[],\n): PersistentObjectAttribute[] {\n const byName = new Map(rebuilt.map(a => [a.name, a]));\n return incoming.map(source => {\n const updated = byName.get(source.name);\n if (!updated) return source;\n return {\n ...source,\n value: updated.value,\n object: updated.object,\n objects: updated.objects,\n asDetailType: updated.asDetailType ?? source.asDetailType,\n isValueChanged: true,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mintplayer-ng-spark-retry-action-modal.mjs","sources":["../../retry-action-modal/src/spark-retry-action-modal.component.ts","../../retry-action-modal/mintplayer-ng-spark-retry-action-modal.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, effect, inject, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective } from '@mintplayer/ng-bootstrap/modal';\nimport { BsButtonTypeDirective } from '@mintplayer/ng-bootstrap/button-type';\nimport { RetryActionService, SparkService } from '@mintplayer/ng-spark/services';\nimport { SparkPoFormComponent } from '@mintplayer/ng-spark/po-form';\nimport {\n dictToNestedPo,\n EntityAttributeDefinition,\n EntityType,\n EntityTypeResolver,\n nestedPoToDict,\n PersistentObject,\n PersistentObjectAttribute,\n} from '@mintplayer/ng-spark/models';\n\n/**\n * Renders a retry-action popup. Before PRD §3 this component rendered title / message /\n * option buttons only and silently forwarded the incoming <c>persistentObject</c> back to\n * the server on submit — meaning any <c>Retry.Action(..., persistentObject)</c> flow had\n * no UI to actually edit the PO. This component now embeds the shared PO form so every\n * scalar / Reference / AsDetail attribute on the scaffolded Virtual PO is a real form\n * field, and the values the user fills in flow back to the server via\n * <c>RetryResult.PersistentObject</c>.\n */\n@Component({\n selector: 'spark-retry-action-modal',\n imports: [CommonModule, BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective, BsButtonTypeDirective, SparkPoFormComponent],\n template: `\n <bs-modal [isOpen]=\"isOpen()\" (isOpenChange)=\"!$event && onOption('Cancel')\">\n <div *bsModal>\n <div bsModalHeader>\n <h5 class=\"modal-title\">{{ retryActionService.payload()?.title }}</h5>\n </div>\n <div bsModalBody>\n @if (retryActionService.payload()?.message; as message) {\n <p>{{ message }}</p>\n }\n @if (entityType(); as et) {\n <spark-po-form\n [entityType]=\"et\"\n [(formData)]=\"formData\"\n [showButtons]=\"false\">\n </spark-po-form>\n }\n </div>\n <div bsModalFooter>\n @for (option of retryActionService.payload()?.options; track option) {\n <button\n type=\"button\"\n [color]=\"option === 'Cancel' ? colors.secondary : colors.primary\"\n (click)=\"onOption(option)\">\n {{ option }}\n </button>\n }\n </div>\n </div>\n </bs-modal>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkRetryActionModalComponent {\n protected readonly retryActionService = inject(RetryActionService);\n private readonly sparkService = inject(SparkService);\n\n colors = Color;\n isOpen = computed(() => this.retryActionService.payload() !== null);\n\n /**\n * EntityType definition for the incoming PO — fetched lazily via SparkService so the\n * form knows which attributes are editable, their labels, rules, renderers, etc.\n * `null` when the payload has no persistentObject or its objectTypeId doesn't match\n * any registered entity type (renders the modal as a simple option picker).\n */\n entityType = signal<EntityType | null>(null);\n formData = signal<Record<string, any>>({});\n private allEntityTypes: EntityType[] = [];\n\n constructor() {\n // Reseed form state every time the retry service opens/closes the modal. Effect\n // cleanup isn't needed since `payload` is a signal and the component's own lifetime\n // is root-scoped.\n effect(() => {\n const payload = this.retryActionService.payload();\n if (!payload?.persistentObject) {\n this.entityType.set(null);\n this.formData.set({});\n return;\n }\n void this.seedForm(payload.persistentObject);\n });\n }\n\n private async seedForm(po: PersistentObject): Promise<void> {\n // Virtual POs used for retry prompts (e.g. ConfirmDeleteCar) typically have no\n // security.json grant — so `getEntityTypes()` filters them out for the current user\n // and the lookup-by-id would return null, leaving the form blank. The scaffolded PO\n // already carries full attribute metadata (label / dataType / rules / renderer / etc.),\n // so we synthesize an EntityType from the attributes directly and skip the HTTP\n // lookup altogether. `getEntityTypes()` is still fetched because the embedded\n // spark-po-form needs the full list to resolve nested AsDetail / Reference types the\n // retry PO might point at. The guard below is a COMPONENT-local cache and is the only\n // one there is: the service does not cache, so every caller that skips such a guard\n // issues another request.\n if (this.allEntityTypes.length === 0) {\n try { this.allEntityTypes = await this.sparkService.getEntityTypes(); }\n catch { this.allEntityTypes = []; }\n }\n this.entityType.set(entityTypeFromPo(po));\n // Flatten the nested PO into the Record<string, any> shape the shared form uses\n // throughout the rest of ng-spark — same transformation po-edit applies.\n this.formData.set(nestedPoToDict(po));\n }\n\n onOption(option: string): void {\n const payload = this.retryActionService.payload();\n if (!payload) return;\n\n const populated = this.populatedPersistentObject(payload.persistentObject);\n this.retryActionService.respond({\n step: payload.step,\n option,\n persistentObject: populated,\n });\n }\n\n /**\n * Builds the PO the server sees under <c>Retry.Result.PersistentObject</c>. If the\n * form resolved an EntityType, rebuild from the schema + formData (identical to the\n * po-edit save path — AsDetail recursion included). Otherwise forward the incoming\n * PO unmodified so pre-§3 flows without editable attributes keep working.\n */\n private populatedPersistentObject(incoming: PersistentObject | undefined): PersistentObject | undefined {\n if (!incoming) return undefined;\n const type = this.entityType();\n if (!type) return incoming;\n\n const resolver: EntityTypeResolver = (clrName) => this.allEntityTypes.find(t => t.clrType === clrName);\n const rebuilt = dictToNestedPo(this.formData(), type, resolver);\n const populated: PersistentObject = {\n ...incoming,\n attributes: mergeAttributeMetadata(incoming.attributes ?? [], rebuilt.attributes),\n };\n return populated;\n }\n}\n\n/**\n * Builds a synthetic <see cref=\"EntityType\"/> from the PO's own scaffolded attributes so\n * the embedded spark-po-form can render without having to locate the matching server-side\n * EntityType registration. Used for Virtual POs that are schema-registered but not\n * security-granted (retry-action popups).\n */\nfunction entityTypeFromPo(po: PersistentObject): EntityType {\n return {\n id: po.objectTypeId,\n name: po.name,\n clrType: '', // Not needed by the form's rendering path; the PO's attributes carry the schema.\n breadcrumb: undefined,\n tabs: [],\n groups: [],\n attributes: (po.attributes ?? []).map(attrToDefinition),\n queries: [],\n };\n}\n\nfunction attrToDefinition(attr: PersistentObjectAttribute): EntityAttributeDefinition {\n return {\n id: attr.id ?? '',\n name: attr.name,\n label: attr.label,\n dataType: attr.dataType,\n isArray: attr.isArray,\n isRequired: attr.isRequired,\n isVisible: attr.isVisible,\n isReadOnly: attr.isReadOnly,\n order: attr.order,\n query: attr.query,\n asDetailType: attr.asDetailType,\n showedOn: attr.showedOn,\n rules: attr.rules ?? [],\n group: attr.group,\n renderer: attr.renderer,\n rendererOptions: attr.rendererOptions,\n };\n}\n\n/**\n * Keeps the server-issued id + metadata on each attribute while overlaying the user's\n * values from the rebuilt PO. Prevents the modal from accidentally dropping server-only\n * fields (e.g. rules, renderer options) that the form didn't need to know about.\n */\nfunction mergeAttributeMetadata(\n incoming: PersistentObjectAttribute[],\n rebuilt: PersistentObjectAttribute[],\n): PersistentObjectAttribute[] {\n const byName = new Map(rebuilt.map(a => [a.name, a]));\n return incoming.map(source => {\n const updated = byName.get(source.name);\n if (!updated) return source;\n return {\n ...source,\n value: updated.value,\n object: updated.object,\n objects: updated.objects,\n asDetailType: updated.asDetailType ?? source.asDetailType,\n isValueChanged: true,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mintplayer/ng-spark",
3
3
  "private": false,
4
- "version": "22.18.0",
4
+ "version": "22.19.0",
5
5
  "description": "Angular component library for MintPlayer.Spark CRUD applications",
6
6
  "repository": {
7
7
  "type": "git",
@@ -28,6 +28,22 @@ declare class SparkPoEditComponent {
28
28
  constructor();
29
29
  private onParamsChange;
30
30
  initFormData(): void;
31
+ /**
32
+ * Resolves a nested AsDetail type by CLR name, for rebuilding the nested PO wire shape on save.
33
+ *
34
+ * ⚠️ <b>`detailTypes` first, catalogue second.</b> The catalogue from `/spark/types` is
35
+ * <b>Query-gated</b>, and an AsDetail row type usually has no rights of its own — nobody grants
36
+ * `Query/GateSettings`, because a gate is edited through the Repository that owns it. So the
37
+ * catalogue does not contain it, the resolver returned undefined, and the save quietly took the
38
+ * scalar branch: the attribute went out as a raw dict under `value` instead of a nested PO under
39
+ * `object`, the server could not map it, and `EntityMapper`'s conversion `catch` swallowed the
40
+ * failure. The save reported success and wrote nothing.
41
+ * <para>
42
+ * `detailTypes` is the parent's own copy of its row types, carried on the type definition for
43
+ * exactly this reason (#385 fixed the rendering half; this is the save half). It is gated on the
44
+ * parent's right, which is the right that governs editing the row anyway.
45
+ * </para>
46
+ */
31
47
  private resolveEntityType;
32
48
  /**
33
49
  * The attributes this page will read values from when it builds the save.
@@ -56,6 +56,17 @@ declare class RefreshCoordinator {
56
56
  trigger(attributeName: string): Promise<void>;
57
57
  }
58
58
 
59
+ /**
60
+ * A refresh a form embedded in another form is asking its host to issue on its behalf.
61
+ *
62
+ * `immediate` mirrors the distinction the host's own editors make: a discrete editor fires on
63
+ * change, free text only marks itself pending and fires on blur.
64
+ */
65
+ interface NestedTriggerRequest {
66
+ /** The full path the server addresses, e.g. `Gate.ProjectMode`. */
67
+ path: string;
68
+ immediate: boolean;
69
+ }
59
70
  declare class SparkPoFormComponent {
60
71
  private readonly sparkService;
61
72
  private readonly translations;
@@ -76,6 +87,18 @@ declare class SparkPoFormComponent {
76
87
  objectTypeId: _angular_core.InputSignal<string | undefined>;
77
88
  /** The id of the object being edited; absent for a create. */
78
89
  objectId: _angular_core.InputSignal<string | undefined>;
90
+ /**
91
+ * Set when this form renders a single embedded AsDetail object inside a host form: the name of
92
+ * the attribute holding it, e.g. `"Gate"`. Presence of a prefix switches the form from issuing
93
+ * its own refreshes to reporting them upward, because an embedded object is not something the
94
+ * server can be asked about on its own — the request must describe, and be authorized against,
95
+ * the object that owns it.
96
+ */
97
+ triggerPathPrefix: _angular_core.InputSignal<string | null>;
98
+ /** Raised instead of self-issuing when {@link triggerPathPrefix} is set. */
99
+ nestedTriggerRequested: _angular_core.OutputEmitterRef<NestedTriggerRequest>;
100
+ /** Blur counterpart, flushing a trigger the host only marked pending. */
101
+ nestedTriggerBlurred: _angular_core.OutputEmitterRef<string>;
79
102
  /**
80
103
  * What the last refresh changed about each attribute's presentation, keyed by attribute name.
81
104
  *
@@ -176,9 +199,8 @@ declare class SparkPoFormComponent {
176
199
  /**
177
200
  * The single funnel every scalar / boolean / inline-cell edit passes through.
178
201
  *
179
- * `attr` is optional only so the AsDetail modal's recursive form, which has no trigger context,
180
- * can still call it. A caller that knows which attribute changed should always say so — without it
181
- * no refresh can fire.
202
+ * `attr` is optional because some editors have nothing useful to say about which attribute moved.
203
+ * A caller that knows should always say so — without it no refresh can fire.
182
204
  */
183
205
  onFieldChange(attr?: EntityAttributeDefinition): void;
184
206
  /**
@@ -187,8 +209,27 @@ declare class SparkPoFormComponent {
187
209
  * addressing scheme being invented for it.
188
210
  */
189
211
  onInlineCellChange(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): void;
190
- /** Which detail row the in-flight refresh belongs to, if any. */
212
+ /**
213
+ * Which embedded target the in-flight refresh belongs to, if any.
214
+ *
215
+ * ⚠️ Tagged, not a nullable `rowIndex`. The two arms write to different places — a row inside
216
+ * `formData`, versus the modal's working copy in `asDetailFormData` — and an index that happens
217
+ * to be absent is not the same statement as "this attribute holds one object". With a nullable
218
+ * index, a row refresh carrying a bad index would fall silently into the object arm.
219
+ */
191
220
  private pendingNestedTrigger;
221
+ /**
222
+ * A trigger inside a single embedded AsDetail object, raised by the modal's recursive form.
223
+ *
224
+ * The child form does not issue the request itself, for two reasons that both bite. Its
225
+ * `entityType` is the embedded type, so a payload built there describes `GateSettings` while the
226
+ * request must describe the Repository that owns it. And nested AsDetail types carry no rights of
227
+ * their own — the server authorizes a nested refresh on the OWNING type — so a child-issued
228
+ * request would be asking the wrong question of security.json.
229
+ */
230
+ onEmbeddedTrigger(attributeName: string, event: NestedTriggerRequest): void;
231
+ /** Blur counterpart of {@link onEmbeddedTrigger}, for the child's free-text editors. */
232
+ onEmbeddedTriggerBlur(path: string): void;
192
233
  /**
193
234
  * Applies a refresh that ran against a detail row: the row's own values, and the column metadata
194
235
  * for the grid it lives in.
@@ -204,6 +245,8 @@ declare class SparkPoFormComponent {
204
245
  /** Blur handler for free-text editors — sends the refresh their keystrokes only marked pending. */
205
246
  onFieldBlur(attr: EntityAttributeDefinition): void;
206
247
  private noteChange;
248
+ /** `Gate.ProjectMode` — the index-free counterpart of the inline grid's `Jobs[1].Kind`. */
249
+ private nestedTriggerPath;
207
250
  private canRefresh;
208
251
  /**
209
252
  * Per-instance, never a service: the retry-action modal renders its own `spark-po-form`, and a
@@ -212,6 +255,16 @@ declare class SparkPoFormComponent {
212
255
  */
213
256
  protected readonly refreshCoordinator: RefreshCoordinator;
214
257
  private buildRefreshPayload;
258
+ /**
259
+ * The values to post, with the open modal's working copy substituted for the attribute it edits.
260
+ *
261
+ * ⚠️ Without this the server receives the embedded object as it was when the modal opened — `{}`
262
+ * for a gate that has never been set — and the hook decides against values the user cannot see it
263
+ * ignoring. `formData` deliberately does not hold the in-progress edit (cancelling the modal has
264
+ * to discard it), so the refresh has to reach into `asDetailFormData` instead. Only the attribute
265
+ * under edit is substituted; everything else is the form's own state.
266
+ */
267
+ private embeddedValues;
215
268
  /**
216
269
  * Folds replaced option lists into the signals the editors already read, so a refreshed dropdown
217
270
  * renders through the same path as a loaded one.
@@ -278,7 +331,7 @@ declare class SparkPoFormComponent {
278
331
  removeArrayItem(attr: EntityAttributeDefinition, index: number): Promise<void>;
279
332
  onAsDetailReorder(attr: EntityAttributeDefinition, event: CdkDragDrop<Record<string, any>[]>): void;
280
333
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SparkPoFormComponent, never>;
281
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkPoFormComponent, "spark-po-form", never, { "entityType": { "alias": "entityType"; "required": false; "isSignal": true; }; "formData": { "alias": "formData"; "required": false; "isSignal": true; }; "validationErrors": { "alias": "validationErrors"; "required": false; "isSignal": true; }; "showButtons": { "alias": "showButtons"; "required": false; "isSignal": true; }; "isSaving": { "alias": "isSaving"; "required": false; "isSignal": true; }; "parentId": { "alias": "parentId"; "required": false; "isSignal": true; }; "parentType": { "alias": "parentType"; "required": false; "isSignal": true; }; "objectTypeId": { "alias": "objectTypeId"; "required": false; "isSignal": true; }; "objectId": { "alias": "objectId"; "required": false; "isSignal": true; }; "refreshOverlay": { "alias": "refreshOverlay"; "required": false; "isSignal": true; }; }, { "formData": "formDataChange"; "save": "save"; "cancel": "cancel"; "refreshOverlay": "refreshOverlayChange"; }, never, never, true, never>;
334
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SparkPoFormComponent, "spark-po-form", never, { "entityType": { "alias": "entityType"; "required": false; "isSignal": true; }; "formData": { "alias": "formData"; "required": false; "isSignal": true; }; "validationErrors": { "alias": "validationErrors"; "required": false; "isSignal": true; }; "showButtons": { "alias": "showButtons"; "required": false; "isSignal": true; }; "isSaving": { "alias": "isSaving"; "required": false; "isSignal": true; }; "parentId": { "alias": "parentId"; "required": false; "isSignal": true; }; "parentType": { "alias": "parentType"; "required": false; "isSignal": true; }; "objectTypeId": { "alias": "objectTypeId"; "required": false; "isSignal": true; }; "objectId": { "alias": "objectId"; "required": false; "isSignal": true; }; "triggerPathPrefix": { "alias": "triggerPathPrefix"; "required": false; "isSignal": true; }; "refreshOverlay": { "alias": "refreshOverlay"; "required": false; "isSignal": true; }; }, { "formData": "formDataChange"; "save": "save"; "cancel": "cancel"; "nestedTriggerRequested": "nestedTriggerRequested"; "nestedTriggerBlurred": "nestedTriggerBlurred"; "refreshOverlay": "refreshOverlayChange"; }, never, never, true, never>;
282
335
  }
283
336
 
284
337
  /**
@@ -357,3 +410,4 @@ declare class SparkLookupPickerComponent {
357
410
  }
358
411
 
359
412
  export { SparkLookupPickerComponent, SparkPoFormComponent, SparkReferencePickerComponent };
413
+ export type { NestedTriggerRequest };