@domicile-desktop/component-library 0.0.0-alpha-321ecd608c5d → 0.0.0-alpha-48d12a220b2e

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
@@ -2,8 +2,8 @@
2
2
  "dependencies": {
3
3
  "@base-ui/react": "^1.8.0",
4
4
  "@cprussin/option-result": "^2.0.1",
5
- "@domicile-desktop/sdk": "0.0.0-alpha-321ecd608c5d",
6
- "@domicile-desktop/system-apps": "0.0.0-alpha-321ecd608c5d",
5
+ "@domicile-desktop/sdk": "0.0.0-alpha-48d12a220b2e",
6
+ "@domicile-desktop/system-apps": "0.0.0-alpha-48d12a220b2e",
7
7
  "@phosphor-icons/react": "^2.1.10"
8
8
  },
9
9
  "description": "React components and the Panda CSS design system that Domicile's shells are built from.",
@@ -104,5 +104,5 @@
104
104
  "test:unit": "bun test"
105
105
  },
106
106
  "type": "module",
107
- "version": "0.0.0-alpha-321ecd608c5d"
107
+ "version": "0.0.0-alpha-48d12a220b2e"
108
108
  }
@@ -1,6 +1,11 @@
1
1
  import { Dialog as BaseDialog } from "@base-ui/react/dialog";
2
2
  import { XIcon } from "@phosphor-icons/react/dist/ssr/X";
3
- import type { ComponentProps, ReactElement, ReactNode } from "react";
3
+ import type {
4
+ ComponentProps,
5
+ KeyboardEvent,
6
+ ReactElement,
7
+ ReactNode,
8
+ } from "react";
4
9
  import { css, cva } from "../../styled-system/css";
5
10
  import { center, flex, hstack } from "../../styled-system/patterns";
6
11
  import { Button } from "../Button/Button";
@@ -105,6 +110,7 @@ const ModalDialogComponent = ({
105
110
  data-placement={placement}
106
111
  data-size={size}
107
112
  data-surface={surface}
113
+ onKeyDown={moveFooterFocus}
108
114
  >
109
115
  {title !== undefined && (
110
116
  <header
@@ -324,3 +330,45 @@ const footerStyles = hstack({
324
330
  paddingBlockStart: 3,
325
331
  paddingInline: 4,
326
332
  });
333
+
334
+ /**
335
+ * Moves focus between the footer's enabled buttons on Left, Right, Home and
336
+ * End, when one of them has focus. Left and Right wrap. Focus only moves, so
337
+ * no key here answers a dialog.
338
+ */
339
+ const moveFooterFocus = (event: KeyboardEvent<HTMLElement>) => {
340
+ const buttons = [
341
+ ...event.currentTarget.querySelectorAll<HTMLButtonElement>(
342
+ ":scope > footer button:not(:disabled)",
343
+ ),
344
+ ];
345
+ const targets: readonly EventTarget[] = buttons;
346
+ const index = targets.indexOf(event.target);
347
+ if (index !== -1) {
348
+ const target = footerKeyTarget(event.key, buttons, index);
349
+ if (target !== undefined) {
350
+ event.preventDefault();
351
+ target.focus();
352
+ }
353
+ }
354
+ };
355
+
356
+ /** The button `key` moves to from `buttons[index]`, or `undefined` for other keys. */
357
+ const footerKeyTarget = (
358
+ key: string,
359
+ buttons: HTMLButtonElement[],
360
+ index: number,
361
+ ): HTMLButtonElement | undefined => {
362
+ switch (key) {
363
+ case "ArrowLeft":
364
+ return buttons.at(index - 1);
365
+ case "ArrowRight":
366
+ return buttons.at((index + 1) % buttons.length);
367
+ case "Home":
368
+ return buttons.at(0);
369
+ case "End":
370
+ return buttons.at(-1);
371
+ default:
372
+ return undefined;
373
+ }
374
+ };