@praxisui/crud 9.0.4-rc.36 → 9.0.4-rc.37
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.
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-08-
|
|
3
|
+
"generatedAt": "2026-08-09T11:18:35.694Z",
|
|
4
4
|
"packageName": "@praxisui/crud",
|
|
5
|
-
"packageVersion": "9.0.4-rc.
|
|
5
|
+
"packageVersion": "9.0.4-rc.37",
|
|
6
6
|
"sourceRegistry": "praxis-component-registry-ingestion",
|
|
7
7
|
"sourceRegistryVersion": "1.0.0",
|
|
8
8
|
"componentCount": 2,
|
|
@@ -97,6 +97,35 @@ function debugCrudLauncher(message, ...data) {
|
|
|
97
97
|
}
|
|
98
98
|
catch { }
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Captures the interaction origin before metadata, capability, or lazy-host
|
|
102
|
+
* work yields the event loop. Material can restore focus to an explicit
|
|
103
|
+
* element, which is essential when the action originated in a closing menu.
|
|
104
|
+
*/
|
|
105
|
+
function captureCrudActionFocusOrigin() {
|
|
106
|
+
if (typeof document === 'undefined') {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const active = document.activeElement;
|
|
110
|
+
if (!(active instanceof HTMLElement) || active === document.body || !active.isConnected) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
return active;
|
|
114
|
+
}
|
|
115
|
+
function isRuntimeFocusOrigin(value) {
|
|
116
|
+
return typeof HTMLElement !== 'undefined'
|
|
117
|
+
&& value instanceof HTMLElement
|
|
118
|
+
&& typeof value.focus === 'function';
|
|
119
|
+
}
|
|
120
|
+
function resolveCrudRestoreFocus(configured, actionFocusOrigin) {
|
|
121
|
+
if (configured === false || typeof configured === 'string') {
|
|
122
|
+
return configured;
|
|
123
|
+
}
|
|
124
|
+
if (typeof HTMLElement !== 'undefined' && configured instanceof HTMLElement) {
|
|
125
|
+
return configured;
|
|
126
|
+
}
|
|
127
|
+
return actionFocusOrigin ?? true;
|
|
128
|
+
}
|
|
100
129
|
class CrudLauncherService {
|
|
101
130
|
router = inject(Router);
|
|
102
131
|
dialog = inject(DialogService);
|
|
@@ -115,6 +144,15 @@ class CrudLauncherService {
|
|
|
115
144
|
}
|
|
116
145
|
})();
|
|
117
146
|
async launch(action, row, metadata, componentKeyId, drawerCallbacks, runtime) {
|
|
147
|
+
// Capture before every await below. A toolbar/row menu closes before the
|
|
148
|
+
// form host is resolved, so the browser's later activeElement is not a
|
|
149
|
+
// reliable representation of the user-facing action trigger.
|
|
150
|
+
// A responsive host can replace the menu trigger while the menu is
|
|
151
|
+
// closing. Keep the original runtime-only reference even when detached:
|
|
152
|
+
// the overlay host resolves its stable data-praxis-focus-key on close.
|
|
153
|
+
const actionFocusOrigin = isRuntimeFocusOrigin(runtime?.focusOrigin)
|
|
154
|
+
? runtime.focusOrigin
|
|
155
|
+
: captureCrudActionFocusOrigin();
|
|
118
156
|
const prefetchedDialogHost = this.isLikelyOverlayAction(action, metadata)
|
|
119
157
|
? this.loadDialogHost()
|
|
120
158
|
: undefined;
|
|
@@ -156,6 +194,8 @@ class CrudLauncherService {
|
|
|
156
194
|
resourceIdentity,
|
|
157
195
|
contextIdentity,
|
|
158
196
|
};
|
|
197
|
+
const modalCfg = { ...(merged.metadata.defaults?.modal || {}) };
|
|
198
|
+
const restoreFocus = resolveCrudRestoreFocus(modalCfg.restoreFocus, actionFocusOrigin);
|
|
159
199
|
if (mode === 'drawer' && runtime?.surfaceRuntime?.push) {
|
|
160
200
|
const frameId = this.buildSurfaceFrameId(actionForLaunch);
|
|
161
201
|
const frameRef = await runtime.surfaceRuntime.push({
|
|
@@ -163,6 +203,9 @@ class CrudLauncherService {
|
|
|
163
203
|
title: this.resolveSurfaceFrameTitle(actionForLaunch, merged.metadata),
|
|
164
204
|
titleIcon: stringOrUndefined$1(actionForLaunch['icon']),
|
|
165
205
|
subtitle: this.resolveSurfaceFrameSubtitle(actionForLaunch, merged.metadata),
|
|
206
|
+
...(actionFocusOrigin
|
|
207
|
+
? { returnFocusTo: actionFocusOrigin }
|
|
208
|
+
: {}),
|
|
166
209
|
content: {
|
|
167
210
|
component: await (prefetchedDialogHost ?? this.loadDialogHost()),
|
|
168
211
|
inputs: { data: dialogData },
|
|
@@ -197,12 +240,12 @@ class CrudLauncherService {
|
|
|
197
240
|
inputs,
|
|
198
241
|
resourceIdentity,
|
|
199
242
|
contextIdentity,
|
|
243
|
+
restoreFocus,
|
|
200
244
|
onClose: drawerCallbacks?.onClose,
|
|
201
245
|
onResult: drawerCallbacks?.onResult,
|
|
202
246
|
}));
|
|
203
247
|
return { mode };
|
|
204
248
|
}
|
|
205
|
-
const modalCfg = { ...(merged.metadata.defaults?.modal || {}) };
|
|
206
249
|
debugCrudLauncher('[CRUD:Launcher] opening dialog with:', {
|
|
207
250
|
action: merged.action.action,
|
|
208
251
|
formId: actionForLaunch.formId,
|
|
@@ -233,7 +276,7 @@ class CrudLauncherService {
|
|
|
233
276
|
panelClass: panelClasses,
|
|
234
277
|
backdropClass: mergedBackdropClasses,
|
|
235
278
|
autoFocus: modalCfg.autoFocus ?? true,
|
|
236
|
-
restoreFocus
|
|
279
|
+
restoreFocus,
|
|
237
280
|
minWidth: drawerMode
|
|
238
281
|
? (modalCfg.minWidth ?? DEFAULT_CRUD_DRAWER_MODAL_CONFIG.minWidth)
|
|
239
282
|
: '360px',
|
|
@@ -4006,6 +4049,7 @@ class PraxisCrudComponent {
|
|
|
4006
4049
|
links: this.resolveCrudRuntimeLinks(effectiveAction.action, contextualRow),
|
|
4007
4050
|
resourceIdentity,
|
|
4008
4051
|
contextIdentity,
|
|
4052
|
+
focusOrigin: runtimeEvent?.focusOrigin ?? null,
|
|
4009
4053
|
surfaceRuntime: this.resolveSurfaceRuntime(),
|
|
4010
4054
|
});
|
|
4011
4055
|
this.afterOpen.emit({ mode, action: effectiveAction.action });
|
|
@@ -4467,6 +4511,17 @@ class PraxisCrudComponent {
|
|
|
4467
4511
|
const first = Array.isArray(event?.selectedRows) ? event.selectedRows[0] : null;
|
|
4468
4512
|
return this.isRecord(first) ? first : null;
|
|
4469
4513
|
}
|
|
4514
|
+
/**
|
|
4515
|
+
* Keeps the DOM trigger exclusively in transient runtime context. Discovery
|
|
4516
|
+
* payloads remain JSON-safe and can still be authored, inspected and
|
|
4517
|
+
* persisted independently from the browser that opened the surface.
|
|
4518
|
+
*/
|
|
4519
|
+
resolveRuntimeFocusOrigin(event) {
|
|
4520
|
+
const focusOrigin = event?.focusOrigin;
|
|
4521
|
+
return focusOrigin && typeof focusOrigin.focus === 'function'
|
|
4522
|
+
? focusOrigin
|
|
4523
|
+
: undefined;
|
|
4524
|
+
}
|
|
4470
4525
|
extractDiscoveryActionConfig(event) {
|
|
4471
4526
|
const config = event?.actionConfig;
|
|
4472
4527
|
return this.isRecord(config) && typeof config['resourceKey'] === 'string'
|
|
@@ -4516,6 +4571,7 @@ class PraxisCrudComponent {
|
|
|
4516
4571
|
this.snack.open(this.txWithParams('crud.surface.openFailed', 'Não foi possível abrir {title}. Tente novamente ou confirme se esse recurso continua disponível.', { title: surface.title || surface.id }), undefined, { duration: 4500 });
|
|
4517
4572
|
return true;
|
|
4518
4573
|
}
|
|
4574
|
+
const focusOrigin = this.resolveRuntimeFocusOrigin(runtimeEvent);
|
|
4519
4575
|
let openPromise;
|
|
4520
4576
|
try {
|
|
4521
4577
|
const surfaceContext = {
|
|
@@ -4534,6 +4590,7 @@ class PraxisCrudComponent {
|
|
|
4534
4590
|
action: normalizedAction,
|
|
4535
4591
|
resourcePath,
|
|
4536
4592
|
},
|
|
4593
|
+
...(focusOrigin ? { focusOrigin } : {}),
|
|
4537
4594
|
},
|
|
4538
4595
|
};
|
|
4539
4596
|
const handledInline = await this.surfaceOutlets.tryActivate(payload, surfaceContext);
|
|
@@ -4610,6 +4667,7 @@ class PraxisCrudComponent {
|
|
|
4610
4667
|
}
|
|
4611
4668
|
return false;
|
|
4612
4669
|
}
|
|
4670
|
+
const focusOrigin = this.resolveRuntimeFocusOrigin(runtimeEvent);
|
|
4613
4671
|
let openPromise;
|
|
4614
4672
|
try {
|
|
4615
4673
|
openPromise = Promise.resolve(this.surfaceService.open(payload, {
|
|
@@ -4628,6 +4686,7 @@ class PraxisCrudComponent {
|
|
|
4628
4686
|
action: normalizedAction,
|
|
4629
4687
|
resourcePath,
|
|
4630
4688
|
},
|
|
4689
|
+
...(focusOrigin ? { focusOrigin } : {}),
|
|
4631
4690
|
},
|
|
4632
4691
|
}));
|
|
4633
4692
|
}
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/crud",
|
|
3
|
-
"version": "9.0.4-rc.
|
|
3
|
+
"version": "9.0.4-rc.37",
|
|
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.
|
|
9
|
-
"@praxisui/table": "^9.0.4-rc.
|
|
10
|
-
"@praxisui/core": "^9.0.4-rc.
|
|
11
|
-
"@praxisui/dynamic-fields": "^9.0.4-rc.
|
|
12
|
-
"@praxisui/settings-panel": "^9.0.4-rc.
|
|
8
|
+
"@praxisui/dynamic-form": "^9.0.4-rc.37",
|
|
9
|
+
"@praxisui/table": "^9.0.4-rc.37",
|
|
10
|
+
"@praxisui/core": "^9.0.4-rc.37",
|
|
11
|
+
"@praxisui/dynamic-fields": "^9.0.4-rc.37",
|
|
12
|
+
"@praxisui/settings-panel": "^9.0.4-rc.37",
|
|
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.
|
|
17
|
+
"@praxisui/ai": "^9.0.4-rc.37",
|
|
18
18
|
"rxjs": "~7.8.0"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
package/types/praxisui-crud.d.ts
CHANGED
|
@@ -149,6 +149,8 @@ type CrudActionRuntimeEvent = {
|
|
|
149
149
|
selectedRows?: Record<string, unknown>[];
|
|
150
150
|
rows?: Record<string, unknown>[];
|
|
151
151
|
resourceIdentity?: MaterializedResourceIdentity | null;
|
|
152
|
+
/** Ephemeral focus origin provided by a runtime surface, never persisted. */
|
|
153
|
+
focusOrigin?: HTMLElement;
|
|
152
154
|
};
|
|
153
155
|
type CrudContextAction = {
|
|
154
156
|
action: string;
|
|
@@ -285,6 +287,12 @@ declare class PraxisCrudComponent implements OnChanges {
|
|
|
285
287
|
private extractSelectedRowFromEvent;
|
|
286
288
|
private extractSelectedRowFromBulkAction;
|
|
287
289
|
private extractSelectedRowFromToolbarAction;
|
|
290
|
+
/**
|
|
291
|
+
* Keeps the DOM trigger exclusively in transient runtime context. Discovery
|
|
292
|
+
* payloads remain JSON-safe and can still be authored, inspected and
|
|
293
|
+
* persisted independently from the browser that opened the surface.
|
|
294
|
+
*/
|
|
295
|
+
private resolveRuntimeFocusOrigin;
|
|
288
296
|
private extractDiscoveryActionConfig;
|
|
289
297
|
private resolveSelectedRowForAction;
|
|
290
298
|
private tryOpenDiscoveredCrudSurface;
|
|
@@ -356,6 +364,8 @@ interface CrudLaunchRuntime {
|
|
|
356
364
|
contextIdentity?: MaterializedResourceIdentity | null;
|
|
357
365
|
/** Active runtime drawer session. It is never persisted in CRUD metadata. */
|
|
358
366
|
surfaceRuntime?: SurfaceDrawerRef | null;
|
|
367
|
+
/** Stable runtime trigger for restoring focus after a form overlay closes. */
|
|
368
|
+
focusOrigin?: HTMLElement | null;
|
|
359
369
|
}
|
|
360
370
|
declare class CrudLauncherService {
|
|
361
371
|
private readonly router;
|