@praxisui/dialog 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-08T10:35:56.871Z",
3
+ "generatedAt": "2026-08-09T11:18:35.694Z",
4
4
  "packageName": "@praxisui/dialog",
5
- "packageVersion": "9.0.4-rc.36",
5
+ "packageVersion": "9.0.4-rc.37",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 1,
@@ -1270,6 +1270,68 @@ function resolveParentSurfaceRuntime(context) {
1270
1270
  ? runtime
1271
1271
  : null;
1272
1272
  }
1273
+ /**
1274
+ * The action origin is runtime-only: persisted surface payloads remain JSON
1275
+ * safe, while the visual host can return keyboard focus to the invoking
1276
+ * control after the surface terminates.
1277
+ */
1278
+ function resolveRuntimeFocusOrigin(context) {
1279
+ const candidate = context?.runtime
1280
+ ?.focusOrigin;
1281
+ return candidate
1282
+ && typeof candidate.focus === 'function'
1283
+ && (candidate.isConnected || !!candidate.getAttribute('data-praxis-focus-key'))
1284
+ ? candidate
1285
+ : null;
1286
+ }
1287
+ function resolveReplacementFocusOrigin(focusOrigin) {
1288
+ if (focusOrigin.isConnected) {
1289
+ return focusOrigin;
1290
+ }
1291
+ const key = focusOrigin.getAttribute('data-praxis-focus-key');
1292
+ if (!key || typeof document === 'undefined') {
1293
+ return null;
1294
+ }
1295
+ return Array.from(document.querySelectorAll('[data-praxis-focus-key]'))
1296
+ .find((candidate) => candidate.getAttribute('data-praxis-focus-key') === key)
1297
+ ?? null;
1298
+ }
1299
+ function restoreRuntimeFocusOnClose(closed$, focusOrigin) {
1300
+ if (!focusOrigin || typeof closed$?.subscribe !== 'function')
1301
+ return;
1302
+ closed$.subscribe(() => {
1303
+ const restore = (remainingAttempts) => {
1304
+ const target = resolveReplacementFocusOrigin(focusOrigin);
1305
+ const activeElement = typeof document !== 'undefined' ? document.activeElement : null;
1306
+ const focusWasLostToDocument = activeElement === document.body
1307
+ || activeElement === document.documentElement;
1308
+ const targetAlreadyFocused = activeElement === target;
1309
+ if (target && (focusWasLostToDocument || targetAlreadyFocused)) {
1310
+ try {
1311
+ target.focus({ preventScroll: true });
1312
+ }
1313
+ catch {
1314
+ try {
1315
+ target.focus();
1316
+ }
1317
+ catch { }
1318
+ }
1319
+ }
1320
+ if (remainingAttempts > 0
1321
+ && typeof setTimeout !== 'undefined'
1322
+ // A late overlay teardown can return focus to document.body after the
1323
+ // first restore. Keep checking only while focus remains unowned (or
1324
+ // on the restored target); never steal a user-initiated new focus.
1325
+ && (focusWasLostToDocument || targetAlreadyFocused)) {
1326
+ setTimeout(() => restore(remainingAttempts - 1), 50);
1327
+ }
1328
+ };
1329
+ // The host can recreate a responsive control only after its close and
1330
+ // change-detection queue settles. Three seconds is the bounded recovery
1331
+ // window; the loop stops immediately if the user focuses elsewhere.
1332
+ queueMicrotask(() => restore(60));
1333
+ });
1334
+ }
1273
1335
  function providePraxisSurfaceGlobalActions() {
1274
1336
  return {
1275
1337
  provide: GLOBAL_SURFACE_SERVICE,
@@ -1283,6 +1345,7 @@ function providePraxisSurfaceGlobalActions() {
1283
1345
  return {
1284
1346
  open: async (payload, context) => {
1285
1347
  const materializedPayload = await materializer.materialize(payload, context);
1348
+ const focusOrigin = resolveRuntimeFocusOrigin(context);
1286
1349
  if (materializedPayload.presentation === 'drawer') {
1287
1350
  if (!surfaceDrawer) {
1288
1351
  throw new Error('Drawer surface service not available');
@@ -1292,6 +1355,7 @@ function providePraxisSurfaceGlobalActions() {
1292
1355
  title: materializedPayload.title || materializedPayload.widget.id,
1293
1356
  titleIcon: materializedPayload.icon,
1294
1357
  subtitle: materializedPayload.subtitle,
1358
+ ...(focusOrigin ? { returnFocusTo: focusOrigin } : {}),
1295
1359
  content: {
1296
1360
  component: PraxisSurfaceHostComponent,
1297
1361
  inputs: buildHostInputs(materializedPayload, context, {
@@ -1334,9 +1398,11 @@ function providePraxisSurfaceGlobalActions() {
1334
1398
  minHeight: materializedPayload.size?.minHeight,
1335
1399
  maxHeight: materializedPayload.size?.maxHeight,
1336
1400
  });
1401
+ restoreRuntimeFocusOnClose(ref.closed$, focusOrigin);
1337
1402
  return ref;
1338
1403
  }
1339
1404
  const ref = dialog.open(PraxisSurfaceHostComponent, buildDialogConfig(materializedPayload));
1405
+ restoreRuntimeFocusOnClose(ref.afterClosed(), focusOrigin);
1340
1406
  const resultSubject = new Subject();
1341
1407
  let terminalOutcomeEmitted = false;
1342
1408
  const complete = (outcome) => {
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@praxisui/dialog",
3
- "version": "9.0.4-rc.36",
3
+ "version": "9.0.4-rc.37",
4
4
  "description": "Dialog helpers and components for Praxis UI with Angular Material integration.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
7
7
  "@angular/core": "^21.0.0",
8
8
  "@angular/cdk": "^21.0.0",
9
9
  "@angular/forms": "^21.0.0",
10
- "@praxisui/core": "^9.0.4-rc.36",
10
+ "@praxisui/core": "^9.0.4-rc.37",
11
11
  "@angular/material": "^21.0.0",
12
12
  "@angular/platform-browser": "^21.0.0",
13
13
  "rxjs": "~7.8.0"