@charcuterie/ui 4.18.1 → 4.18.2

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.
@@ -0,0 +1,10 @@
1
+ /** Anchored children share their enclosing modal's dismissal boundary. */
2
+ type AnchoredOverlayScope = {
3
+ topId: string | null;
4
+ register: (id: string) => void;
5
+ unregister: (id: string) => void;
6
+ };
7
+ export declare const AnchoredOverlayScopeContext: import("react").Context<AnchoredOverlayScope | null>;
8
+ export declare function useAnchoredOverlayScope(): AnchoredOverlayScope;
9
+ export {};
10
+ //# sourceMappingURL=AnchoredOverlayScope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnchoredOverlayScope.d.ts","sourceRoot":"","sources":["../../src/Overlay/AnchoredOverlayScope.ts"],"names":[],"mappings":"AAOA,0EAA0E;AAC1E,KAAK,oBAAoB,GAAG;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,CAAA;AACD,eAAO,MAAM,2BAA2B,sDACU,CAAA;AAClD,wBAAgB,uBAAuB,IAAI,oBAAoB,CA0B9D"}
@@ -0,0 +1,15 @@
1
+ import { createContext, useCallback, useMemo, useState, } from "react";
2
+ export const AnchoredOverlayScopeContext = createContext(null);
3
+ export function useAnchoredOverlayScope() {
4
+ const [ids, setIds] = useState([]);
5
+ const register = useCallback((id) => setIds((previous) => previous.includes(id)
6
+ ? previous
7
+ : [...previous, id]), []);
8
+ const unregister = useCallback((id) => setIds((previous) => previous.filter((value) => value !== id)), []);
9
+ return useMemo(() => ({
10
+ topId: ids.at(-1) ?? null,
11
+ register,
12
+ unregister,
13
+ }), [ids, register, unregister]);
14
+ }
15
+ //# sourceMappingURL=AnchoredOverlayScope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnchoredOverlayScope.js","sourceRoot":"","sources":["../../src/Overlay/AnchoredOverlayScope.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,WAAW,EACX,OAAO,EACP,QAAQ,GACT,MAAM,OAAO,CAAA;AAQd,MAAM,CAAC,MAAM,2BAA2B,GACtC,aAAa,CAA8B,IAAI,CAAC,CAAA;AAClD,MAAM,UAAU,uBAAuB;IACrC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAW,EAAE,CAAC,CAAA;IAC5C,MAAM,QAAQ,GAAG,WAAW,CAC1B,CAAC,EAAU,EAAE,EAAE,CACb,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAClB,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnB,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC,CAAC,GAAG,QAAQ,EAAE,EAAE,CAAC,CACtB,EACH,EAAE,CACH,CAAA;IACD,MAAM,UAAU,GAAG,WAAW,CAC5B,CAAC,EAAU,EAAE,EAAE,CACb,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAClB,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC,CACzC,EACH,EAAE,CACH,CAAA;IACD,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,CAAC;QACL,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI;QACzB,QAAQ;QACR,UAAU;KACX,CAAC,EACF,CAAC,GAAG,EAAE,QAAQ,EAAE,UAAU,CAAC,CAC5B,CAAA;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"OverlayPanel.d.ts","sourceRoot":"","sources":["../../src/Overlay/OverlayPanel.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAOjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;IAC5C,8CAA8C;IAC9C,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,IAAI,CAAC,EAAE,aAAa,GAAG,QAAQ,CAAA;CAChC,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,6IAU1B,iBAAiB,KAAG,SAuLtB,CAAA"}
1
+ {"version":3,"file":"OverlayPanel.d.ts","sourceRoot":"","sources":["../../src/Overlay/OverlayPanel.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAWjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;IAC5C,8CAA8C;IAC9C,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,IAAI,CAAC,EAAE,aAAa,GAAG,QAAQ,CAAA;CAChC,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,6IAU1B,iBAAiB,KAAG,SA8LtB,CAAA"}
@@ -2,6 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { useLatestRef, useUniqueId, } from "@charcuterie/logic";
3
3
  import { FloatingFocusManager, FloatingPortal, useDismiss, useFloating, useInteractions, useRole, } from "@floating-ui/react";
4
4
  import { useCallback, useEffect } from "react";
5
+ import { AnchoredOverlayScopeContext, useAnchoredOverlayScope, } from "./AnchoredOverlayScope.js";
5
6
  import { lockScrollBehind } from "./lockScrollBehind.js";
6
7
  import { useOverlayStack } from "./OverlayStack.js";
7
8
  import { SharedBackdrop } from "./SharedBackdrop.js";
@@ -15,6 +16,8 @@ export const OverlayPanel = ({ "aria-label": ariaLabel, "aria-labelledby": ariaL
15
16
  onCloseRef.current();
16
17
  }, [onCloseRef]);
17
18
  const isTop = !stack.isProvided || stack.topId === id;
19
+ const anchors = useAnchoredOverlayScope();
20
+ const hasOpenAnchor = anchors.topId !== null;
18
21
  const { context, refs } = useFloating({
19
22
  open: isVisible,
20
23
  onOpenChange: (isNextVisible) => {
@@ -25,13 +28,13 @@ export const OverlayPanel = ({ "aria-label": ariaLabel, "aria-labelledby": ariaL
25
28
  });
26
29
  const { getFloatingProps } = useInteractions([
27
30
  useDismiss(context, {
28
- escapeKey: isDismissable && isTop,
31
+ escapeKey: isDismissable && isTop && !hasOpenAnchor,
29
32
  // The top's outside press is the backdrop click. A per-panel
30
33
  // `outsidePress` on a *lower* modal would close it too, so it is
31
34
  // scoped to the top — which is also why the scrim needs no click
32
35
  // handler of its own.
33
36
  outsidePress: (event) => {
34
- if (!isDismissable || !isTop)
37
+ if (!isDismissable || !isTop || hasOpenAnchor)
35
38
  return false;
36
39
  /*
37
40
  * ⚠️ A press on a toast is NOT an outside press.
@@ -123,6 +126,6 @@ export const OverlayPanel = ({ "aria-label": ariaLabel, "aria-labelledby": ariaL
123
126
  // but the top, so a lower modal is unreachable across the
124
127
  // portal boundary even where `FloatingFocusManager`'s
125
128
  // outside-hiding does not reach a sibling portal.
126
- inert: isTop ? undefined : true, ref: refs.setFloating, role: role, children: children }) }) }) })] }));
129
+ inert: isTop ? undefined : true, ref: refs.setFloating, role: role, children: _jsx(AnchoredOverlayScopeContext.Provider, { value: anchors, children: children }) }) }) }) })] }));
127
130
  };
128
131
  //# sourceMappingURL=OverlayPanel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"OverlayPanel.js","sourceRoot":"","sources":["../../src/Overlay/OverlayPanel.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OverlayPanel.js","sourceRoot":"","sources":["../../src/Overlay/OverlayPanel.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAnchoredOverlay.d.ts","sourceRoot":"","sources":["../../src/Overlay/useAnchoredOverlay.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,cAAc,EACf,MAAM,oBAAoB,CAAA;AAY3B,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,MAAM,mBAAmB,GAC3B,QAAQ,GACR,SAAS,GACT,MAAM,CAAA;AAEV;;;;;;;;;;GAUG;AACH,MAAM,MAAM,qBAAqB,GAC7B,WAAW,GACX,cAAc,CAAA;AAElB,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAA;IACnD;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,yBAAyB,CAAC,EAAE,OAAO,CAAA;IACnC,+DAA+D;IAC/D,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,SAAS,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,IAAI,EAAE,mBAAmB,CAAA;IACzB;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB,CAAA;AAQD,eAAO,MAAM,kBAAkB,GAAI,8MAehC,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;CA4L3B,CAAA"}
1
+ {"version":3,"file":"useAnchoredOverlay.d.ts","sourceRoot":"","sources":["../../src/Overlay/useAnchoredOverlay.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,cAAc,EACf,MAAM,oBAAoB,CAAA;AAY3B,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAQpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,MAAM,mBAAmB,GAC3B,QAAQ,GACR,SAAS,GACT,MAAM,CAAA;AAEV;;;;;;;;;;GAUG;AACH,MAAM,MAAM,qBAAqB,GAC7B,WAAW,GACX,cAAc,CAAA;AAElB,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAA;IACnD;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,yBAAyB,CAAC,EAAE,OAAO,CAAA;IACnC,+DAA+D;IAC/D,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,SAAS,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,IAAI,EAAE,mBAAmB,CAAA;IACzB;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB,CAAA;AAQD,eAAO,MAAM,kBAAkB,GAAI,8MAehC,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;CAyM3B,CAAA"}
@@ -1,12 +1,24 @@
1
1
  import { useClonedChild, useUniqueId, } from "@charcuterie/logic";
2
2
  import { autoUpdate, flip, offset, shift, size, useDismiss, useFloating, useInteractions, useRole, } from "@floating-ui/react";
3
- import { createElement, useLayoutEffect } from "react";
3
+ import { createElement, useContext, useLayoutEffect, } from "react";
4
+ import { AnchoredOverlayScopeContext } from "./AnchoredOverlayScope.js";
4
5
  // `useClonedChild` insists on exactly one element, and hooks cannot be
5
6
  // called conditionally — so in `anchorRef` mode we still call it, on this
6
7
  // throwaway, and discard the result. It is never rendered and never
7
8
  // receives the floating reference (that goes to `anchorRef`).
8
9
  const PLACEHOLDER_TRIGGER = createElement("span");
9
10
  export const useAnchoredOverlay = ({ anchorRef, isEscapeDismissable = true, isHeightClamped = false, isOutsidePressDismissable = true, isTriggerWidthMatched = false, isVisible, isWidthClamped = false, maxHeightPx, maxWidthPx, offsetValue = 8, onDismiss, placement = "bottom-start", role, trigger, }) => {
11
+ const scope = useContext(AnchoredOverlayScopeContext);
12
+ const anchorId = useUniqueId();
13
+ const register = scope?.register;
14
+ const unregister = scope?.unregister;
15
+ useLayoutEffect(() => {
16
+ if (!isVisible || !register || !unregister)
17
+ return;
18
+ register(anchorId);
19
+ return () => unregister(anchorId);
20
+ }, [isVisible, anchorId, register, unregister]);
21
+ const isTopAnchor = scope === null || scope.topId === anchorId;
10
22
  const middleware = [
11
23
  offset(offsetValue),
12
24
  flip(),
@@ -58,8 +70,8 @@ export const useAnchoredOverlay = ({ anchorRef, isEscapeDismissable = true, isHe
58
70
  // decision raised and this answers.
59
71
  const { getFloatingProps, getReferenceProps } = useInteractions([
60
72
  useDismiss(context, {
61
- escapeKey: isEscapeDismissable,
62
- outsidePress: isOutsidePressDismissable,
73
+ escapeKey: isEscapeDismissable && isTopAnchor,
74
+ outsidePress: isOutsidePressDismissable && isTopAnchor,
63
75
  }),
64
76
  useRole(context, { role }),
65
77
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"useAnchoredOverlay.js","sourceRoot":"","sources":["../../src/Overlay/useAnchoredOverlay.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useAnchoredOverlay.js","sourceRoot":"","sources":["../../src/Overlay/useAnchoredOverlay.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@charcuterie/ui",
3
- "version": "4.18.1",
3
+ "version": "4.18.2",
4
4
  "description": "The Charcuterie components. Tokens for look, @charcuterie/logic for state, ARIA that agents can actually drive.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -54,6 +54,15 @@ differential — the dialog that ignores Escape is then closed by its own button
54
54
 
55
55
  <Canvas of={DialogStories.AllStates} />
56
56
 
57
+ ## Pickers inside a draft
58
+
59
+ An open picker, menu, or popover owns the first Escape or outside press inside a
60
+ modal. Dismissing that child keeps the enclosing dialog and its unsaved fields open.
61
+ After the child closes, Escape dismisses the dialog normally. Selecting a picker
62
+ option also keeps the draft open and returns focus to its trigger.
63
+
64
+ <Canvas of={DialogStories.PickerDraft} />
65
+
57
66
  ## Stacking, and the one shared scrim
58
67
 
59
68
  A lone dialog needs no setup. Stack one over another and an `OverlayStackProvider` at the
@@ -2,12 +2,13 @@ import { useVisibility } from "@charcuterie/logic"
2
2
  import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters"
3
3
  import type { Meta, StoryObj } from "@storybook/react"
4
4
  import type { ReactNode } from "react"
5
- import { useRef } from "react"
6
-
5
+ import { useRef, useState } from "react"
7
6
  import { toStoryChoice } from "../argTypes.storyHelpers.ts"
8
7
  import { Button } from "../Button/Button.tsx"
9
8
  import { StoryRow } from "../board.storyHelpers.tsx"
9
+ import { Field } from "../Field/Field.tsx"
10
10
  import { OverlayStackProvider } from "../Overlay/OverlayStack.tsx"
11
+ import { Picker } from "../Picker/Picker.tsx"
11
12
  import type { DialogSize } from "./Dialog.tsx"
12
13
  import { Dialog } from "./Dialog.tsx"
13
14
 
@@ -445,3 +446,51 @@ export const Interactive: Story = {
445
446
  />
446
447
  ),
447
448
  }
449
+
450
+ const PickerDraftDemo = () => {
451
+ const [isVisible, setIsVisible] = useState(true)
452
+ const [draft, setDraft] = useState(
453
+ "Keep the unsaved draft",
454
+ )
455
+ const [value, setValue] = useState("")
456
+ return (
457
+ <div className="min-h-screen">
458
+ <Button onClick={() => setIsVisible(true)}>
459
+ Open draft
460
+ </Button>
461
+ <Dialog
462
+ heading="Edit a draft"
463
+ isVisible={isVisible}
464
+ onClose={() => setIsVisible(false)}
465
+ >
466
+ <Field label="Draft name">
467
+ <input
468
+ value={draft}
469
+ onChange={(event) =>
470
+ setDraft(event.currentTarget.value)
471
+ }
472
+ />
473
+ </Field>
474
+ <Picker
475
+ label="Draft parent"
476
+ value={value}
477
+ options={[
478
+ { value: "", label: "No parent" },
479
+ { value: "other", label: "Another draft" },
480
+ ]}
481
+ onChange={setValue}
482
+ />
483
+ </Dialog>
484
+ </div>
485
+ )
486
+ }
487
+
488
+ /** Escape dismisses the picker's portal before its enclosing dialog. */
489
+ export const PickerDraft: Story = {
490
+ args: {
491
+ heading: "Edit a draft",
492
+ isVisible: true,
493
+ onClose: () => {},
494
+ },
495
+ render: () => <PickerDraftDemo />,
496
+ }
@@ -11,6 +11,7 @@ import {
11
11
  import * as stories from "./Dialog.stories.tsx"
12
12
 
13
13
  const {
14
+ PickerDraft,
14
15
  AllStates,
15
16
  AllVariants,
16
17
  Playground,
@@ -368,3 +369,61 @@ test("initialFocus puts the caret in the field, not on Close", async () => {
368
369
 
369
370
  await expect(field).toHaveValue("Sharpen the chisels")
370
371
  })
372
+
373
+ test("a picker cancels its selection without closing the enclosing draft", async () => {
374
+ const { body } = await mountStory(PickerDraft)
375
+ const input = expectAgentDrivable(body, {
376
+ role: "textbox",
377
+ name: "Draft name",
378
+ })
379
+ await userEvent.clear(input)
380
+ await userEvent.type(input, "An unsaved change")
381
+ const trigger = body.getByRole("button", {
382
+ name: "Draft parent: No parent",
383
+ })
384
+ await userEvent.click(trigger)
385
+ const option = body.getByRole("option", {
386
+ name: "No parent",
387
+ })
388
+ await expect(option).toHaveFocus()
389
+ await userEvent.keyboard("{Escape}")
390
+ await expect(
391
+ body.getByRole("dialog", { name: "Edit a draft" }),
392
+ ).toBeVisible()
393
+ await expect(input).toHaveValue("An unsaved change")
394
+ await expect(trigger).toHaveFocus()
395
+ await expect(
396
+ body.queryByRole("listbox"),
397
+ ).not.toBeInTheDocument()
398
+ // A press elsewhere in the draft also dismisses only the picker.
399
+ await userEvent.click(trigger)
400
+ await expect(
401
+ body.getByRole("option", { name: "No parent" }),
402
+ ).toHaveFocus()
403
+ await userEvent.click(input)
404
+ await expect(
405
+ body.queryByRole("listbox"),
406
+ ).not.toBeInTheDocument()
407
+ await expect(
408
+ body.getByRole("dialog", { name: "Edit a draft" }),
409
+ ).toBeVisible()
410
+ await expect(input).toHaveValue("An unsaved change")
411
+ await userEvent.click(trigger)
412
+ await userEvent.click(
413
+ body.getByRole("option", { name: "Another draft" }),
414
+ )
415
+ await expect(
416
+ body.getByRole("dialog", { name: "Edit a draft" }),
417
+ ).toBeVisible()
418
+ await expect(
419
+ body.getByRole("button", {
420
+ name: "Draft parent: Another draft",
421
+ }),
422
+ ).toHaveFocus()
423
+ await userEvent.keyboard("{Escape}")
424
+ await waitFor(() =>
425
+ expect(
426
+ body.queryByRole("dialog", { name: "Edit a draft" }),
427
+ ).not.toBeInTheDocument(),
428
+ )
429
+ })
@@ -0,0 +1,42 @@
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useMemo,
5
+ useState,
6
+ } from "react"
7
+
8
+ /** Anchored children share their enclosing modal's dismissal boundary. */
9
+ type AnchoredOverlayScope = {
10
+ topId: string | null
11
+ register: (id: string) => void
12
+ unregister: (id: string) => void
13
+ }
14
+ export const AnchoredOverlayScopeContext =
15
+ createContext<AnchoredOverlayScope | null>(null)
16
+ export function useAnchoredOverlayScope(): AnchoredOverlayScope {
17
+ const [ids, setIds] = useState<string[]>([])
18
+ const register = useCallback(
19
+ (id: string) =>
20
+ setIds((previous) =>
21
+ previous.includes(id)
22
+ ? previous
23
+ : [...previous, id],
24
+ ),
25
+ [],
26
+ )
27
+ const unregister = useCallback(
28
+ (id: string) =>
29
+ setIds((previous) =>
30
+ previous.filter((value) => value !== id),
31
+ ),
32
+ [],
33
+ )
34
+ return useMemo(
35
+ () => ({
36
+ topId: ids.at(-1) ?? null,
37
+ register,
38
+ unregister,
39
+ }),
40
+ [ids, register, unregister],
41
+ )
42
+ }
@@ -13,6 +13,10 @@ import {
13
13
  import type { ReactNode, RefObject } from "react"
14
14
  import { useCallback, useEffect } from "react"
15
15
 
16
+ import {
17
+ AnchoredOverlayScopeContext,
18
+ useAnchoredOverlayScope,
19
+ } from "./AnchoredOverlayScope.ts"
16
20
  import { lockScrollBehind } from "./lockScrollBehind.ts"
17
21
  import { useOverlayStack } from "./OverlayStack.tsx"
18
22
  import { SharedBackdrop } from "./SharedBackdrop.tsx"
@@ -100,6 +104,8 @@ export const OverlayPanel = ({
100
104
  }, [onCloseRef])
101
105
 
102
106
  const isTop = !stack.isProvided || stack.topId === id
107
+ const anchors = useAnchoredOverlayScope()
108
+ const hasOpenAnchor = anchors.topId !== null
103
109
 
104
110
  const { context, refs } = useFloating({
105
111
  open: isVisible,
@@ -112,13 +118,14 @@ export const OverlayPanel = ({
112
118
 
113
119
  const { getFloatingProps } = useInteractions([
114
120
  useDismiss(context, {
115
- escapeKey: isDismissable && isTop,
121
+ escapeKey: isDismissable && isTop && !hasOpenAnchor,
116
122
  // The top's outside press is the backdrop click. A per-panel
117
123
  // `outsidePress` on a *lower* modal would close it too, so it is
118
124
  // scoped to the top — which is also why the scrim needs no click
119
125
  // handler of its own.
120
126
  outsidePress: (event) => {
121
- if (!isDismissable || !isTop) return false
127
+ if (!isDismissable || !isTop || hasOpenAnchor)
128
+ return false
122
129
  /*
123
130
  * ⚠️ A press on a toast is NOT an outside press.
124
131
  *
@@ -262,7 +269,11 @@ export const OverlayPanel = ({
262
269
  ref={refs.setFloating}
263
270
  role={role}
264
271
  >
265
- {children}
272
+ <AnchoredOverlayScopeContext.Provider
273
+ value={anchors}
274
+ >
275
+ {children}
276
+ </AnchoredOverlayScopeContext.Provider>
266
277
  </div>
267
278
  </FloatingFocusManager>
268
279
  </div>
@@ -18,7 +18,12 @@ import {
18
18
  useRole,
19
19
  } from "@floating-ui/react"
20
20
  import type { ReactElement, RefObject } from "react"
21
- import { createElement, useLayoutEffect } from "react"
21
+ import {
22
+ createElement,
23
+ useContext,
24
+ useLayoutEffect,
25
+ } from "react"
26
+ import { AnchoredOverlayScopeContext } from "./AnchoredOverlayScope.ts"
22
27
 
23
28
  /**
24
29
  * The floating-ui block every anchored overlay — `Popover`, `Menu`,
@@ -178,6 +183,18 @@ export const useAnchoredOverlay = ({
178
183
  role,
179
184
  trigger,
180
185
  }: UseAnchoredOverlayOptions) => {
186
+ const scope = useContext(AnchoredOverlayScopeContext)
187
+ const anchorId = useUniqueId()
188
+ const register = scope?.register
189
+ const unregister = scope?.unregister
190
+ useLayoutEffect(() => {
191
+ if (!isVisible || !register || !unregister) return
192
+ register(anchorId)
193
+ return () => unregister(anchorId)
194
+ }, [isVisible, anchorId, register, unregister])
195
+ const isTopAnchor =
196
+ scope === null || scope.topId === anchorId
197
+
181
198
  const middleware = [
182
199
  offset(offsetValue),
183
200
  flip(),
@@ -249,8 +266,9 @@ export const useAnchoredOverlay = ({
249
266
  const { getFloatingProps, getReferenceProps } =
250
267
  useInteractions([
251
268
  useDismiss(context, {
252
- escapeKey: isEscapeDismissable,
253
- outsidePress: isOutsidePressDismissable,
269
+ escapeKey: isEscapeDismissable && isTopAnchor,
270
+ outsidePress:
271
+ isOutsidePressDismissable && isTopAnchor,
254
272
  }),
255
273
  useRole(context, { role }),
256
274
  ])