@praxisui/crud 9.0.4-rc.27 → 9.0.4-rc.28

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
@@ -202,6 +202,19 @@ to” and keeps it distinct from the current child record. Both identities stay
202
202
  outside dynamic-form inputs and submit payloads; capabilities and HATEOAS remain
203
203
  the authority for whether the operation may execute.
204
204
 
205
+ When a CRUD component is already materialized inside a runtime surface drawer,
206
+ drawer actions reuse that same session. The launcher calls the active
207
+ `surfaceRuntime.push(...)`, preserves the live parent frame and returns through
208
+ the shell Back action instead of stacking a second overlay. The dynamic form
209
+ registers its dirty-state confirmation through the active frame guard, so Back,
210
+ Escape, backdrop and shell close share one discard policy. This is runtime-only:
211
+ the navigation stack, component references and guards must never be serialized
212
+ into CRUD metadata or `x-ui`.
213
+
214
+ `SURFACE_DRAWER_REF` and `SURFACE_DRAWER_CONTENT_DATA` are the canonical DI
215
+ boundary for components hosted in a runtime frame. Hosts should not manufacture
216
+ an equivalent local token or pass the drawer reference through form values.
217
+
205
218
  Route mode has a different lifecycle because `@praxisui/crud` does not own the
206
219
  destination component. Routed edit hosts must load the entity through the
207
220
  canonical read contract and materialize identity from its response schema. Do
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-08-04T23:26:34.977Z",
3
+ "generatedAt": "2026-08-05T00:44:25.177Z",
4
4
  "packageName": "@praxisui/crud",
5
- "packageVersion": "9.0.4-rc.27",
5
+ "packageVersion": "9.0.4-rc.28",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 2,
@@ -1,12 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, InjectionToken, inject, input, signal, computed, effect, ChangeDetectionStrategy, Component, EventEmitter, DestroyRef, ChangeDetectorRef, Injector, ViewChild, Output, Input, Inject, ViewEncapsulation, ENVIRONMENT_INITIALIZER } from '@angular/core';
2
+ import { Injectable, InjectionToken, inject, input, signal, computed, effect, ChangeDetectionStrategy, Component, EventEmitter, DestroyRef, ChangeDetectorRef, Injector, ViewChild, Output, Input, Optional, Inject, ViewEncapsulation, ENVIRONMENT_INITIALIZER } from '@angular/core';
3
3
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
4
  import { HttpClient } from '@angular/common/http';
5
5
  import { Router, ActivatedRoute, RouterLink } from '@angular/router';
6
6
  import { MatSnackBar } from '@angular/material/snack-bar';
7
7
  import { firstValueFrom, BehaviorSubject, Subscription } from 'rxjs';
8
8
  import * as i2$1 from '@praxisui/core';
9
- import { ASYNC_CONFIG_STORAGE, GlobalConfigService, CrudOperationResolutionService, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, EnterpriseRuntimeContextService, GLOBAL_SURFACE_SERVICE, SurfaceOutletRegistryService, ComponentKeyService, ResourceDiscoveryService, ResourceActionOpenAdapterService, ResourceSurfaceOpenAdapterService, normalizeSurfaceOperationContext, translateUnavailableWorkflowMessage, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, PraxisIconButtonComponent, PraxisResourceIdentityComponent, GenericCrudService, ComponentMetadataRegistry } from '@praxisui/core';
9
+ import { ASYNC_CONFIG_STORAGE, GlobalConfigService, CrudOperationResolutionService, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, EnterpriseRuntimeContextService, GLOBAL_SURFACE_SERVICE, SurfaceOutletRegistryService, ComponentKeyService, ResourceDiscoveryService, ResourceActionOpenAdapterService, ResourceSurfaceOpenAdapterService, normalizeSurfaceOperationContext, translateUnavailableWorkflowMessage, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, SURFACE_DRAWER_REF, SURFACE_DRAWER_CONTENT_DATA, PraxisIconButtonComponent, PraxisResourceIdentityComponent, GenericCrudService, ComponentMetadataRegistry } from '@praxisui/core';
10
10
  import { SettingsPanelService } from '@praxisui/settings-panel';
11
11
  import { PraxisTableInlineAuthoringEditorComponent, PraxisTable } from '@praxisui/table';
12
12
  import { ConfirmDialogComponent } from '@praxisui/dynamic-fields';
@@ -104,6 +104,7 @@ class CrudLauncherService {
104
104
  global = inject(GlobalConfigService);
105
105
  operationResolver = inject(CrudOperationResolutionService);
106
106
  dialogHostPromise;
107
+ nextSurfaceFrameId = 0;
107
108
  drawerAdapter = (() => {
108
109
  try {
109
110
  return inject(CRUD_DRAWER_ADAPTER);
@@ -145,6 +146,31 @@ class CrudLauncherService {
145
146
  ? runtime?.resourceIdentity ?? null
146
147
  : null;
147
148
  const contextIdentity = runtime?.contextIdentity ?? null;
149
+ const dialogData = {
150
+ action: actionForLaunch,
151
+ row,
152
+ metadata: merged.metadata,
153
+ inputs,
154
+ presentation: mode === 'drawer' ? 'drawer' : 'modal',
155
+ resourceIdentity,
156
+ contextIdentity,
157
+ };
158
+ if (mode === 'drawer' && runtime?.surfaceRuntime?.push) {
159
+ const frameRef = await runtime.surfaceRuntime.push({
160
+ id: this.buildSurfaceFrameId(actionForLaunch),
161
+ title: this.resolveSurfaceFrameTitle(actionForLaunch, merged.metadata),
162
+ titleIcon: stringOrUndefined$1(actionForLaunch['icon']),
163
+ subtitle: this.resolveSurfaceFrameSubtitle(actionForLaunch, merged.metadata),
164
+ content: {
165
+ component: await (prefetchedDialogHost ?? this.loadDialogHost()),
166
+ inputs: { data: dialogData },
167
+ },
168
+ });
169
+ if (frameRef) {
170
+ this.bindSurfaceFrameLifecycle(frameRef, drawerCallbacks);
171
+ return { mode };
172
+ }
173
+ }
148
174
  if (mode === 'drawer' && this.drawerAdapter) {
149
175
  await Promise.resolve(this.drawerAdapter.open({
150
176
  action: actionForLaunch,
@@ -207,14 +233,9 @@ class CrudLauncherService {
207
233
  position: dialogPosition,
208
234
  ariaLabelledBy: 'crudDialogTitle',
209
235
  data: {
210
- action: actionForLaunch,
211
- row,
212
- metadata: merged.metadata,
213
- inputs,
236
+ ...dialogData,
214
237
  presentation: drawerMode ? 'drawer' : 'modal',
215
238
  dialogPosition,
216
- resourceIdentity,
217
- contextIdentity,
218
239
  },
219
240
  });
220
241
  if (drawerMode) {
@@ -484,6 +505,46 @@ class CrudLauncherService {
484
505
  schemaType: 'request',
485
506
  };
486
507
  }
508
+ buildSurfaceFrameId(action) {
509
+ this.nextSurfaceFrameId += 1;
510
+ return `crud:${action.action}:${action.formId || 'form'}:${this.nextSurfaceFrameId}`;
511
+ }
512
+ resolveSurfaceFrameTitle(action, metadata) {
513
+ return stringOrUndefined$1(action['label'])
514
+ ?? stringOrUndefined$1(metadata.form?.['title'])
515
+ ?? stringOrUndefined$1(metadata.table?.['title'])
516
+ ?? (action.action === 'create'
517
+ ? 'Adicionar registro'
518
+ : action.action === 'view'
519
+ ? 'Consultar registro'
520
+ : 'Editar registro');
521
+ }
522
+ resolveSurfaceFrameSubtitle(action, metadata) {
523
+ return stringOrUndefined$1(action['description'])
524
+ ?? stringOrUndefined$1(action['tooltip'])
525
+ ?? stringOrUndefined$1(metadata.form?.['description'])
526
+ ?? stringOrUndefined$1(metadata.table?.['subtitle']);
527
+ }
528
+ bindSurfaceFrameLifecycle(frameRef, callbacks) {
529
+ let semanticResultHandled = false;
530
+ const handleResult = (value) => {
531
+ if (semanticResultHandled)
532
+ return;
533
+ const result = toCrudDrawerResult(value);
534
+ if (!result || result.type === 'close')
535
+ return;
536
+ semanticResultHandled = true;
537
+ callbacks?.onResult?.(result);
538
+ };
539
+ frameRef.result$?.pipe(take(1)).subscribe(handleResult);
540
+ frameRef.closed$.pipe(take(1)).subscribe((value) => {
541
+ handleResult(value);
542
+ callbacks?.onClose?.();
543
+ if (!semanticResultHandled) {
544
+ callbacks?.onResult?.({ type: 'close' });
545
+ }
546
+ });
547
+ }
487
548
  async mergeCrudOverrides(metadata, action, componentKeyId) {
488
549
  try {
489
550
  if (!componentKeyId)
@@ -565,6 +626,10 @@ function toCrudDrawerResult(value) {
565
626
  ? result
566
627
  : undefined;
567
628
  }
629
+ function stringOrUndefined$1(value) {
630
+ const text = String(value ?? '').trim();
631
+ return text || undefined;
632
+ }
568
633
 
569
634
  const DOCUMENT_KIND = 'praxis.crud.editor';
570
635
  const DOCUMENT_VERSION = 1;
@@ -3922,6 +3987,7 @@ class PraxisCrudComponent {
3922
3987
  links: this.resolveCrudRuntimeLinks(effectiveAction.action, contextualRow),
3923
3988
  resourceIdentity,
3924
3989
  contextIdentity,
3990
+ surfaceRuntime: this.resolveSurfaceRuntime(),
3925
3991
  });
3926
3992
  this.afterOpen.emit({ mode, action: effectiveAction.action });
3927
3993
  if (mode === 'drawer') {
@@ -4188,6 +4254,7 @@ class PraxisCrudComponent {
4188
4254
  }, {
4189
4255
  capabilities: this.collectionCapabilities,
4190
4256
  links: this.tableCollectionLinks,
4257
+ surfaceRuntime: this.resolveSurfaceRuntime(),
4191
4258
  });
4192
4259
  this.afterOpen.emit({ mode, action: normalizedAction });
4193
4260
  if (mode !== 'drawer' && ref) {
@@ -4207,6 +4274,14 @@ class PraxisCrudComponent {
4207
4274
  }
4208
4275
  this.table.refetch();
4209
4276
  }
4277
+ resolveSurfaceRuntime() {
4278
+ const candidate = this.context?.['surfaceRuntime'];
4279
+ if (!candidate || typeof candidate !== 'object') {
4280
+ return null;
4281
+ }
4282
+ const runtime = candidate;
4283
+ return typeof runtime.push === 'function' ? runtime : null;
4284
+ }
4210
4285
  tryRefreshMaterializedLocalData() {
4211
4286
  const readUrl = this.resolveMaterializedReadUrl();
4212
4287
  if (!readUrl || this.resolveResourcePath(this.resolvedMetadata)) {
@@ -5408,10 +5483,10 @@ function debugCrudHost(message, data) {
5408
5483
  }
5409
5484
  class DynamicFormDialogHostComponent {
5410
5485
  dialogRef;
5411
- data;
5412
5486
  dialogService;
5413
5487
  crud;
5414
5488
  configStorage;
5489
+ surfaceRef;
5415
5490
  formComp;
5416
5491
  modal = {};
5417
5492
  presentation = 'modal';
@@ -5421,6 +5496,7 @@ class DynamicFormDialogHostComponent {
5421
5496
  rememberState = false;
5422
5497
  stateKey;
5423
5498
  backDefaults = {};
5499
+ terminalClose = false;
5424
5500
  destroyRef = inject(DestroyRef);
5425
5501
  i18n = inject(PraxisI18nService);
5426
5502
  resourcePath;
@@ -5440,6 +5516,8 @@ class DynamicFormDialogHostComponent {
5440
5516
  mode = 'create';
5441
5517
  backConfig;
5442
5518
  idField = 'id';
5519
+ isSurfaceFrame;
5520
+ data;
5443
5521
  texts = {
5444
5522
  title: 'Formulário',
5445
5523
  close: 'Fechar',
@@ -5454,14 +5532,25 @@ class DynamicFormDialogHostComponent {
5454
5532
  discardConfirm: 'Descartar',
5455
5533
  discardCancel: 'Cancelar',
5456
5534
  };
5457
- constructor(dialogRef, data, dialogService, crud, configStorage) {
5535
+ constructor(dialogRef, dialogData, dialogService, crud, configStorage, surfaceRef = null, surfaceContentData = null) {
5458
5536
  this.dialogRef = dialogRef;
5459
- this.data = data;
5460
5537
  this.dialogService = dialogService;
5461
5538
  this.crud = crud;
5462
5539
  this.configStorage = configStorage;
5463
- this.dialogRef.disableClose = true;
5464
- this.presentation = this.data.presentation === 'drawer' ? 'drawer' : 'modal';
5540
+ this.surfaceRef = surfaceRef;
5541
+ this.data = dialogData ?? surfaceContentData?.['data'] ?? {};
5542
+ this.isSurfaceFrame = !!this.surfaceRef;
5543
+ if (!this.dialogRef && !this.surfaceRef) {
5544
+ throw new Error('DynamicFormDialogHostComponent requires a MatDialogRef or an active SurfaceDrawerRef.');
5545
+ }
5546
+ if (this.dialogRef) {
5547
+ this.dialogRef.disableClose = true;
5548
+ }
5549
+ this.presentation = this.isSurfaceFrame
5550
+ ? 'surface-frame'
5551
+ : this.data.presentation === 'drawer'
5552
+ ? 'drawer'
5553
+ : 'modal';
5465
5554
  // i18n
5466
5555
  this.texts = {
5467
5556
  ...this.texts,
@@ -5521,14 +5610,14 @@ class DynamicFormDialogHostComponent {
5521
5610
  backConfig: this.backConfig,
5522
5611
  });
5523
5612
  // Esc
5524
- if (!this.modal.disableCloseOnEsc) {
5613
+ if (this.dialogRef && !this.modal.disableCloseOnEsc) {
5525
5614
  this.dialogRef
5526
5615
  .keydownEvents()
5527
5616
  .pipe(filter((e) => e.key === 'Escape'), takeUntilDestroyed(this.destroyRef))
5528
5617
  .subscribe(() => this.onCancel());
5529
5618
  }
5530
5619
  // Backdrop
5531
- if (!this.modal.disableCloseOnBackdrop) {
5620
+ if (this.dialogRef && !this.modal.disableCloseOnBackdrop) {
5532
5621
  this.dialogRef
5533
5622
  .backdropClick()
5534
5623
  .pipe(takeUntilDestroyed(this.destroyRef))
@@ -5536,7 +5625,7 @@ class DynamicFormDialogHostComponent {
5536
5625
  }
5537
5626
  // Salvar estado ao fechar, se aplicável
5538
5627
  this.dialogRef
5539
- .afterClosed()
5628
+ ?.afterClosed()
5540
5629
  .pipe(takeUntilDestroyed(this.destroyRef))
5541
5630
  .subscribe(() => this.saveState());
5542
5631
  }
@@ -5720,6 +5809,13 @@ class DynamicFormDialogHostComponent {
5720
5809
  });
5721
5810
  }
5722
5811
  }
5812
+ ngAfterViewInit() {
5813
+ if (!this.surfaceRef?.setCanLeave) {
5814
+ return;
5815
+ }
5816
+ this.surfaceRef.updateTitle?.(this.dialogTitle);
5817
+ this.surfaceRef.setCanLeave(() => this.canLeaveSurfaceFrame());
5818
+ }
5723
5819
  onSave(result) {
5724
5820
  const stage = getFormSubmitStage(result);
5725
5821
  if (stage === 'before' || stage === 'error') {
@@ -5729,9 +5825,21 @@ class DynamicFormDialogHostComponent {
5729
5825
  this.saveState();
5730
5826
  return;
5731
5827
  }
5732
- this.dialogRef.close({ type: 'save', data: result });
5828
+ this.terminalClose = true;
5829
+ this.formComp?.form.markAsPristine();
5830
+ if (this.surfaceRef) {
5831
+ this.surfaceRef.emitResult?.({ type: 'save', data: result });
5832
+ this.surfaceRef.close?.({ type: 'save', data: result });
5833
+ }
5834
+ else {
5835
+ this.dialogRef?.close({ type: 'save', data: result });
5836
+ }
5733
5837
  }
5734
5838
  onCancel() {
5839
+ if (this.surfaceRef) {
5840
+ this.surfaceRef.close?.({ type: 'close' });
5841
+ return;
5842
+ }
5735
5843
  const dirty = this.formComp?.form.dirty;
5736
5844
  const backCfg = (this.data.action?.back || this.data.metadata?.defaults?.back) || {};
5737
5845
  const confirm = backCfg.confirmOnDirty ?? true;
@@ -5749,12 +5857,32 @@ class DynamicFormDialogHostComponent {
5749
5857
  ref
5750
5858
  .afterClosed()
5751
5859
  .pipe(filter((confirmed) => !!confirmed), takeUntilDestroyed(this.destroyRef))
5752
- .subscribe(() => this.dialogRef.close({ type: 'close' }));
5860
+ .subscribe(() => this.dialogRef?.close({ type: 'close' }));
5753
5861
  }
5754
5862
  else {
5755
- this.dialogRef.close({ type: 'close' });
5863
+ this.dialogRef?.close({ type: 'close' });
5756
5864
  }
5757
5865
  }
5866
+ async canLeaveSurfaceFrame() {
5867
+ if (this.terminalClose || !this.formComp?.form.dirty) {
5868
+ return true;
5869
+ }
5870
+ const backCfg = (this.data.action?.back || this.data.metadata?.defaults?.back) || {};
5871
+ if (backCfg.confirmOnDirty === false) {
5872
+ return true;
5873
+ }
5874
+ const ref = this.dialogService.open(ConfirmDialogComponent, {
5875
+ data: {
5876
+ title: this.texts.discardTitle,
5877
+ message: this.texts.discardMessage,
5878
+ confirmText: this.texts.discardConfirm,
5879
+ cancelText: this.texts.discardCancel,
5880
+ type: 'warning',
5881
+ },
5882
+ autoFocus: false,
5883
+ });
5884
+ return !!(await firstValueFrom(ref.afterClosed().pipe(take(1))));
5885
+ }
5758
5886
  toggleMaximize(initial = false) {
5759
5887
  const pane = this.resolveOverlayPane();
5760
5888
  if (!initial && !this.maximized && pane) {
@@ -5768,7 +5896,12 @@ class DynamicFormDialogHostComponent {
5768
5896
  const height = this.maximized
5769
5897
  ? `calc(100dvh - ${2 * (gap ?? 0)}px)`
5770
5898
  : this.initialSize.height;
5771
- this.dialogRef.updateSize(width, height);
5899
+ if (this.surfaceRef) {
5900
+ this.surfaceRef.updateSize?.(this.maximized ? 'full' : 'default');
5901
+ }
5902
+ else {
5903
+ this.dialogRef?.updateSize(width, height);
5904
+ }
5772
5905
  this.updateHostPosition();
5773
5906
  if (pane && pane.classList.contains('pfx-dialog-pane')) {
5774
5907
  if (this.presentation === 'drawer') {
@@ -5825,6 +5958,9 @@ class DynamicFormDialogHostComponent {
5825
5958
  startMaximized: this.modal.startMaximized,
5826
5959
  fullscreenBreakpoint: this.modal.fullscreenBreakpoint,
5827
5960
  });
5961
+ if (this.surfaceRef) {
5962
+ return;
5963
+ }
5828
5964
  let shouldMax = false;
5829
5965
  if (saved && typeof saved.maximized === 'boolean') {
5830
5966
  shouldMax = !!saved.maximized;
@@ -5839,16 +5975,19 @@ class DynamicFormDialogHostComponent {
5839
5975
  this.toggleMaximize(true);
5840
5976
  }
5841
5977
  else if (this.initialSize.width || this.initialSize.height) {
5842
- this.dialogRef.updateSize(this.initialSize.width, this.initialSize.height);
5978
+ this.dialogRef?.updateSize(this.initialSize.width, this.initialSize.height);
5843
5979
  this.updateHostPosition();
5844
5980
  }
5845
5981
  }
5846
5982
  updateHostPosition() {
5983
+ if (this.surfaceRef) {
5984
+ return;
5985
+ }
5847
5986
  if (this.presentation !== 'drawer') {
5848
- this.dialogRef.updatePosition();
5987
+ this.dialogRef?.updatePosition();
5849
5988
  return;
5850
5989
  }
5851
- this.dialogRef.updatePosition(this.maximized
5990
+ this.dialogRef?.updatePosition(this.maximized
5852
5991
  ? this.resolveMaximizedDrawerPosition()
5853
5992
  : this.initialPosition);
5854
5993
  }
@@ -5863,11 +6002,12 @@ class DynamicFormDialogHostComponent {
5863
6002
  : { top: offset };
5864
6003
  return { ...horizontal, ...vertical };
5865
6004
  }
5866
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormDialogHostComponent, deps: [{ token: MatDialogRef }, { token: MAT_DIALOG_DATA }, { token: DialogService }, { token: i2$1.GenericCrudService }, { token: ASYNC_CONFIG_STORAGE }], target: i0.ɵɵFactoryTarget.Component });
5867
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicFormDialogHostComponent, isStandalone: true, selector: "praxis-dynamic-form-dialog-host", host: { properties: { "attr.data-density": "modal.density || \"default\"", "attr.data-presentation": "presentation", "class.praxis-drawer": "presentation === \"drawer\"" }, classAttribute: "praxis-dialog" }, providers: [
6005
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormDialogHostComponent, deps: [{ token: MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }, { token: DialogService }, { token: i2$1.GenericCrudService }, { token: ASYNC_CONFIG_STORAGE }, { token: SURFACE_DRAWER_REF, optional: true }, { token: SURFACE_DRAWER_CONTENT_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component });
6006
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicFormDialogHostComponent, isStandalone: true, selector: "praxis-dynamic-form-dialog-host", host: { properties: { "attr.data-density": "modal.density || \"default\"", "attr.data-presentation": "presentation", "class.praxis-drawer": "presentation === \"drawer\"", "class.praxis-surface-frame": "isSurfaceFrame" }, classAttribute: "praxis-dialog" }, providers: [
5868
6007
  GenericCrudService,
5869
6008
  providePraxisI18nConfig(PRAXIS_CRUD_RUNTIME_I18N_CONFIG),
5870
6009
  ], viewQueries: [{ propertyName: "formComp", first: true, predicate: PraxisDynamicForm, descendants: true }], ngImport: i0, template: `
6010
+ @if (!isSurfaceFrame) {
5871
6011
  <div mat-dialog-title class="dialog-header">
5872
6012
  @if (dialogIcon; as icon) {
5873
6013
  <span class="dialog-title-icon" aria-hidden="true">
@@ -5903,11 +6043,14 @@ class DynamicFormDialogHostComponent {
5903
6043
  cdkFocusInitial
5904
6044
  ></button>
5905
6045
  </div>
6046
+ }
5906
6047
 
5907
6048
  <mat-dialog-content
5908
6049
  class="dialog-content"
5909
- aria-labelledby="crudDialogTitle"
5910
- [attr.aria-describedby]="dialogSubtitle ? 'crudDialogDescription' : null"
6050
+ [attr.aria-labelledby]="isSurfaceFrame ? null : 'crudDialogTitle'"
6051
+ [attr.aria-describedby]="
6052
+ !isSurfaceFrame && dialogSubtitle ? 'crudDialogDescription' : null
6053
+ "
5911
6054
  >
5912
6055
  @if (showContextIdentity || resourceIdentity) {
5913
6056
  <div class="crud-operation-context" data-testid="crud-operation-context">
@@ -5960,7 +6103,7 @@ class DynamicFormDialogHostComponent {
5960
6103
  (formCancel)="onCancel()"
5961
6104
  ></praxis-dynamic-form>
5962
6105
  </mat-dialog-content>
5963
- `, isInline: true, styles: ["praxis-dynamic-form-dialog-host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}praxis-dynamic-form-dialog-host[data-density=compact]{--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}praxis-dynamic-form-dialog-host .dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);min-height:var(--dlg-header-h);padding:10px var(--dlg-pad);margin:0;background:var(--md-sys-color-surface-container-high);border-bottom:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-heading{display:grid;min-width:0;gap:2px}praxis-dynamic-form-dialog-host .dialog-title-icon{display:inline-grid;flex:0 0 auto;width:36px;height:36px;place-items:center;border-radius:10px;color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}praxis-dynamic-form-dialog-host .dialog-title-icon mat-icon{width:22px;height:22px;font-size:22px}praxis-dynamic-form-dialog-host .dialog-subtitle{display:-webkit-box;overflow:hidden;color:var(--md-sys-color-on-surface-variant);font-size:.82rem;font-weight:400;line-height:1.35;-webkit-box-orient:vertical;-webkit-line-clamp:2}praxis-dynamic-form-dialog-host .spacer{flex:1}praxis-dynamic-form-dialog-host .dialog-content{flex:1 1 auto;overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}praxis-dynamic-form-dialog-host .crud-operation-context{position:sticky;top:0;z-index:1;display:grid;gap:.75rem;margin-block-end:var(--dlg-pad);padding-block:.5rem .75rem;background:var(--pfx-form-surface, var(--md-sys-color-surface));border-bottom:1px solid var(--md-sys-color-outline-variant)}praxis-dynamic-form-dialog-host .crud-resource-identity{display:grid;gap:.25rem;min-width:0}praxis-dynamic-form-dialog-host .crud-resource-identity__label{color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-label-medium, 500 .75rem/1rem inherit)}praxis-dynamic-form-dialog-host.praxis-drawer{--dlg-header-h: 72px;width:100%;min-width:0;max-width:100vw;height:100dvh;background:var(--pfx-form-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-header{background:color-mix(in srgb,var(--md-sys-color-surface-container-high, var(--md-sys-color-surface)),transparent 4%)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content{max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button{color:var(--md-sys-color-on-surface-variant)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button:hover{color:var(--md-sys-color-primary)}praxis-dynamic-form-dialog-host .dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}.pfx-blur-backdrop{background-color:var(--pfx-backdrop, rgba(15, 23, 42, .42))!important;backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%)}.pfx-transparent-backdrop{background-color:transparent!important}.cdk-overlay-pane.pfx-dialog-pane{overflow:hidden;transition:width .2s ease,height .2s ease,margin .2s ease}.cdk-overlay-pane.pfx-dialog-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane .mdc-dialog__surface{display:flex;flex-direction:column;width:100%;min-width:0;height:100%;max-height:inherit;overflow:hidden;background:var(--pfx-form-surface, var(--md-sys-color-surface))!important;border:1px solid var(--pfx-form-stroke, var(--md-sys-color-outline-variant))!important}.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mdc-dialog__surface{backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cdk-overlay-pane.pfx-drawer-pane{margin:0!important;border-radius:0!important;box-shadow:var(--md-sys-elevation-level3, 0 24px 80px rgba(15, 23, 42, .28)),0 0 0 1px color-mix(in srgb,var(--md-sys-color-outline, #64748b) 24%,transparent)}.cdk-overlay-pane.pfx-drawer-pane.pfx-drawer-maximized{margin:var(--pfx-drawer-edge-gap, 8px)!important}.cdk-overlay-pane.pfx-drawer-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-drawer-pane .mdc-dialog__surface{min-width:0!important;border-radius:0!important}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisResourceIdentityComponent, selector: "praxis-resource-identity", inputs: ["identity", "emptyTitle", "density", "showMetadataLabels", "showKeyLabel", "ariaLabel"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "initialValue", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "readUrl", "submitUrl", "submitMethod", "submitIdempotencyKey", "submitCorrelationId", "submitResourceVersion", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "showAiAssistant", "formId", "componentInstanceId", "configPersistenceStrategy", "layout", "generatedLayoutPreset", "layoutPolicy", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "fieldIconPolicy", "domainRules", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "configPatchChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "ruleDiagnosticsChange"] }], encapsulation: i0.ViewEncapsulation.None });
6106
+ `, isInline: true, styles: ["praxis-dynamic-form-dialog-host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}praxis-dynamic-form-dialog-host[data-density=compact]{--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}praxis-dynamic-form-dialog-host .dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);min-height:var(--dlg-header-h);padding:10px var(--dlg-pad);margin:0;background:var(--md-sys-color-surface-container-high);border-bottom:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-heading{display:grid;min-width:0;gap:2px}praxis-dynamic-form-dialog-host .dialog-title-icon{display:inline-grid;flex:0 0 auto;width:36px;height:36px;place-items:center;border-radius:10px;color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}praxis-dynamic-form-dialog-host .dialog-title-icon mat-icon{width:22px;height:22px;font-size:22px}praxis-dynamic-form-dialog-host .dialog-subtitle{display:-webkit-box;overflow:hidden;color:var(--md-sys-color-on-surface-variant);font-size:.82rem;font-weight:400;line-height:1.35;-webkit-box-orient:vertical;-webkit-line-clamp:2}praxis-dynamic-form-dialog-host .spacer{flex:1}praxis-dynamic-form-dialog-host .dialog-content{flex:1 1 auto;overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}praxis-dynamic-form-dialog-host .crud-operation-context{position:sticky;top:0;z-index:1;display:grid;gap:.75rem;margin-block-end:var(--dlg-pad);padding-block:.5rem .75rem;background:var(--pfx-form-surface, var(--md-sys-color-surface));border-bottom:1px solid var(--md-sys-color-outline-variant)}praxis-dynamic-form-dialog-host .crud-resource-identity{display:grid;gap:.25rem;min-width:0}praxis-dynamic-form-dialog-host .crud-resource-identity__label{color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-label-medium, 500 .75rem/1rem inherit)}praxis-dynamic-form-dialog-host.praxis-drawer{--dlg-header-h: 72px;width:100%;min-width:0;max-width:100vw;height:100dvh;background:var(--pfx-form-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-header{background:color-mix(in srgb,var(--md-sys-color-surface-container-high, var(--md-sys-color-surface)),transparent 4%)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content{max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host.praxis-surface-frame .dialog-content{max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button{color:var(--md-sys-color-on-surface-variant)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button:hover{color:var(--md-sys-color-primary)}praxis-dynamic-form-dialog-host .dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}.pfx-blur-backdrop{background-color:var(--pfx-backdrop, rgba(15, 23, 42, .42))!important;backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%)}.pfx-transparent-backdrop{background-color:transparent!important}.cdk-overlay-pane.pfx-dialog-pane{overflow:hidden;transition:width .2s ease,height .2s ease,margin .2s ease}.cdk-overlay-pane.pfx-dialog-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane .mdc-dialog__surface{display:flex;flex-direction:column;width:100%;min-width:0;height:100%;max-height:inherit;overflow:hidden;background:var(--pfx-form-surface, var(--md-sys-color-surface))!important;border:1px solid var(--pfx-form-stroke, var(--md-sys-color-outline-variant))!important}.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mdc-dialog__surface{backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cdk-overlay-pane.pfx-drawer-pane{margin:0!important;border-radius:0!important;box-shadow:var(--md-sys-elevation-level3, 0 24px 80px rgba(15, 23, 42, .28)),0 0 0 1px color-mix(in srgb,var(--md-sys-color-outline, #64748b) 24%,transparent)}.cdk-overlay-pane.pfx-drawer-pane.pfx-drawer-maximized{margin:var(--pfx-drawer-edge-gap, 8px)!important}.cdk-overlay-pane.pfx-drawer-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-drawer-pane .mdc-dialog__surface{min-width:0!important;border-radius:0!important}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisResourceIdentityComponent, selector: "praxis-resource-identity", inputs: ["identity", "emptyTitle", "density", "showMetadataLabels", "showKeyLabel", "ariaLabel"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "initialValue", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "readUrl", "submitUrl", "submitMethod", "submitIdempotencyKey", "submitCorrelationId", "submitResourceVersion", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "showAiAssistant", "formId", "componentInstanceId", "configPersistenceStrategy", "layout", "generatedLayoutPreset", "layoutPolicy", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "fieldIconPolicy", "domainRules", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "configPatchChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "ruleDiagnosticsChange"] }], encapsulation: i0.ViewEncapsulation.None });
5964
6107
  }
5965
6108
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormDialogHostComponent, decorators: [{
5966
6109
  type: Component,
@@ -5979,7 +6122,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5979
6122
  '[attr.data-density]': 'modal.density || "default"',
5980
6123
  '[attr.data-presentation]': 'presentation',
5981
6124
  '[class.praxis-drawer]': 'presentation === "drawer"',
6125
+ '[class.praxis-surface-frame]': 'isSurfaceFrame',
5982
6126
  }, template: `
6127
+ @if (!isSurfaceFrame) {
5983
6128
  <div mat-dialog-title class="dialog-header">
5984
6129
  @if (dialogIcon; as icon) {
5985
6130
  <span class="dialog-title-icon" aria-hidden="true">
@@ -6015,11 +6160,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
6015
6160
  cdkFocusInitial
6016
6161
  ></button>
6017
6162
  </div>
6163
+ }
6018
6164
 
6019
6165
  <mat-dialog-content
6020
6166
  class="dialog-content"
6021
- aria-labelledby="crudDialogTitle"
6022
- [attr.aria-describedby]="dialogSubtitle ? 'crudDialogDescription' : null"
6167
+ [attr.aria-labelledby]="isSurfaceFrame ? null : 'crudDialogTitle'"
6168
+ [attr.aria-describedby]="
6169
+ !isSurfaceFrame && dialogSubtitle ? 'crudDialogDescription' : null
6170
+ "
6023
6171
  >
6024
6172
  @if (showContextIdentity || resourceIdentity) {
6025
6173
  <div class="crud-operation-context" data-testid="crud-operation-context">
@@ -6072,16 +6220,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
6072
6220
  (formCancel)="onCancel()"
6073
6221
  ></praxis-dynamic-form>
6074
6222
  </mat-dialog-content>
6075
- `, styles: ["praxis-dynamic-form-dialog-host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}praxis-dynamic-form-dialog-host[data-density=compact]{--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}praxis-dynamic-form-dialog-host .dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);min-height:var(--dlg-header-h);padding:10px var(--dlg-pad);margin:0;background:var(--md-sys-color-surface-container-high);border-bottom:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-heading{display:grid;min-width:0;gap:2px}praxis-dynamic-form-dialog-host .dialog-title-icon{display:inline-grid;flex:0 0 auto;width:36px;height:36px;place-items:center;border-radius:10px;color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}praxis-dynamic-form-dialog-host .dialog-title-icon mat-icon{width:22px;height:22px;font-size:22px}praxis-dynamic-form-dialog-host .dialog-subtitle{display:-webkit-box;overflow:hidden;color:var(--md-sys-color-on-surface-variant);font-size:.82rem;font-weight:400;line-height:1.35;-webkit-box-orient:vertical;-webkit-line-clamp:2}praxis-dynamic-form-dialog-host .spacer{flex:1}praxis-dynamic-form-dialog-host .dialog-content{flex:1 1 auto;overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}praxis-dynamic-form-dialog-host .crud-operation-context{position:sticky;top:0;z-index:1;display:grid;gap:.75rem;margin-block-end:var(--dlg-pad);padding-block:.5rem .75rem;background:var(--pfx-form-surface, var(--md-sys-color-surface));border-bottom:1px solid var(--md-sys-color-outline-variant)}praxis-dynamic-form-dialog-host .crud-resource-identity{display:grid;gap:.25rem;min-width:0}praxis-dynamic-form-dialog-host .crud-resource-identity__label{color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-label-medium, 500 .75rem/1rem inherit)}praxis-dynamic-form-dialog-host.praxis-drawer{--dlg-header-h: 72px;width:100%;min-width:0;max-width:100vw;height:100dvh;background:var(--pfx-form-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-header{background:color-mix(in srgb,var(--md-sys-color-surface-container-high, var(--md-sys-color-surface)),transparent 4%)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content{max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button{color:var(--md-sys-color-on-surface-variant)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button:hover{color:var(--md-sys-color-primary)}praxis-dynamic-form-dialog-host .dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}.pfx-blur-backdrop{background-color:var(--pfx-backdrop, rgba(15, 23, 42, .42))!important;backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%)}.pfx-transparent-backdrop{background-color:transparent!important}.cdk-overlay-pane.pfx-dialog-pane{overflow:hidden;transition:width .2s ease,height .2s ease,margin .2s ease}.cdk-overlay-pane.pfx-dialog-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane .mdc-dialog__surface{display:flex;flex-direction:column;width:100%;min-width:0;height:100%;max-height:inherit;overflow:hidden;background:var(--pfx-form-surface, var(--md-sys-color-surface))!important;border:1px solid var(--pfx-form-stroke, var(--md-sys-color-outline-variant))!important}.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mdc-dialog__surface{backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cdk-overlay-pane.pfx-drawer-pane{margin:0!important;border-radius:0!important;box-shadow:var(--md-sys-elevation-level3, 0 24px 80px rgba(15, 23, 42, .28)),0 0 0 1px color-mix(in srgb,var(--md-sys-color-outline, #64748b) 24%,transparent)}.cdk-overlay-pane.pfx-drawer-pane.pfx-drawer-maximized{margin:var(--pfx-drawer-edge-gap, 8px)!important}.cdk-overlay-pane.pfx-drawer-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-drawer-pane .mdc-dialog__surface{min-width:0!important;border-radius:0!important}\n"] }]
6223
+ `, styles: ["praxis-dynamic-form-dialog-host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}praxis-dynamic-form-dialog-host[data-density=compact]{--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}praxis-dynamic-form-dialog-host .dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);min-height:var(--dlg-header-h);padding:10px var(--dlg-pad);margin:0;background:var(--md-sys-color-surface-container-high);border-bottom:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-heading{display:grid;min-width:0;gap:2px}praxis-dynamic-form-dialog-host .dialog-title-icon{display:inline-grid;flex:0 0 auto;width:36px;height:36px;place-items:center;border-radius:10px;color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}praxis-dynamic-form-dialog-host .dialog-title-icon mat-icon{width:22px;height:22px;font-size:22px}praxis-dynamic-form-dialog-host .dialog-subtitle{display:-webkit-box;overflow:hidden;color:var(--md-sys-color-on-surface-variant);font-size:.82rem;font-weight:400;line-height:1.35;-webkit-box-orient:vertical;-webkit-line-clamp:2}praxis-dynamic-form-dialog-host .spacer{flex:1}praxis-dynamic-form-dialog-host .dialog-content{flex:1 1 auto;overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}praxis-dynamic-form-dialog-host .crud-operation-context{position:sticky;top:0;z-index:1;display:grid;gap:.75rem;margin-block-end:var(--dlg-pad);padding-block:.5rem .75rem;background:var(--pfx-form-surface, var(--md-sys-color-surface));border-bottom:1px solid var(--md-sys-color-outline-variant)}praxis-dynamic-form-dialog-host .crud-resource-identity{display:grid;gap:.25rem;min-width:0}praxis-dynamic-form-dialog-host .crud-resource-identity__label{color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-label-medium, 500 .75rem/1rem inherit)}praxis-dynamic-form-dialog-host.praxis-drawer{--dlg-header-h: 72px;width:100%;min-width:0;max-width:100vw;height:100dvh;background:var(--pfx-form-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-header{background:color-mix(in srgb,var(--md-sys-color-surface-container-high, var(--md-sys-color-surface)),transparent 4%)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content{max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host.praxis-surface-frame .dialog-content{max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button{color:var(--md-sys-color-on-surface-variant)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button:hover{color:var(--md-sys-color-primary)}praxis-dynamic-form-dialog-host .dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}.pfx-blur-backdrop{background-color:var(--pfx-backdrop, rgba(15, 23, 42, .42))!important;backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%)}.pfx-transparent-backdrop{background-color:transparent!important}.cdk-overlay-pane.pfx-dialog-pane{overflow:hidden;transition:width .2s ease,height .2s ease,margin .2s ease}.cdk-overlay-pane.pfx-dialog-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane .mdc-dialog__surface{display:flex;flex-direction:column;width:100%;min-width:0;height:100%;max-height:inherit;overflow:hidden;background:var(--pfx-form-surface, var(--md-sys-color-surface))!important;border:1px solid var(--pfx-form-stroke, var(--md-sys-color-outline-variant))!important}.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mdc-dialog__surface{backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cdk-overlay-pane.pfx-drawer-pane{margin:0!important;border-radius:0!important;box-shadow:var(--md-sys-elevation-level3, 0 24px 80px rgba(15, 23, 42, .28)),0 0 0 1px color-mix(in srgb,var(--md-sys-color-outline, #64748b) 24%,transparent)}.cdk-overlay-pane.pfx-drawer-pane.pfx-drawer-maximized{margin:var(--pfx-drawer-edge-gap, 8px)!important}.cdk-overlay-pane.pfx-drawer-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-drawer-pane .mdc-dialog__surface{min-width:0!important;border-radius:0!important}\n"] }]
6076
6224
  }], ctorParameters: () => [{ type: undefined, decorators: [{
6225
+ type: Optional
6226
+ }, {
6077
6227
  type: Inject,
6078
6228
  args: [MatDialogRef]
6079
6229
  }] }, { type: undefined, decorators: [{
6230
+ type: Optional
6231
+ }, {
6080
6232
  type: Inject,
6081
6233
  args: [MAT_DIALOG_DATA]
6082
6234
  }] }, { type: DialogService }, { type: i2$1.GenericCrudService }, { type: undefined, decorators: [{
6083
6235
  type: Inject,
6084
6236
  args: [ASYNC_CONFIG_STORAGE]
6237
+ }] }, { type: undefined, decorators: [{
6238
+ type: Optional
6239
+ }, {
6240
+ type: Inject,
6241
+ args: [SURFACE_DRAWER_REF]
6242
+ }] }, { type: undefined, decorators: [{
6243
+ type: Optional
6244
+ }, {
6245
+ type: Inject,
6246
+ args: [SURFACE_DRAWER_CONTENT_DATA]
6085
6247
  }] }], propDecorators: { formComp: [{
6086
6248
  type: ViewChild,
6087
6249
  args: [PraxisDynamicForm]
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@praxisui/crud",
3
- "version": "9.0.4-rc.27",
3
+ "version": "9.0.4-rc.28",
4
4
  "description": "CRUD building blocks for Praxis UI: integrates dynamic forms and tables with unified configuration and services.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
7
7
  "@angular/core": "^21.0.0",
8
- "@praxisui/dynamic-form": "^9.0.4-rc.27",
9
- "@praxisui/table": "^9.0.4-rc.27",
10
- "@praxisui/core": "^9.0.4-rc.27",
11
- "@praxisui/dynamic-fields": "^9.0.4-rc.27",
12
- "@praxisui/settings-panel": "^9.0.4-rc.27",
8
+ "@praxisui/dynamic-form": "^9.0.4-rc.28",
9
+ "@praxisui/table": "^9.0.4-rc.28",
10
+ "@praxisui/core": "^9.0.4-rc.28",
11
+ "@praxisui/dynamic-fields": "^9.0.4-rc.28",
12
+ "@praxisui/settings-panel": "^9.0.4-rc.28",
13
13
  "@angular/cdk": "^21.0.0",
14
14
  "@angular/forms": "^21.0.0",
15
15
  "@angular/material": "^21.0.0",
16
16
  "@angular/router": "^21.0.0",
17
- "@praxisui/ai": "^9.0.4-rc.27",
17
+ "@praxisui/ai": "^9.0.4-rc.28",
18
18
  "rxjs": "~7.8.0"
19
19
  },
20
20
  "dependencies": {
@@ -1,7 +1,7 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { NgZone, OnChanges, EventEmitter, SimpleChanges, OnInit, Provider, AfterViewInit, OnDestroy } from '@angular/core';
2
+ import { NgZone, OnChanges, EventEmitter, SimpleChanges, OnInit, AfterViewInit, Provider, OnDestroy } from '@angular/core';
3
3
  import * as _praxisui_core from '@praxisui/core';
4
- import { RowAction, ToolbarAction, ApiEndpoint, ApiUrlEntry, DynamicFormLayoutPolicy, BackConfig, TableConfig, PraxisDataQueryContext, FormConfig, ResourceActionCatalogItem, ResourceSurfaceCatalogItem, MaterializedResourceIdentity, LoadingState, RestApiLinks, ResourceCapabilitySnapshot, GenericCrudService, AsyncConfigStorage, ComponentDocMeta, AiCapabilityCatalog, AiCapability, AiCapabilityCategory, AiValueKind, ComponentAuthoringManifest, SettingsValueProvider } from '@praxisui/core';
4
+ import { RowAction, ToolbarAction, ApiEndpoint, ApiUrlEntry, DynamicFormLayoutPolicy, BackConfig, TableConfig, PraxisDataQueryContext, FormConfig, ResourceActionCatalogItem, ResourceSurfaceCatalogItem, MaterializedResourceIdentity, LoadingState, RestApiLinks, ResourceCapabilitySnapshot, SurfaceDrawerRef, GenericCrudService, AsyncConfigStorage, ComponentDocMeta, AiCapabilityCatalog, AiCapability, AiCapabilityCategory, AiValueKind, ComponentAuthoringManifest, SettingsValueProvider } from '@praxisui/core';
5
5
  export { BackConfig } from '@praxisui/core';
6
6
  import { TableConfigPersistenceStrategy } from '@praxisui/table';
7
7
  import { MatDialogConfig, MatDialogRef, MatDialog } from '@angular/material/dialog';
@@ -267,6 +267,7 @@ declare class PraxisCrudComponent implements OnChanges {
267
267
  private tryHandleCanonicalDeleteAction;
268
268
  private tryHandleCanonicalDuplicateDraftAction;
269
269
  private refreshTable;
270
+ private resolveSurfaceRuntime;
270
271
  private tryRefreshMaterializedLocalData;
271
272
  private isCurrentMaterializedRefresh;
272
273
  private resolveMaterializedReadUrl;
@@ -345,6 +346,14 @@ interface CrudDrawerResult {
345
346
  data?: Record<string, unknown>;
346
347
  }
347
348
 
349
+ interface CrudLaunchRuntime {
350
+ capabilities?: ResourceCapabilitySnapshot | null;
351
+ links?: RestApiLinks | null;
352
+ resourceIdentity?: MaterializedResourceIdentity | null;
353
+ contextIdentity?: MaterializedResourceIdentity | null;
354
+ /** Active runtime drawer session. It is never persisted in CRUD metadata. */
355
+ surfaceRuntime?: SurfaceDrawerRef | null;
356
+ }
348
357
  declare class CrudLauncherService {
349
358
  private readonly router;
350
359
  private readonly dialog;
@@ -352,16 +361,12 @@ declare class CrudLauncherService {
352
361
  private readonly global;
353
362
  private readonly operationResolver;
354
363
  private dialogHostPromise?;
364
+ private nextSurfaceFrameId;
355
365
  private readonly drawerAdapter;
356
366
  launch(action: CrudAction, row: Record<string, unknown> | undefined, metadata: CrudMetadata, componentKeyId?: string | null, drawerCallbacks?: {
357
367
  onClose?: () => void;
358
368
  onResult?: (result: CrudDrawerResult) => void;
359
- }, runtime?: {
360
- capabilities?: ResourceCapabilitySnapshot | null;
361
- links?: RestApiLinks | null;
362
- resourceIdentity?: MaterializedResourceIdentity | null;
363
- contextIdentity?: MaterializedResourceIdentity | null;
364
- }): Promise<{
369
+ }, runtime?: CrudLaunchRuntime): Promise<{
365
370
  mode: FormOpenMode;
366
371
  ref?: DialogRef<any>;
367
372
  }>;
@@ -390,26 +395,31 @@ declare class CrudLauncherService {
390
395
  private normalizeCrudOperationName;
391
396
  private isExplicitCrudAction;
392
397
  private defaultInferredLayoutPolicy;
398
+ private buildSurfaceFrameId;
399
+ private resolveSurfaceFrameTitle;
400
+ private resolveSurfaceFrameSubtitle;
401
+ private bindSurfaceFrameLifecycle;
393
402
  private mergeCrudOverrides;
394
403
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<CrudLauncherService, never>;
395
404
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<CrudLauncherService>;
396
405
  }
397
406
 
398
- declare class DynamicFormDialogHostComponent implements OnInit {
399
- dialogRef: DialogRef<DynamicFormDialogHostComponent>;
400
- data: any;
407
+ declare class DynamicFormDialogHostComponent implements OnInit, AfterViewInit {
408
+ dialogRef: DialogRef<DynamicFormDialogHostComponent> | null;
401
409
  private dialogService;
402
410
  private crud;
403
411
  private configStorage;
412
+ private readonly surfaceRef;
404
413
  formComp?: PraxisDynamicForm;
405
414
  modal: any;
406
- presentation: 'modal' | 'drawer';
415
+ presentation: 'modal' | 'drawer' | 'surface-frame';
407
416
  maximized: boolean;
408
417
  private initialSize;
409
418
  private readonly initialPosition?;
410
419
  private rememberState;
411
420
  private stateKey?;
412
421
  private backDefaults;
422
+ private terminalClose;
413
423
  private destroyRef;
414
424
  private readonly i18n;
415
425
  resourcePath?: string;
@@ -429,8 +439,10 @@ declare class DynamicFormDialogHostComponent implements OnInit {
429
439
  mode: 'create' | 'edit' | 'view';
430
440
  backConfig?: BackConfig;
431
441
  private idField;
442
+ readonly isSurfaceFrame: boolean;
443
+ readonly data: any;
432
444
  texts: Record<string, string>;
433
- constructor(dialogRef: DialogRef<DynamicFormDialogHostComponent>, data: any, dialogService: DialogService, crud: GenericCrudService<any>, configStorage: AsyncConfigStorage);
445
+ constructor(dialogRef: DialogRef<DynamicFormDialogHostComponent> | null, dialogData: any, dialogService: DialogService, crud: GenericCrudService<any>, configStorage: AsyncConfigStorage, surfaceRef?: SurfaceDrawerRef | null, surfaceContentData?: Readonly<Record<string, unknown>> | null);
434
446
  get resourceIdentityLabel(): string;
435
447
  get showContextIdentity(): boolean;
436
448
  private sameIdentity;
@@ -446,8 +458,10 @@ declare class DynamicFormDialogHostComponent implements OnInit {
446
458
  private resolveLayoutPolicy;
447
459
  private resolveSubmitLabel;
448
460
  ngOnInit(): void;
461
+ ngAfterViewInit(): void;
449
462
  onSave(result: unknown): void;
450
463
  onCancel(): void;
464
+ private canLeaveSurfaceFrame;
451
465
  toggleMaximize(initial?: boolean): void;
452
466
  private resolveOverlayPane;
453
467
  private captureRenderedInitialSize;
@@ -455,7 +469,7 @@ declare class DynamicFormDialogHostComponent implements OnInit {
455
469
  private applySavedState;
456
470
  private updateHostPosition;
457
471
  private resolveMaximizedDrawerPosition;
458
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<DynamicFormDialogHostComponent, never>;
472
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DynamicFormDialogHostComponent, [{ optional: true; }, { optional: true; }, null, null, null, { optional: true; }, { optional: true; }]>;
459
473
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DynamicFormDialogHostComponent, "praxis-dynamic-form-dialog-host", never, {}, {}, never, never, true, never>;
460
474
  }
461
475