@remit/web-client 0.0.19 → 0.0.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/web-client",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "type": "module",
5
5
  "description": "Remit web client, published as composable primitives — the app shell, auth shells, and runtime config. A distributor imports what it composes and bundles it.",
6
6
  "exports": {
@@ -232,6 +232,12 @@ export const MessageList = ({
232
232
  // leave this null, so the list never yanks focus out of the reading pane.
233
233
  const pendingDomFocusRef = useRef<string | null>(null);
234
234
 
235
+ // The row the cursor was on when the delete confirmation opened. The dialog
236
+ // takes DOM focus for as long as it is up, so dismissing it has to give that
237
+ // focus back or the list is left with no cursor and the next shortcut acts on
238
+ // nothing (#80).
239
+ const focusBeforeConfirmRef = useRef<string | null>(null);
240
+
235
241
  // Whether the list can serve keyboard commands at all. It stays true while
236
242
  // the delete confirmation is open — withdrawing the commands there would let
237
243
  // the route fall through to its own unconfirmed delete on a second Delete
@@ -341,9 +347,10 @@ export const MessageList = ({
341
347
  const requestDelete = useCallback(
342
348
  (ids: string[]) => {
343
349
  if (!onDeleteMessages || ids.length === 0) return;
350
+ focusBeforeConfirmRef.current = focusedMessageId ?? null;
344
351
  setPendingDeleteIds(ids);
345
352
  },
346
- [onDeleteMessages],
353
+ [onDeleteMessages, focusedMessageId],
347
354
  );
348
355
 
349
356
  // Keyboard shift-arrow range extend: move focus one row in `direction` and
@@ -452,9 +459,14 @@ export const MessageList = ({
452
459
 
453
460
  onDeleteMessages?.(pendingDeleteIds);
454
461
  clearSelection();
462
+ focusBeforeConfirmRef.current = null;
455
463
  setPendingDeleteIds(null);
456
464
 
457
465
  if (nextFocus !== undefined) {
466
+ // Same hand-back as cancelling, aimed at the surviving neighbour
467
+ // instead: confirming also closes a dialog that held DOM focus.
468
+ pendingDomFocusRef.current = nextFocus;
469
+ setFocusedMessageId(nextFocus);
458
470
  navigate({
459
471
  to: "/mail/$mailboxId",
460
472
  params: { mailboxId },
@@ -471,8 +483,17 @@ export const MessageList = ({
471
483
  mailboxId,
472
484
  ]);
473
485
 
486
+ // Every way out of the confirmation that isn't the delete — Escape, Cancel,
487
+ // the backdrop — arrives here, so this is the one place the keyboard has to
488
+ // be handed back. Restoring the cursor also puts DOM focus back on that row,
489
+ // via the same pending-focus channel j/k use.
474
490
  const handleCancelDelete = useCallback(() => {
491
+ const restoreTo = focusBeforeConfirmRef.current;
492
+ focusBeforeConfirmRef.current = null;
475
493
  setPendingDeleteIds(null);
494
+ if (restoreTo === null) return;
495
+ pendingDomFocusRef.current = restoreTo;
496
+ setFocusedMessageId(restoreTo);
476
497
  }, []);
477
498
 
478
499
  // Handle mark as read