@praxisui/crud 9.0.5 → 9.0.6

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
@@ -109,6 +109,15 @@ It emits:
109
109
  - `tableRuntimeConfigChange`
110
110
  - `crudAuthoringDocumentApplied`, `crudAuthoringDocumentSaved`
111
111
 
112
+ When visual authoring opens, the CRUD editor is seeded from the latest portable
113
+ table configuration emitted by the internal table. This keeps the editor aligned
114
+ with the columns, visibility, ordering, and other runtime settings currently shown
115
+ to the user instead of reopening an older input snapshot.
116
+
117
+ When both `metadata.resource.path` and `metadata.data` are present, remote resource
118
+ loading remains authoritative and the editor reports that the local rows are
119
+ ignored. Use only `metadata.data` for a local-data preview.
120
+
112
121
  The component keeps `crudContext` stable for change detection and refreshes the table after successful save/delete flows.
113
122
 
114
123
  Built-in modal, drawer and nested surface-frame form hosts do not close or complete save while a child Reactive Determination is pending or when its latest generation settled unsatisfied; cancel waits for bounded successful stabilization and stays open on timeout/failure. A custom `CrudDrawerAdapter` must bind `onReactiveDetermination`, `onReactiveDeterminationPendingChange`, and `onReactiveDeterminationStabilityChange`, use the child Dynamic Form stability API, and refuse every adapter-owned close/submit while pending or unsatisfied. These callbacks are runtime-only and must never be persisted in CRUD metadata.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-08-18T02:12:19.525Z",
3
+ "generatedAt": "2026-08-19T23:21:15.260Z",
4
4
  "packageName": "@praxisui/crud",
5
- "packageVersion": "9.0.5",
5
+ "packageVersion": "9.0.6",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 2,
@@ -770,6 +770,9 @@ function validateCrudAuthoringDocument(doc, context) {
770
770
  if (!trimString(metadata.resource?.path) && !Array.isArray(metadata.data)) {
771
771
  diagnostics.push(warnDiagnostic('crud.metadata.resource-or-data.missing', 'Provide metadata.resource.path or metadata.data so the CRUD can resolve its source', 'metadata.resource.path'));
772
772
  }
773
+ if (trimString(metadata.resource?.path) && Array.isArray(metadata.data)) {
774
+ diagnostics.push(warnDiagnostic('crud.metadata.data.ignored-by-resource', 'metadata.data is ignored because metadata.resource.path activates remote table mode', 'metadata.data'));
775
+ }
773
776
  if (context?.requireCanonicalActions) {
774
777
  for (const actionName of CANONICAL_ACTIONS) {
775
778
  if (!findCrudAction(metadata.actions, actionName)) {
@@ -1131,6 +1134,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1131
1134
  'crud.authoring.section.connection': 'Conexão',
1132
1135
  'crud.authoring.section.actions': 'Ações',
1133
1136
  'crud.authoring.section.defaults': 'Abertura e cabeçalho',
1137
+ 'crud.authoring.tab.defaults': 'Abertura',
1134
1138
  'crud.authoring.section.table': 'Tabela',
1135
1139
  'crud.authoring.section.json': 'JSON',
1136
1140
  'crud.authoring.overview.connection': 'Conexão',
@@ -1141,6 +1145,9 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1141
1145
  'crud.authoring.overview.connectionIdField': 'ID: {value}',
1142
1146
  'crud.authoring.overview.connectionEndpoint': 'API: {value}',
1143
1147
  'crud.authoring.overview.connectionDetailsPending': 'Identificador e endpoint ainda são opcionais aqui.',
1148
+ 'crud.authoring.overview.sourceRemote': 'Fonte remota',
1149
+ 'crud.authoring.overview.sourceRemoteLocalIgnored': 'Fonte remota · {count} registros locais ignorados',
1150
+ 'crud.authoring.overview.sourceLocal': 'Fonte local · {count} registros',
1144
1151
  'crud.authoring.overview.backVisible': 'Voltar visível',
1145
1152
  'crud.authoring.overview.backHidden': 'Voltar oculto',
1146
1153
  'crud.authoring.overview.modalDensity': 'Modal: {value}',
@@ -1191,6 +1198,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1191
1198
  'crud.authoring.diagnostics.warnings': 'Alertas',
1192
1199
  'crud.authoring.diagnostics.errorSummary': '{count} diagnósticos bloqueantes nesta seção.',
1193
1200
  'crud.authoring.diagnostics.warningSummary': '{count} diagnósticos que valem revisão nesta seção.',
1201
+ 'crud.authoring.diagnostics.warningSummarySingle': '1 diagnóstico que vale revisão nesta seção.',
1194
1202
  'crud.authoring.connection.resourcePath': 'Recurso',
1195
1203
  'crud.authoring.connection.resourcePath.placeholder': 'ex.: api/human-resources/cargos',
1196
1204
  'crud.authoring.connection.idField': 'Campo identificador',
@@ -1314,6 +1322,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1314
1322
  'crud.authoring.action.create': 'Criar',
1315
1323
  'crud.authoring.action.view': 'Visualizar',
1316
1324
  'crud.authoring.action.edit': 'Editar',
1325
+ 'crud.authoring.action.delete': 'Excluir',
1317
1326
  'crud.authoring.table.summary': 'A configuração detalhada da tabela continua sendo tratada pela superfície canônica de TableConfig. Nesta fase, o editor de CRUD preserva o snapshot atual da tabela sem duplicar o editor completo.',
1318
1327
  'crud.authoring.json.hint': 'O JSON abaixo representa o documento canônico de authoring do CRUD.',
1319
1328
  'crud.authoring.table.structureSummary': '{count} colunas | {density}',
@@ -1333,6 +1342,13 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1333
1342
  'crud.authoring.json.summaryWarnings': 'O documento canônico possui diagnósticos que valem revisão.',
1334
1343
  'crud.authoring.json.summaryClean': 'O documento canônico está estruturalmente consistente.',
1335
1344
  'crud.authoring.validation.routeRequired': 'Informe uma rota quando o modo efetivo for route.',
1345
+ 'crud.authoring.validation.componentInvalid': 'O componente deve ser praxis-crud.',
1346
+ 'crud.authoring.validation.tableRequired': 'Informe a configuração canônica da tabela.',
1347
+ 'crud.authoring.validation.resourcePathRequired': 'Informe o caminho do recurso configurado.',
1348
+ 'crud.authoring.validation.sourceMissing': 'Informe um recurso remoto ou dados locais para o CRUD.',
1349
+ 'crud.authoring.validation.dataIgnoredByResource': 'Os registros locais são ignorados porque o recurso remoto está ativo.',
1350
+ 'crud.authoring.validation.openModeInvalid': 'Use route, modal ou drawer como modo de abertura.',
1351
+ 'crud.authoring.validation.canonicalActionMissing': 'A ação CRUD canônica “{action}” ainda não foi declarada.',
1336
1352
  'crud.authoring.validation.formIdRequired': 'Informe um formId quando o modo efetivo for modal ou drawer.',
1337
1353
  'crud.authoring.validation.submitContract': 'submitUrl e submitMethod devem ser informados juntos.',
1338
1354
  'crud.authoring.overview.actionsNextCompact': 'Próxima: {action}. {summary}',
@@ -1351,6 +1367,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1351
1367
  'crud.authoring.section.connection': 'Connection',
1352
1368
  'crud.authoring.section.actions': 'Actions',
1353
1369
  'crud.authoring.section.defaults': 'Open mode and header',
1370
+ 'crud.authoring.tab.defaults': 'Opening',
1354
1371
  'crud.authoring.section.table': 'Table',
1355
1372
  'crud.authoring.section.json': 'JSON',
1356
1373
  'crud.authoring.overview.connection': 'Connection',
@@ -1361,6 +1378,9 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1361
1378
  'crud.authoring.overview.connectionIdField': 'ID: {value}',
1362
1379
  'crud.authoring.overview.connectionEndpoint': 'API: {value}',
1363
1380
  'crud.authoring.overview.connectionDetailsPending': 'Identifier and endpoint details are still optional here.',
1381
+ 'crud.authoring.overview.sourceRemote': 'Remote source',
1382
+ 'crud.authoring.overview.sourceRemoteLocalIgnored': 'Remote source · {count} local rows ignored',
1383
+ 'crud.authoring.overview.sourceLocal': 'Local source · {count} rows',
1364
1384
  'crud.authoring.overview.backVisible': 'Back visible',
1365
1385
  'crud.authoring.overview.backHidden': 'Back hidden',
1366
1386
  'crud.authoring.overview.modalDensity': 'Modal: {value}',
@@ -1411,6 +1431,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1411
1431
  'crud.authoring.diagnostics.warnings': 'Warnings',
1412
1432
  'crud.authoring.diagnostics.errorSummary': '{count} blocking diagnostics in this section.',
1413
1433
  'crud.authoring.diagnostics.warningSummary': '{count} diagnostics worth reviewing in this section.',
1434
+ 'crud.authoring.diagnostics.warningSummarySingle': '1 diagnostic worth reviewing in this section.',
1414
1435
  'crud.authoring.connection.resourcePath': 'Resource',
1415
1436
  'crud.authoring.connection.resourcePath.placeholder': 'e.g. api/human-resources/cargos',
1416
1437
  'crud.authoring.connection.idField': 'Identifier field',
@@ -1534,6 +1555,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1534
1555
  'crud.authoring.action.create': 'Create',
1535
1556
  'crud.authoring.action.view': 'View',
1536
1557
  'crud.authoring.action.edit': 'Edit',
1558
+ 'crud.authoring.action.delete': 'Delete',
1537
1559
  'crud.authoring.table.summary': 'Detailed table configuration remains owned by the canonical TableConfig surface. In this phase, the CRUD editor preserves the current table snapshot without duplicating the full table editor.',
1538
1560
  'crud.authoring.table.structureSummary': '{count} columns | {density}',
1539
1561
  'crud.authoring.table.paginationSummary': 'Page size {value}',
@@ -1553,6 +1575,13 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1553
1575
  'crud.authoring.json.summaryWarnings': 'The canonical document has diagnostics worth reviewing.',
1554
1576
  'crud.authoring.json.summaryClean': 'The canonical document is structurally consistent.',
1555
1577
  'crud.authoring.validation.routeRequired': 'Provide a route when the effective mode is route.',
1578
+ 'crud.authoring.validation.componentInvalid': 'The component must be praxis-crud.',
1579
+ 'crud.authoring.validation.tableRequired': 'Provide the canonical table configuration.',
1580
+ 'crud.authoring.validation.resourcePathRequired': 'Provide the configured resource path.',
1581
+ 'crud.authoring.validation.sourceMissing': 'Provide a remote resource or local data for the CRUD.',
1582
+ 'crud.authoring.validation.dataIgnoredByResource': 'Local rows are ignored because the remote resource is active.',
1583
+ 'crud.authoring.validation.openModeInvalid': 'Use route, modal, or drawer as the open mode.',
1584
+ 'crud.authoring.validation.canonicalActionMissing': 'Canonical CRUD action “{action}” is not declared yet.',
1556
1585
  'crud.authoring.validation.formIdRequired': 'Provide a formId when the effective mode is modal or drawer.',
1557
1586
  'crud.authoring.validation.submitContract': 'submitUrl and submitMethod must be provided together.',
1558
1587
  'crud.authoring.overview.actionsNextCompact': 'Next: {action}. {summary}',
@@ -1711,6 +1740,10 @@ class CrudMetadataEditorComponent {
1711
1740
  actionLabel(actionName) {
1712
1741
  return this.tx(`crud.authoring.action.${actionName}`, actionName);
1713
1742
  }
1743
+ actionUsesRoute(actionName) {
1744
+ const action = this.actionValue(actionName);
1745
+ return (action.openMode || this.defaultsOpenMode()) === 'route';
1746
+ }
1714
1747
  actionSummary(actionName) {
1715
1748
  const action = this.actionValue(actionName);
1716
1749
  const mode = (action.openMode || this.defaultsOpenMode());
@@ -1809,7 +1842,8 @@ class CrudMetadataEditorComponent {
1809
1842
  const apiStatus = this.actionGroupStatus(actionName, 'api');
1810
1843
  const inputsSummary = this.actionInputsSummaryBadge(actionName);
1811
1844
  if (submitStatus === 'ready' && apiStatus === 'ready') {
1812
- return this.tx('crud.authoring.action.advancedReady', 'Advanced ready');
1845
+ const summary = this.tx('crud.authoring.action.advancedReady', 'Advanced ready');
1846
+ return inputsSummary ? `${summary} · ${inputsSummary}` : summary;
1813
1847
  }
1814
1848
  const pending = [];
1815
1849
  if (submitStatus !== 'ready') {
@@ -1917,20 +1951,16 @@ class CrudMetadataEditorComponent {
1917
1951
  return hasBinding ? 'ready' : 'pending';
1918
1952
  }
1919
1953
  if (group === 'schema') {
1920
- return String(action.form?.schemaUrl || '').trim() ? 'ready' : 'pending';
1954
+ return 'ready';
1921
1955
  }
1922
1956
  if (group === 'submit') {
1923
1957
  const hasSubmitError = actionDiagnostics.some((issue) => issue.code === 'crud.metadata.actions.form.submit-contract.invalid');
1924
1958
  if (hasSubmitError) {
1925
1959
  return 'invalid';
1926
1960
  }
1927
- const hasSubmitPair = !!String(action.form?.submitUrl || '').trim()
1928
- && !!String(action.form?.submitMethod || '').trim();
1929
- return hasSubmitPair ? 'ready' : 'pending';
1961
+ return 'ready';
1930
1962
  }
1931
- const hasApiEndpoint = !!String(action.form?.apiEndpointKey || '').trim();
1932
- const hasApiEntry = !!String(this.actionApiUrlEntryDraft(actionName) || '').trim();
1933
- return hasApiEndpoint || hasApiEntry ? 'ready' : 'pending';
1963
+ return 'ready';
1934
1964
  }
1935
1965
  actionGroupStatusLabel(actionName, group) {
1936
1966
  return {
@@ -1990,13 +2020,7 @@ class CrudMetadataEditorComponent {
1990
2020
  const hasBinding = effectiveMode === 'route'
1991
2021
  ? !!String(action.route || '').trim()
1992
2022
  : !!String(action.formId || '').trim();
1993
- const hasSubmitPair = !!String(action.form?.submitUrl || '').trim()
1994
- && !!String(action.form?.submitMethod || '').trim();
1995
- const hasSchema = !!String(action.form?.schemaUrl || '').trim();
1996
- if (hasBinding && (hasSubmitPair || hasSchema)) {
1997
- return 'ready';
1998
- }
1999
- return 'pending';
2023
+ return hasBinding ? 'ready' : 'pending';
2000
2024
  }
2001
2025
  actionStatusLabel(actionName) {
2002
2026
  return {
@@ -2025,6 +2049,16 @@ class CrudMetadataEditorComponent {
2025
2049
  if (endpointKey) {
2026
2050
  parts.push(this.tx('crud.authoring.overview.connectionEndpoint', 'API: {value}').replace('{value}', endpointKey));
2027
2051
  }
2052
+ const localRows = this.currentDocument().metadata.data;
2053
+ if (this.resourcePath().trim()) {
2054
+ parts.push(Array.isArray(localRows)
2055
+ ? this.tx('crud.authoring.overview.sourceRemoteLocalIgnored', 'Fonte remota · {count} registros locais ignorados').replace('{count}', String(localRows.length))
2056
+ : this.tx('crud.authoring.overview.sourceRemote', 'Fonte remota'));
2057
+ }
2058
+ else if (Array.isArray(localRows)) {
2059
+ parts.push(this.tx('crud.authoring.overview.sourceLocal', 'Fonte local · {count} registros')
2060
+ .replace('{count}', String(localRows.length)));
2061
+ }
2028
2062
  if (!parts.length) {
2029
2063
  return this.tx('crud.authoring.overview.connectionDetailsPending', 'Identifier and endpoint details are still optional here.');
2030
2064
  }
@@ -2220,9 +2254,13 @@ class CrudMetadataEditorComponent {
2220
2254
  return this.tx('crud.authoring.json.summaryClean', 'The canonical document is structurally consistent.');
2221
2255
  }
2222
2256
  sectionStatus(section) {
2223
- if (this.sectionDiagnostics(section).some((issue) => issue.level === 'error')) {
2257
+ const diagnostics = this.sectionDiagnostics(section);
2258
+ if (diagnostics.some((issue) => issue.level === 'error')) {
2224
2259
  return 'invalid';
2225
2260
  }
2261
+ if (diagnostics.some((issue) => issue.level === 'warning')) {
2262
+ return 'pending';
2263
+ }
2226
2264
  if (section === 'connection') {
2227
2265
  return this.resourcePath().trim() ? 'ready' : 'pending';
2228
2266
  }
@@ -2357,7 +2395,12 @@ class CrudMetadataEditorComponent {
2357
2395
  return this.tx('crud.authoring.diagnostics.errorSummary', '{count} blocking diagnostics in this section.')
2358
2396
  .replace('{count}', String(errorCount));
2359
2397
  }
2360
- return this.tx('crud.authoring.diagnostics.warningSummary', '{count} diagnostics worth reviewing in this section.')
2398
+ const key = warningCount === 1
2399
+ ? 'crud.authoring.diagnostics.warningSummarySingle'
2400
+ : 'crud.authoring.diagnostics.warningSummary';
2401
+ return this.tx(key, warningCount === 1
2402
+ ? '1 diagnostic worth reviewing in this section.'
2403
+ : '{count} diagnostics worth reviewing in this section.')
2361
2404
  .replace('{count}', String(warningCount));
2362
2405
  }
2363
2406
  modeLabel(mode) {
@@ -2390,11 +2433,25 @@ class CrudMetadataEditorComponent {
2390
2433
  }
2391
2434
  diagnosticMessage(code, fallback) {
2392
2435
  const key = {
2436
+ 'crud.metadata.component.invalid': 'crud.authoring.validation.componentInvalid',
2437
+ 'crud.metadata.table.required': 'crud.authoring.validation.tableRequired',
2438
+ 'crud.metadata.resource.path.required': 'crud.authoring.validation.resourcePathRequired',
2439
+ 'crud.metadata.resource-or-data.missing': 'crud.authoring.validation.sourceMissing',
2440
+ 'crud.metadata.data.ignored-by-resource': 'crud.authoring.validation.dataIgnoredByResource',
2441
+ 'crud.metadata.actions.openMode.invalid': 'crud.authoring.validation.openModeInvalid',
2393
2442
  'crud.metadata.actions.route.required': 'crud.authoring.validation.routeRequired',
2394
2443
  'crud.metadata.actions.formId.required': 'crud.authoring.validation.formIdRequired',
2395
2444
  'crud.metadata.actions.form.submit-contract.invalid': 'crud.authoring.validation.submitContract',
2396
2445
  }[code];
2397
- return key ? this.tx(key, fallback) : fallback;
2446
+ if (key) {
2447
+ return this.tx(key, fallback);
2448
+ }
2449
+ const canonicalActionMatch = code.match(/^crud\.metadata\.actions\.(create|view|edit|delete)\.missing$/);
2450
+ if (canonicalActionMatch) {
2451
+ return this.tx('crud.authoring.validation.canonicalActionMissing', 'Canonical CRUD action {action} is not declared yet')
2452
+ .replace('{action}', this.actionLabel(canonicalActionMatch[1]));
2453
+ }
2454
+ return fallback;
2398
2455
  }
2399
2456
  setResourceField(field, value) {
2400
2457
  this.updateMetadata((metadata) => ({
@@ -2625,13 +2682,13 @@ class CrudMetadataEditorComponent {
2625
2682
  .some((section) => this.sectionStatus(section) === bucket);
2626
2683
  }
2627
2684
  sectionDiagnostics(section) {
2628
- const prefix = {
2629
- connection: 'metadata.resource',
2630
- defaults: 'metadata.defaults',
2631
- actions: 'metadata.actions',
2632
- table: 'metadata.table',
2685
+ const prefixes = {
2686
+ connection: ['metadata.component', 'metadata.resource', 'metadata.data'],
2687
+ defaults: ['metadata.defaults'],
2688
+ actions: ['metadata.actions'],
2689
+ table: ['metadata.table'],
2633
2690
  }[section];
2634
- return this.diagnostics().filter((issue) => String(issue.path || '').includes(prefix));
2691
+ return this.diagnostics().filter((issue) => prefixes.some((prefix) => String(issue.path || '').includes(prefix)));
2635
2692
  }
2636
2693
  actionDiagnostics(actionName) {
2637
2694
  return this.diagnostics()
@@ -2737,7 +2794,7 @@ class CrudMetadataEditorComponent {
2737
2794
  </div>
2738
2795
  </section>
2739
2796
 
2740
- <mat-tab-group class="editor-tabs" data-testid="crud-editor-tabs" dynamicHeight>
2797
+ <mat-tab-group class="editor-tabs" data-testid="crud-editor-tabs" dynamicHeight animationDuration="0ms">
2741
2798
  <mat-tab>
2742
2799
  <ng-template mat-tab-label>
2743
2800
  <mat-icon [praxisIcon]="'link'"></mat-icon>
@@ -2781,7 +2838,7 @@ class CrudMetadataEditorComponent {
2781
2838
  <mat-tab>
2782
2839
  <ng-template mat-tab-label>
2783
2840
  <mat-icon [praxisIcon]="'tune'"></mat-icon>
2784
- <span data-testid="crud-editor-tab-defaults">{{ tx('crud.authoring.section.defaults', 'Open mode and header') }}</span>
2841
+ <span data-testid="crud-editor-tab-defaults">{{ tx('crud.authoring.tab.defaults', 'Opening') }}</span>
2785
2842
  <span
2786
2843
  class="tab-status-dot"
2787
2844
  [class.tab-status-dot--warn]="sectionStatus('defaults') === 'pending'"
@@ -2966,16 +3023,20 @@ class CrudMetadataEditorComponent {
2966
3023
  }
2967
3024
  </mat-select>
2968
3025
  </mat-form-field>
2969
- <mat-form-field appearance="outline">
2970
- <mat-label>{{ tx('crud.authoring.action.route', 'Route') }}</mat-label>
2971
- <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-route'" [ngModel]="actionValue(actionName).route || ''" (ngModelChange)="setActionField(actionName, 'route', $event)" [disabled]="isReadonly()" />
2972
- </mat-form-field>
2973
- <mat-form-field appearance="outline">
2974
- <mat-label>{{ tx('crud.authoring.action.formId', 'formId') }}</mat-label>
2975
- <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-formId'" [ngModel]="actionValue(actionName).formId || ''" (ngModelChange)="setActionField(actionName, 'formId', $event)" [disabled]="isReadonly()" />
2976
- </mat-form-field>
3026
+ @if (actionUsesRoute(actionName)) {
3027
+ <mat-form-field appearance="outline">
3028
+ <mat-label>{{ tx('crud.authoring.action.route', 'Route') }}</mat-label>
3029
+ <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-route'" [ngModel]="actionValue(actionName).route || ''" (ngModelChange)="setActionField(actionName, 'route', $event)" [disabled]="isReadonly()" />
3030
+ </mat-form-field>
3031
+ } @else {
3032
+ <mat-form-field appearance="outline">
3033
+ <mat-label>{{ tx('crud.authoring.action.formId', 'formId') }}</mat-label>
3034
+ <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-formId'" [ngModel]="actionValue(actionName).formId || ''" (ngModelChange)="setActionField(actionName, 'formId', $event)" [disabled]="isReadonly()" />
3035
+ </mat-form-field>
3036
+ }
2977
3037
  </div>
2978
3038
  </section>
3039
+ @if (!actionUsesRoute(actionName)) {
2979
3040
  <section class="action-group" [attr.data-testid]="'crud-editor-action-' + actionName + '-schema-group'">
2980
3041
  <div class="action-group__header">
2981
3042
  <div>
@@ -3128,6 +3189,7 @@ class CrudMetadataEditorComponent {
3128
3189
  </div>
3129
3190
  </section>
3130
3191
  </mat-expansion-panel>
3192
+ }
3131
3193
  </mat-expansion-panel>
3132
3194
  }
3133
3195
  </div>
@@ -3216,7 +3278,7 @@ class CrudMetadataEditorComponent {
3216
3278
  </mat-tab>
3217
3279
  </mat-tab-group>
3218
3280
  </section>
3219
- `, isInline: true, styles: [":host{display:block;min-width:0;color:var(--md-sys-color-on-surface,#1a1b20)}.editor-shell{display:grid;gap:16px}.editor-header{display:flex;justify-content:space-between;gap:16px;align-items:start}.editor-overview,.editor-health-map{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.editor-health-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-health-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-health-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus{display:flex;gap:12px;align-items:start;padding:14px 16px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-focus--success{background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.editor-focus-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent);white-space:nowrap}.editor-focus-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-focus-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus-copy{display:grid;gap:4px}.editor-focus-copy p{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-tabs{display:flex;flex-direction:column;min-width:0}.editor-tabs .mat-mdc-tab{min-width:118px}.editor-tabs ::ng-deep .mdc-tab__content{gap:8px}.editor-tabs ::ng-deep .mat-mdc-tab-body-wrapper,.editor-tabs ::ng-deep .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.editor-tabs ::ng-deep .mat-mdc-tab-body-content{overflow:visible}.tab-status-dot{width:8px;height:8px;border-radius:999px;background:var(--md-sys-color-primary,#1263b4);flex:0 0 auto}.tab-status-dot--warn{background:var(--md-sys-color-tertiary,#7d5700)}.tab-status-dot--error{background:var(--md-sys-color-error,#b3261e)}.tab-content{display:grid;gap:14px;min-width:0;padding:12px 2px 4px;box-sizing:border-box}.editor-overview-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-overview-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-overview-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-overview-label{font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-overview-value{line-height:1.3;overflow-wrap:anywhere}.editor-overview-note{margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-header h2,.editor-card h3{margin:0}.editor-header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-chip{padding:6px 10px;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary,#1263b4) 12%,transparent);font-size:12px}.editor-card{display:grid;gap:14px;padding:16px;border-radius:20px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 70%,transparent)}.editor-section-header{display:flex;justify-content:space-between;gap:12px;align-items:start}.editor-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid--compact{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.editor-toggles{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-actions,.action-grid{display:grid;gap:14px}.action-summary-strip{display:flex;flex-wrap:wrap;gap:8px}.action-summary-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.action-summary-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.action-summary-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.action-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 88%,transparent)}.action-group--advanced{background:color-mix(in srgb,var(--md-sys-color-surface-container,#f5f7fb) 90%,transparent)}.action-group__header{display:grid;gap:4px}.action-subgroup{display:grid;gap:10px;padding:10px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low,#fafbfd) 92%,transparent)}.action-subgroup__header{display:grid;gap:4px}.action-param-list{display:grid;gap:12px}.action-param-row{display:grid;gap:8px}.action-advanced-panel{border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 65%,transparent);border-radius:16px}.editor-section-note{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.diagnostics-groups{display:grid;gap:12px}.diagnostics-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.diagnostics{display:grid;gap:8px;margin:0;padding-left:18px}.diagnostics .error{color:var(--md-sys-color-error,#b3261e)}pre{margin:0;overflow:auto;max-height:320px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, 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] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i4.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i4.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i8.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i9.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i10.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i10.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i10.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisTableInlineAuthoringEditorComponent, selector: "praxis-table-inline-authoring-editor", inputs: ["config", "readonly"], outputs: ["configChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3281
+ `, isInline: true, styles: [":host{display:block;min-width:0;color:var(--md-sys-color-on-surface,#1a1b20)}.editor-shell{display:grid;gap:16px}.editor-header{display:flex;justify-content:space-between;gap:16px;align-items:start}.editor-overview,.editor-health-map{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.editor-health-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-health-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-health-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus{display:flex;gap:12px;align-items:start;padding:14px 16px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-focus--success{background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.editor-focus-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent);white-space:nowrap}.editor-focus-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-focus-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus-copy{display:grid;gap:4px}.editor-focus-copy p{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-tabs{display:flex;flex-direction:column;min-width:0}.editor-tabs .mat-mdc-tab{min-width:118px}.editor-tabs ::ng-deep .mdc-tab__content{gap:8px}.editor-tabs ::ng-deep .mat-mdc-tab-body-wrapper,.editor-tabs ::ng-deep .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.editor-tabs ::ng-deep .mat-mdc-tab-body-content{overflow:hidden}.tab-status-dot{width:8px;height:8px;border-radius:999px;background:var(--md-sys-color-primary,#1263b4);flex:0 0 auto}.tab-status-dot--warn{background:var(--md-sys-color-tertiary,#7d5700)}.tab-status-dot--error{background:var(--md-sys-color-error,#b3261e)}.tab-content{display:grid;gap:14px;min-width:0;padding:12px 2px 4px;box-sizing:border-box}.editor-overview-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-overview-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-overview-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-overview-label{font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-overview-value{line-height:1.3;overflow-wrap:anywhere}.editor-overview-note{margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-header h2,.editor-card h3{margin:0}.editor-header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-chip{padding:6px 10px;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary,#1263b4) 12%,transparent);font-size:12px}.editor-card{display:grid;gap:14px;padding:16px;border-radius:20px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 70%,transparent)}.editor-section-header{display:flex;justify-content:space-between;gap:12px;align-items:start}.editor-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid--compact{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.editor-toggles{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-actions,.action-grid{display:grid;gap:14px}.action-summary-strip{display:flex;flex-wrap:wrap;gap:8px}.action-summary-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.action-summary-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.action-summary-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.action-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 88%,transparent)}.action-group--advanced{background:color-mix(in srgb,var(--md-sys-color-surface-container,#f5f7fb) 90%,transparent)}.action-group__header{display:grid;gap:4px}.action-subgroup{display:grid;gap:10px;padding:10px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low,#fafbfd) 92%,transparent)}.action-subgroup__header{display:grid;gap:4px}.action-param-list{display:grid;gap:12px}.action-param-row{display:grid;gap:8px}.action-advanced-panel{border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 65%,transparent);border-radius:16px}.editor-section-note{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.diagnostics-groups{display:grid;gap:12px}.diagnostics-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.diagnostics{display:grid;gap:8px;margin:0;padding-left:18px}.diagnostics .error{color:var(--md-sys-color-error,#b3261e)}pre{margin:0;overflow:auto;max-height:320px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, 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] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i4.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i4.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i8.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i9.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i10.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i10.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i10.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisTableInlineAuthoringEditorComponent, selector: "praxis-table-inline-authoring-editor", inputs: ["config", "readonly"], outputs: ["configChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3220
3282
  }
3221
3283
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CrudMetadataEditorComponent, decorators: [{
3222
3284
  type: Component,
@@ -3301,7 +3363,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3301
3363
  </div>
3302
3364
  </section>
3303
3365
 
3304
- <mat-tab-group class="editor-tabs" data-testid="crud-editor-tabs" dynamicHeight>
3366
+ <mat-tab-group class="editor-tabs" data-testid="crud-editor-tabs" dynamicHeight animationDuration="0ms">
3305
3367
  <mat-tab>
3306
3368
  <ng-template mat-tab-label>
3307
3369
  <mat-icon [praxisIcon]="'link'"></mat-icon>
@@ -3345,7 +3407,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3345
3407
  <mat-tab>
3346
3408
  <ng-template mat-tab-label>
3347
3409
  <mat-icon [praxisIcon]="'tune'"></mat-icon>
3348
- <span data-testid="crud-editor-tab-defaults">{{ tx('crud.authoring.section.defaults', 'Open mode and header') }}</span>
3410
+ <span data-testid="crud-editor-tab-defaults">{{ tx('crud.authoring.tab.defaults', 'Opening') }}</span>
3349
3411
  <span
3350
3412
  class="tab-status-dot"
3351
3413
  [class.tab-status-dot--warn]="sectionStatus('defaults') === 'pending'"
@@ -3530,16 +3592,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3530
3592
  }
3531
3593
  </mat-select>
3532
3594
  </mat-form-field>
3533
- <mat-form-field appearance="outline">
3534
- <mat-label>{{ tx('crud.authoring.action.route', 'Route') }}</mat-label>
3535
- <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-route'" [ngModel]="actionValue(actionName).route || ''" (ngModelChange)="setActionField(actionName, 'route', $event)" [disabled]="isReadonly()" />
3536
- </mat-form-field>
3537
- <mat-form-field appearance="outline">
3538
- <mat-label>{{ tx('crud.authoring.action.formId', 'formId') }}</mat-label>
3539
- <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-formId'" [ngModel]="actionValue(actionName).formId || ''" (ngModelChange)="setActionField(actionName, 'formId', $event)" [disabled]="isReadonly()" />
3540
- </mat-form-field>
3595
+ @if (actionUsesRoute(actionName)) {
3596
+ <mat-form-field appearance="outline">
3597
+ <mat-label>{{ tx('crud.authoring.action.route', 'Route') }}</mat-label>
3598
+ <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-route'" [ngModel]="actionValue(actionName).route || ''" (ngModelChange)="setActionField(actionName, 'route', $event)" [disabled]="isReadonly()" />
3599
+ </mat-form-field>
3600
+ } @else {
3601
+ <mat-form-field appearance="outline">
3602
+ <mat-label>{{ tx('crud.authoring.action.formId', 'formId') }}</mat-label>
3603
+ <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-formId'" [ngModel]="actionValue(actionName).formId || ''" (ngModelChange)="setActionField(actionName, 'formId', $event)" [disabled]="isReadonly()" />
3604
+ </mat-form-field>
3605
+ }
3541
3606
  </div>
3542
3607
  </section>
3608
+ @if (!actionUsesRoute(actionName)) {
3543
3609
  <section class="action-group" [attr.data-testid]="'crud-editor-action-' + actionName + '-schema-group'">
3544
3610
  <div class="action-group__header">
3545
3611
  <div>
@@ -3692,6 +3758,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3692
3758
  </div>
3693
3759
  </section>
3694
3760
  </mat-expansion-panel>
3761
+ }
3695
3762
  </mat-expansion-panel>
3696
3763
  }
3697
3764
  </div>
@@ -3780,7 +3847,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3780
3847
  </mat-tab>
3781
3848
  </mat-tab-group>
3782
3849
  </section>
3783
- `, styles: [":host{display:block;min-width:0;color:var(--md-sys-color-on-surface,#1a1b20)}.editor-shell{display:grid;gap:16px}.editor-header{display:flex;justify-content:space-between;gap:16px;align-items:start}.editor-overview,.editor-health-map{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.editor-health-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-health-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-health-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus{display:flex;gap:12px;align-items:start;padding:14px 16px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-focus--success{background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.editor-focus-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent);white-space:nowrap}.editor-focus-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-focus-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus-copy{display:grid;gap:4px}.editor-focus-copy p{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-tabs{display:flex;flex-direction:column;min-width:0}.editor-tabs .mat-mdc-tab{min-width:118px}.editor-tabs ::ng-deep .mdc-tab__content{gap:8px}.editor-tabs ::ng-deep .mat-mdc-tab-body-wrapper,.editor-tabs ::ng-deep .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.editor-tabs ::ng-deep .mat-mdc-tab-body-content{overflow:visible}.tab-status-dot{width:8px;height:8px;border-radius:999px;background:var(--md-sys-color-primary,#1263b4);flex:0 0 auto}.tab-status-dot--warn{background:var(--md-sys-color-tertiary,#7d5700)}.tab-status-dot--error{background:var(--md-sys-color-error,#b3261e)}.tab-content{display:grid;gap:14px;min-width:0;padding:12px 2px 4px;box-sizing:border-box}.editor-overview-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-overview-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-overview-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-overview-label{font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-overview-value{line-height:1.3;overflow-wrap:anywhere}.editor-overview-note{margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-header h2,.editor-card h3{margin:0}.editor-header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-chip{padding:6px 10px;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary,#1263b4) 12%,transparent);font-size:12px}.editor-card{display:grid;gap:14px;padding:16px;border-radius:20px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 70%,transparent)}.editor-section-header{display:flex;justify-content:space-between;gap:12px;align-items:start}.editor-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid--compact{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.editor-toggles{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-actions,.action-grid{display:grid;gap:14px}.action-summary-strip{display:flex;flex-wrap:wrap;gap:8px}.action-summary-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.action-summary-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.action-summary-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.action-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 88%,transparent)}.action-group--advanced{background:color-mix(in srgb,var(--md-sys-color-surface-container,#f5f7fb) 90%,transparent)}.action-group__header{display:grid;gap:4px}.action-subgroup{display:grid;gap:10px;padding:10px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low,#fafbfd) 92%,transparent)}.action-subgroup__header{display:grid;gap:4px}.action-param-list{display:grid;gap:12px}.action-param-row{display:grid;gap:8px}.action-advanced-panel{border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 65%,transparent);border-radius:16px}.editor-section-note{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.diagnostics-groups{display:grid;gap:12px}.diagnostics-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.diagnostics{display:grid;gap:8px;margin:0;padding-left:18px}.diagnostics .error{color:var(--md-sys-color-error,#b3261e)}pre{margin:0;overflow:auto;max-height:320px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}\n"] }]
3850
+ `, styles: [":host{display:block;min-width:0;color:var(--md-sys-color-on-surface,#1a1b20)}.editor-shell{display:grid;gap:16px}.editor-header{display:flex;justify-content:space-between;gap:16px;align-items:start}.editor-overview,.editor-health-map{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.editor-health-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-health-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-health-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus{display:flex;gap:12px;align-items:start;padding:14px 16px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-focus--success{background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.editor-focus-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent);white-space:nowrap}.editor-focus-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-focus-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus-copy{display:grid;gap:4px}.editor-focus-copy p{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-tabs{display:flex;flex-direction:column;min-width:0}.editor-tabs .mat-mdc-tab{min-width:118px}.editor-tabs ::ng-deep .mdc-tab__content{gap:8px}.editor-tabs ::ng-deep .mat-mdc-tab-body-wrapper,.editor-tabs ::ng-deep .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.editor-tabs ::ng-deep .mat-mdc-tab-body-content{overflow:hidden}.tab-status-dot{width:8px;height:8px;border-radius:999px;background:var(--md-sys-color-primary,#1263b4);flex:0 0 auto}.tab-status-dot--warn{background:var(--md-sys-color-tertiary,#7d5700)}.tab-status-dot--error{background:var(--md-sys-color-error,#b3261e)}.tab-content{display:grid;gap:14px;min-width:0;padding:12px 2px 4px;box-sizing:border-box}.editor-overview-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-overview-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-overview-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-overview-label{font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-overview-value{line-height:1.3;overflow-wrap:anywhere}.editor-overview-note{margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-header h2,.editor-card h3{margin:0}.editor-header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-chip{padding:6px 10px;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary,#1263b4) 12%,transparent);font-size:12px}.editor-card{display:grid;gap:14px;padding:16px;border-radius:20px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 70%,transparent)}.editor-section-header{display:flex;justify-content:space-between;gap:12px;align-items:start}.editor-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid--compact{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.editor-toggles{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-actions,.action-grid{display:grid;gap:14px}.action-summary-strip{display:flex;flex-wrap:wrap;gap:8px}.action-summary-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.action-summary-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.action-summary-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.action-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 88%,transparent)}.action-group--advanced{background:color-mix(in srgb,var(--md-sys-color-surface-container,#f5f7fb) 90%,transparent)}.action-group__header{display:grid;gap:4px}.action-subgroup{display:grid;gap:10px;padding:10px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low,#fafbfd) 92%,transparent)}.action-subgroup__header{display:grid;gap:4px}.action-param-list{display:grid;gap:12px}.action-param-row{display:grid;gap:8px}.action-advanced-panel{border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 65%,transparent);border-radius:16px}.editor-section-note{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.diagnostics-groups{display:grid;gap:12px}.diagnostics-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.diagnostics{display:grid;gap:8px;margin:0;padding-left:18px}.diagnostics .error{color:var(--md-sys-color-error,#b3261e)}pre{margin:0;overflow:auto;max-height:320px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}\n"] }]
3784
3851
  }], ctorParameters: () => [], propDecorators: { documentInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "document", required: false }] }], metadataInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "metadata", required: false }] }], crudIdInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "crudId", required: false }] }], readonlyInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }] } });
3785
3852
  function cloneTableConfig(config) {
3786
3853
  return JSON.parse(JSON.stringify(config || { columns: [] }));
@@ -3880,6 +3947,7 @@ class PraxisCrudComponent {
3880
3947
  })();
3881
3948
  warnedMissingId = false;
3882
3949
  lastEmittedTableRuntimeConfigJson = '';
3950
+ lastTableRuntimeConfigSnapshot = null;
3883
3951
  lastAssignedTableConfigJson = '';
3884
3952
  lastAssignedTableCrudContextJson = '';
3885
3953
  lastAppliedResourceIdentity = null;
@@ -3970,6 +4038,8 @@ class PraxisCrudComponent {
3970
4038
  return;
3971
4039
  }
3972
4040
  this.materializedRefreshSequence += 1;
4041
+ this.lastTableRuntimeConfigSnapshot = null;
4042
+ this.lastEmittedTableRuntimeConfigJson = '';
3973
4043
  try {
3974
4044
  const parsed = typeof this.metadata === 'string'
3975
4045
  ? JSON.parse(this.metadata)
@@ -4177,6 +4247,10 @@ class PraxisCrudComponent {
4177
4247
  return current;
4178
4248
  }
4179
4249
  }
4250
+ onTableConfigChange(config) {
4251
+ this.lastTableRuntimeConfigSnapshot = this.cloneTableConfigSnapshot(config);
4252
+ this.emitTableRuntimeConfigSnapshot(this.lastTableRuntimeConfigSnapshot);
4253
+ }
4180
4254
  onTableMetadataChange() {
4181
4255
  this.emitTableRuntimeConfigSnapshot();
4182
4256
  }
@@ -4464,10 +4538,11 @@ class PraxisCrudComponent {
4464
4538
  }
4465
4539
  return null;
4466
4540
  }
4467
- emitTableRuntimeConfigSnapshot() {
4468
- const snapshot = this.getCurrentTableConfigSnapshot();
4541
+ emitTableRuntimeConfigSnapshot(preferredSnapshot) {
4542
+ const snapshot = preferredSnapshot || this.getCurrentTableConfigSnapshot();
4469
4543
  if (!snapshot)
4470
4544
  return;
4545
+ this.lastTableRuntimeConfigSnapshot = this.cloneTableConfigSnapshot(snapshot);
4471
4546
  try {
4472
4547
  const nextJson = JSON.stringify(snapshot);
4473
4548
  if (nextJson === this.lastEmittedTableRuntimeConfigJson)
@@ -4477,6 +4552,14 @@ class PraxisCrudComponent {
4477
4552
  catch { }
4478
4553
  this.tableRuntimeConfigChange.emit(snapshot);
4479
4554
  }
4555
+ cloneTableConfigSnapshot(config) {
4556
+ try {
4557
+ return JSON.parse(JSON.stringify(config));
4558
+ }
4559
+ catch {
4560
+ return config;
4561
+ }
4562
+ }
4480
4563
  applyResolvedCrudState(meta) {
4481
4564
  this.effectiveTableConfig = this.buildEffectiveTableConfig(meta, this.collectionCapabilities);
4482
4565
  const nextTableConfig = this.effectiveTableConfig ||
@@ -5210,8 +5293,18 @@ class PraxisCrudComponent {
5210
5293
  if (!this.isCustomizationAvailable()) {
5211
5294
  return;
5212
5295
  }
5213
- const seed = this.currentAuthoringDocument
5296
+ const canonicalSeed = this.currentAuthoringDocument
5214
5297
  || createCrudAuthoringDocument({ metadata: this.resolvedMetadata });
5298
+ const liveTableConfig = this.lastTableRuntimeConfigSnapshot
5299
+ || this.getCurrentTableConfigSnapshot();
5300
+ const seed = liveTableConfig
5301
+ ? createCrudAuthoringDocument({
5302
+ metadata: {
5303
+ ...canonicalSeed.metadata,
5304
+ table: this.cloneTableConfigSnapshot(liveTableConfig),
5305
+ },
5306
+ })
5307
+ : canonicalSeed;
5215
5308
  const ref = openCrudMetadataEditor(this.settingsPanel, {
5216
5309
  crudId: this.crudId,
5217
5310
  document: seed,
@@ -5249,6 +5342,9 @@ class PraxisCrudComponent {
5249
5342
  const next = parseLegacyOrCrudDocument(payload);
5250
5343
  this.currentAuthoringDocument = next;
5251
5344
  this.resolvedMetadata = next.metadata;
5345
+ this.lastTableRuntimeConfigSnapshot = next.metadata.table
5346
+ ? this.cloneTableConfigSnapshot(next.metadata.table)
5347
+ : null;
5252
5348
  this.tableQueryContext = this.resolveQueryContext(this.resolvedMetadata);
5253
5349
  this.tableFilterCriteria = this.resolveFilterCriteria(this.resolvedMetadata);
5254
5350
  this.applyResolvedCrudState(this.resolvedMetadata);
@@ -5569,6 +5665,7 @@ class PraxisCrudComponent {
5569
5665
  (bulkAction)="onBulkAction($event)"
5570
5666
  (collectionLinksChange)="onCollectionLinksChange($event)"
5571
5667
  (reset)="onResetPreferences()"
5668
+ (configChange)="onTableConfigChange($event)"
5572
5669
  (metadataChange)="onTableMetadataChange()"
5573
5670
  (loadingStateChange)="onTableLoadingStateChange($event)"
5574
5671
  ></praxis-table>
@@ -5618,6 +5715,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5618
5715
  (bulkAction)="onBulkAction($event)"
5619
5716
  (collectionLinksChange)="onCollectionLinksChange($event)"
5620
5717
  (reset)="onResetPreferences()"
5718
+ (configChange)="onTableConfigChange($event)"
5621
5719
  (metadataChange)="onTableMetadataChange()"
5622
5720
  (loadingStateChange)="onTableLoadingStateChange($event)"
5623
5721
  ></praxis-table>
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@praxisui/crud",
3
- "version": "9.0.5",
3
+ "version": "9.0.6",
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.5",
9
- "@praxisui/table": "^9.0.5",
10
- "@praxisui/core": "^9.0.5",
11
- "@praxisui/dynamic-fields": "^9.0.5",
12
- "@praxisui/settings-panel": "^9.0.5",
8
+ "@praxisui/dynamic-form": "^9.0.6",
9
+ "@praxisui/table": "^9.0.6",
10
+ "@praxisui/core": "^9.0.6",
11
+ "@praxisui/dynamic-fields": "^9.0.6",
12
+ "@praxisui/settings-panel": "^9.0.6",
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.5",
17
+ "@praxisui/ai": "^9.0.6",
18
18
  "rxjs": "~7.8.0"
19
19
  },
20
20
  "dependencies": {
@@ -257,6 +257,7 @@ declare class PraxisCrudComponent implements OnChanges {
257
257
  private readonly route;
258
258
  private warnedMissingId;
259
259
  private lastEmittedTableRuntimeConfigJson;
260
+ private lastTableRuntimeConfigSnapshot;
260
261
  private lastAssignedTableConfigJson;
261
262
  private lastAssignedTableCrudContextJson;
262
263
  private lastAppliedResourceIdentity;
@@ -286,6 +287,7 @@ declare class PraxisCrudComponent implements OnChanges {
286
287
  private resolveOpeningActionLabel;
287
288
  private hasExplicitOpenBinding;
288
289
  getCurrentTableConfigSnapshot(): TableConfig | null;
290
+ onTableConfigChange(config: TableConfig): void;
289
291
  onTableMetadataChange(): void;
290
292
  onTableLoadingStateChange(state: LoadingState): void;
291
293
  onCollectionLinksChange(links: RestApiLinks | null | undefined): void;
@@ -307,6 +309,7 @@ declare class PraxisCrudComponent implements OnChanges {
307
309
  private unwrapRestData;
308
310
  private extractCollectionData;
309
311
  private emitTableRuntimeConfigSnapshot;
312
+ private cloneTableConfigSnapshot;
310
313
  private applyResolvedCrudState;
311
314
  private assignTableConfigForBinding;
312
315
  private assignTableCrudContext;
@@ -703,6 +706,7 @@ declare class CrudMetadataEditorComponent implements SettingsValueProvider {
703
706
  actionParams(actionName: CrudEditorActionKey): CrudParamMapping[];
704
707
  actionInitialValueDraft(actionName: CrudEditorActionKey): string;
705
708
  actionLabel(actionName: CrudEditorActionKey): string;
709
+ actionUsesRoute(actionName: CrudEditorActionKey): boolean;
706
710
  actionSummary(actionName: CrudEditorActionKey): string;
707
711
  actionEffectiveBindingSummary(actionName: CrudEditorActionKey): string;
708
712
  actionSubmitSummary(actionName: CrudEditorActionKey): string;