@flipdish/ui-library 0.43.1 → 0.44.0
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/dist/components/organisms/SidePanel/components/SidePanelRegion.cjs +1 -1
- package/dist/components/organisms/SidePanel/components/SidePanelRegion.js +1 -1
- package/dist/components/organisms/SidebarNavigation/hooks/useRailBounds.cjs +48 -0
- package/dist/components/organisms/SidebarNavigation/hooks/useRailBounds.cjs.map +1 -0
- package/dist/components/organisms/SidebarNavigation/hooks/useRailBounds.d.ts +19 -0
- package/dist/components/organisms/SidebarNavigation/hooks/useRailBounds.d.ts.map +1 -0
- package/dist/components/organisms/SidebarNavigation/hooks/useRailBounds.js +46 -0
- package/dist/components/organisms/SidebarNavigation/hooks/useRailBounds.js.map +1 -0
- package/dist/components/organisms/SidebarNavigation/index.cjs +10 -5
- package/dist/components/organisms/SidebarNavigation/index.cjs.map +1 -1
- package/dist/components/organisms/SidebarNavigation/index.d.ts.map +1 -1
- package/dist/components/organisms/SidebarNavigation/index.js +10 -5
- package/dist/components/organisms/SidebarNavigation/index.js.map +1 -1
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.cjs +27 -9
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.cjs.map +1 -1
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.d.ts +5 -4
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.d.ts.map +1 -1
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.js +28 -10
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.js.map +1 -1
- package/dist/components/organisms/SlideoutMenu/index.cjs +2 -1
- package/dist/components/organisms/SlideoutMenu/index.cjs.map +1 -1
- package/dist/components/organisms/SlideoutMenu/index.d.ts +12 -2
- package/dist/components/organisms/SlideoutMenu/index.d.ts.map +1 -1
- package/dist/components/organisms/SlideoutMenu/index.js +2 -1
- package/dist/components/organisms/SlideoutMenu/index.js.map +1 -1
- package/dist/components/organisms/SlideoutMenu/providers/PageArea.context.cjs +14 -0
- package/dist/components/organisms/SlideoutMenu/providers/PageArea.context.cjs.map +1 -0
- package/dist/components/organisms/SlideoutMenu/providers/PageArea.context.d.ts +9 -0
- package/dist/components/organisms/SlideoutMenu/providers/PageArea.context.d.ts.map +1 -0
- package/dist/components/organisms/SlideoutMenu/providers/PageArea.context.js +12 -0
- package/dist/components/organisms/SlideoutMenu/providers/PageArea.context.js.map +1 -0
- package/dist/components/templates/AppShell/index.cjs +11 -7
- package/dist/components/templates/AppShell/index.cjs.map +1 -1
- package/dist/components/templates/AppShell/index.d.ts +2 -1
- package/dist/components/templates/AppShell/index.d.ts.map +1 -1
- package/dist/components/templates/AppShell/index.js +11 -7
- package/dist/components/templates/AppShell/index.js.map +1 -1
- package/dist/{components/organisms/SidePanel/hooks → hooks/__internal__/usePanelFocus}/usePanelFocus.cjs +56 -8
- package/dist/hooks/__internal__/usePanelFocus/usePanelFocus.cjs.map +1 -0
- package/dist/{components/organisms/SidePanel/hooks → hooks/__internal__/usePanelFocus}/usePanelFocus.d.ts +12 -3
- package/dist/hooks/__internal__/usePanelFocus/usePanelFocus.d.ts.map +1 -0
- package/dist/{components/organisms/SidePanel/hooks → hooks/__internal__/usePanelFocus}/usePanelFocus.js +56 -8
- package/dist/hooks/__internal__/usePanelFocus/usePanelFocus.js.map +1 -0
- package/dist/providers/ThemeProvider/index.cjs +8 -0
- package/dist/providers/ThemeProvider/index.cjs.map +1 -1
- package/dist/providers/ThemeProvider/index.d.ts +7 -0
- package/dist/providers/ThemeProvider/index.d.ts.map +1 -1
- package/dist/providers/ThemeProvider/index.js +8 -1
- package/dist/providers/ThemeProvider/index.js.map +1 -1
- package/dist/utilities/__internal__/inertUtils/inert.utils.cjs +46 -0
- package/dist/utilities/__internal__/inertUtils/inert.utils.cjs.map +1 -0
- package/dist/utilities/__internal__/inertUtils/inert.utils.d.ts +18 -0
- package/dist/utilities/__internal__/inertUtils/inert.utils.d.ts.map +1 -0
- package/dist/utilities/__internal__/inertUtils/inert.utils.js +43 -0
- package/dist/utilities/__internal__/inertUtils/inert.utils.js.map +1 -0
- package/dist/utilities/__internal__/viewportUtils/viewport.utils.cjs +37 -0
- package/dist/utilities/__internal__/viewportUtils/viewport.utils.cjs.map +1 -0
- package/dist/utilities/__internal__/viewportUtils/viewport.utils.d.ts +17 -0
- package/dist/utilities/__internal__/viewportUtils/viewport.utils.d.ts.map +1 -0
- package/dist/utilities/__internal__/viewportUtils/viewport.utils.js +34 -0
- package/dist/utilities/__internal__/viewportUtils/viewport.utils.js.map +1 -0
- package/package.json +2 -1
- package/dist/components/organisms/SidePanel/hooks/usePanelFocus.cjs.map +0 -1
- package/dist/components/organisms/SidePanel/hooks/usePanelFocus.d.ts.map +0 -1
- package/dist/components/organisms/SidePanel/hooks/usePanelFocus.js.map +0 -1
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { useBreakpoints } from '@flipdish/ui-library/hooks/useBreakpoints';
|
|
3
|
+
import { useThemeRoot } from '@flipdish/ui-library/providers/ThemeProvider';
|
|
2
4
|
import { cx } from '@flipdish/ui-library/utilities/cx';
|
|
3
5
|
import { useResolvedTestId, TestIdProvider } from '@flipdish/ui-library/utilities/testIdUtils';
|
|
4
6
|
import { AnimatePresence, useIsPresent, useReducedMotion, motion } from 'motion/react';
|
|
5
|
-
import { useContext, useRef, useEffect, useState, useCallback } from 'react';
|
|
7
|
+
import { useContext, useRef, useMemo, useEffect, useState, useCallback } from 'react';
|
|
6
8
|
import { Overlay, useOverlay, FocusScope } from 'react-aria';
|
|
7
9
|
import { OverlayTriggerStateContext, Dialog } from 'react-aria-components';
|
|
10
|
+
import { usePanelFocus } from '../../../../hooks/__internal__/usePanelFocus/usePanelFocus.js';
|
|
8
11
|
import { getFocusedElement } from '../../../../utilities/__internal__/focusUtils/focus.utils.js';
|
|
9
12
|
import { isTypingInField } from '../../../../utilities/__internal__/hotkeysUtils/hotkeys.utils.js';
|
|
10
|
-
import { usePanelFocus } from '../../SidePanel/hooks/usePanelFocus.js';
|
|
11
13
|
import { useSlideoutState } from '../hooks/useSlideoutState.js';
|
|
12
14
|
import { OverlayBoundsContext } from '../providers/OverlayBounds.context.js';
|
|
15
|
+
import { PageAreaContext } from '../providers/PageArea.context.js';
|
|
13
16
|
import { SlideoutMenuContext } from '../providers/SlideoutMenu.context.js';
|
|
14
17
|
import { resolveSlideoutBounds } from '../utilities/slideoutMenu.utils.js';
|
|
15
18
|
|
|
16
19
|
const SLIDE = { type: 'tween', duration: 0.3, ease: 'easeOut' };
|
|
17
20
|
const SLIDE_OUT = { type: 'tween', duration: 0.3, ease: 'easeIn' };
|
|
18
21
|
/** The panel itself. A component of its own so it can tell, while animating out, that it is leaving. */
|
|
19
|
-
const SlideoutPanel = ({ openState, label, labelledBy, boundsStyle, rootTestId, isKeyboardDismissDisabled, children, className, dialogClassName, }) => {
|
|
22
|
+
const SlideoutPanel = ({ openState, label, labelledBy, boundsStyle, rootTestId, isDesktop, isKeyboardDismissDisabled, pageAreaRef, children, className, dialogClassName, }) => {
|
|
20
23
|
const panelRef = useRef(null);
|
|
21
24
|
const dialogRef = useRef(null);
|
|
22
25
|
const isPresent = useIsPresent();
|
|
23
26
|
const shouldReduceMotion = useReducedMotion();
|
|
27
|
+
const shellPageAreaRef = useContext(PageAreaContext);
|
|
28
|
+
// Outside `AppShell` the panel covers the app itself: in a micro-frontend the themed root is the page area under the host's
|
|
29
|
+
// chrome, and with `scopeId` overlays portal outside it. The hook leaves a root the panel sits inside alone, so an app that
|
|
30
|
+
// portals into its root passes `pageAreaRef`.
|
|
31
|
+
const themeRoot = useThemeRoot();
|
|
32
|
+
const themeRootRef = useMemo(() => ({ current: themeRoot }), [themeRoot]);
|
|
24
33
|
// Escape inside the panel; nothing outside is listened to for clicks, so the page stays usable and a click there never closes it.
|
|
25
34
|
const { overlayProps } = useOverlay({ isOpen: isPresent, onClose: openState.close, isDismissable: false, shouldCloseOnBlur: false, isKeyboardDismissDisabled }, panelRef);
|
|
26
35
|
// Escape anywhere on the page closes it too: the panel is the transient thing the user opened. Not when another overlay
|
|
@@ -43,9 +52,17 @@ const SlideoutPanel = ({ openState, label, labelledBy, boundsStyle, rootTestId,
|
|
|
43
52
|
document.addEventListener('keydown', onKeyDown);
|
|
44
53
|
return () => document.removeEventListener('keydown', onKeyDown);
|
|
45
54
|
}, [isPresent, isKeyboardDismissDisabled]);
|
|
46
|
-
// react-aria's `useDialog` already focuses the dialog on mount; this hook is here for the hand-back on close
|
|
47
|
-
// and `
|
|
48
|
-
|
|
55
|
+
// react-aria's `useDialog` already focuses the dialog on mount; this hook is here for the hand-back on close, the inert exit,
|
|
56
|
+
// and below `lg` the page area under the panel, which it takes out of reach: keyboard and screen reader skip what nobody can
|
|
57
|
+
// see. The host's own page area wins over the shell's, and the shell's over the app root. `shouldFocusOnMount` is what makes
|
|
58
|
+
// the hook record the opener.
|
|
59
|
+
const { onFocusCapture, setOpener } = usePanelFocus({
|
|
60
|
+
panelRef: dialogRef,
|
|
61
|
+
isPresent,
|
|
62
|
+
isDesktop,
|
|
63
|
+
coveredRef: pageAreaRef ?? shellPageAreaRef ?? themeRootRef,
|
|
64
|
+
shouldFocusOnMount: true,
|
|
65
|
+
});
|
|
49
66
|
// A row opened by mouse selects on pointerdown, so the panel focuses itself before the browser's own mousedown focus lands
|
|
50
67
|
// back on the row. Settle one frame later: if nothing inside holds focus by then, the dialog takes it. A new gesture before
|
|
51
68
|
// that frame means the user has moved on, such as to the next row, and the frame is dropped so focus is not pulled back.
|
|
@@ -98,10 +115,10 @@ const SlideoutPanel = ({ openState, label, labelledBy, boundsStyle, rootTestId,
|
|
|
98
115
|
};
|
|
99
116
|
/**
|
|
100
117
|
* Non-modal shell: portals the panel to the app's overlay root, keeps it mounted through its exit slide,
|
|
101
|
-
* and shares the open state with a surrounding `SlideoutMenu.Trigger`. Nothing is dimmed
|
|
102
|
-
*
|
|
118
|
+
* and shares the open state with a surrounding `SlideoutMenu.Trigger`. Nothing is dimmed or locked; from
|
|
119
|
+
* `lg` the page beside the panel stays usable, and below `lg` only the page area it covers goes inert.
|
|
103
120
|
*/
|
|
104
|
-
const SlideoutShell = ({ isOpen, defaultOpen, onOpenChange, isKeyboardDismissDisabled, bounds, 'aria-label': ariaLabel, 'aria-labelledby': labelledBy, children, className, dialogClassName, 'data-testid': dataTestId, }) => {
|
|
121
|
+
const SlideoutShell = ({ isOpen, defaultOpen, onOpenChange, isKeyboardDismissDisabled, bounds, pageAreaRef, 'aria-label': ariaLabel, 'aria-labelledby': labelledBy, children, className, dialogClassName, 'data-testid': dataTestId, }) => {
|
|
105
122
|
const rootTestId = useResolvedTestId({ testId: dataTestId, segment: 'slideout-menu', rootName: 'slideout-menu' });
|
|
106
123
|
const triggerState = useContext(OverlayTriggerStateContext);
|
|
107
124
|
const local = useSlideoutState({ isOpen, defaultOpen, onOpenChange });
|
|
@@ -109,9 +126,10 @@ const SlideoutShell = ({ isOpen, defaultOpen, onOpenChange, isKeyboardDismissDis
|
|
|
109
126
|
const openState = isOpen !== undefined || defaultOpen !== undefined || !triggerState ? local : triggerState;
|
|
110
127
|
const measured = useContext(OverlayBoundsContext);
|
|
111
128
|
const boundsStyle = resolveSlideoutBounds(bounds, measured);
|
|
129
|
+
const isDesktop = useBreakpoints('lg');
|
|
112
130
|
// A heading named through `aria-labelledby` replaces the default name; an explicit `aria-label` still wins.
|
|
113
131
|
const label = ariaLabel ?? (labelledBy ? undefined : 'Slideout menu');
|
|
114
|
-
return (jsx(TestIdProvider, { value: rootTestId, children: jsx(AnimatePresence, { children: openState.isOpen && (jsx(Overlay, { disableFocusManagement: true, children: jsx(OverlayTriggerStateContext.Provider, { value: openState, children: jsx(SlideoutPanel, { openState: openState, label: label, labelledBy: labelledBy, boundsStyle: boundsStyle, rootTestId: rootTestId, isKeyboardDismissDisabled: isKeyboardDismissDisabled, className: className, dialogClassName: dialogClassName, children: children }) }) }, "slideout-menu")) }) }));
|
|
132
|
+
return (jsx(TestIdProvider, { value: rootTestId, children: jsx(AnimatePresence, { children: openState.isOpen && (jsx(Overlay, { disableFocusManagement: true, children: jsx(OverlayTriggerStateContext.Provider, { value: openState, children: jsx(SlideoutPanel, { openState: openState, label: label, labelledBy: labelledBy, boundsStyle: boundsStyle, rootTestId: rootTestId, isDesktop: isDesktop, isKeyboardDismissDisabled: isKeyboardDismissDisabled, pageAreaRef: pageAreaRef, className: className, dialogClassName: dialogClassName, children: children }) }) }, "slideout-menu")) }) }));
|
|
115
133
|
};
|
|
116
134
|
SlideoutShell.displayName = 'SlideoutShell';
|
|
117
135
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SlideoutShell.js","sources":["../../../../../src/components/organisms/SlideoutMenu/components/SlideoutShell.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs","AriaDialog"],"mappings":"
|
|
1
|
+
{"version":3,"file":"SlideoutShell.js","sources":["../../../../../src/components/organisms/SlideoutMenu/components/SlideoutShell.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs","AriaDialog"],"mappings":"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"}
|
|
@@ -50,7 +50,8 @@ Footer.displayName = 'SlideoutFooter';
|
|
|
50
50
|
/**
|
|
51
51
|
* Panel that slides in from the right edge and floats over the page without dimming or blocking it,
|
|
52
52
|
* for the detail of the record the user just selected. It covers the page area only, never the header, the
|
|
53
|
-
* sidebar or a docked side panel;
|
|
53
|
+
* sidebar or a docked side panel; below `lg` it fills that area and the page under it goes inert until it
|
|
54
|
+
* closes. Compose `Header`, `Content` and `Footer` inside it.
|
|
54
55
|
*
|
|
55
56
|
* @summary non-modal right-edge panel for the selected record's detail
|
|
56
57
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../../src/components/organisms/SlideoutMenu/index.tsx"],"sourcesContent":[null],"names":["_jsx","SlideoutShell","useTestIdPrefix","joinTestId","cx","useContext","SlideoutMenuContext","useRef","useObjectRef","useLayoutEffect","_jsxs","ButtonUtility","ArrowUpRight","CloseButton","AriaDialogTrigger"],"mappings":";;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../../src/components/organisms/SlideoutMenu/index.tsx"],"sourcesContent":[null],"names":["_jsx","SlideoutShell","useTestIdPrefix","joinTestId","cx","useContext","SlideoutMenuContext","useRef","useObjectRef","useLayoutEffect","_jsxs","ButtonUtility","ArrowUpRight","CloseButton","AriaDialogTrigger"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComponentPropsWithRef, ReactNode } from 'react';
|
|
1
|
+
import type { ComponentPropsWithRef, ReactNode, RefObject } from 'react';
|
|
2
2
|
import { DialogTrigger as AriaDialogTrigger } from 'react-aria-components';
|
|
3
3
|
import type { SlideoutRenderProps } from './components/SlideoutShell';
|
|
4
4
|
import type { SlideoutBounds } from './utilities/slideoutMenu.utils';
|
|
@@ -24,6 +24,15 @@ export interface SlideoutMenuProps {
|
|
|
24
24
|
* back to its variable when the others are omitted.
|
|
25
25
|
*/
|
|
26
26
|
bounds?: SlideoutBounds;
|
|
27
|
+
/**
|
|
28
|
+
* The page area the panel covers below `lg`: the element `bounds` describes. The panel makes it `inert` while it covers it,
|
|
29
|
+
* so keyboard and screen reader skip what nobody can see, and restores it as it closes. Without it, inside `AppShell` the
|
|
30
|
+
* shell's page area is covered, and elsewhere the app's root from `UIProvider` when overlays portal outside that root, as
|
|
31
|
+
* they do with `scopeId`; in a micro-frontend that root is the page area under the host's chrome. Pass it when the root
|
|
32
|
+
* holds the app's own header, or when overlays portal into the root. The element must stay mounted while the panel is
|
|
33
|
+
* open, and nothing else may write `inert` on it.
|
|
34
|
+
*/
|
|
35
|
+
pageAreaRef?: RefObject<HTMLElement | null>;
|
|
27
36
|
/** Names the dialog for assistive technology. Defaults to "Slideout menu"; prefer the name of what the panel shows. */
|
|
28
37
|
'aria-label'?: string;
|
|
29
38
|
/** Names the dialog by an element inside it, such as the heading in `Header`, instead of `aria-label`. */
|
|
@@ -74,7 +83,8 @@ declare const Footer: {
|
|
|
74
83
|
/**
|
|
75
84
|
* Panel that slides in from the right edge and floats over the page without dimming or blocking it,
|
|
76
85
|
* for the detail of the record the user just selected. It covers the page area only, never the header, the
|
|
77
|
-
* sidebar or a docked side panel;
|
|
86
|
+
* sidebar or a docked side panel; below `lg` it fills that area and the page under it goes inert until it
|
|
87
|
+
* closes. Compose `Header`, `Content` and `Footer` inside it.
|
|
78
88
|
*
|
|
79
89
|
* @summary non-modal right-edge panel for the selected record's detail
|
|
80
90
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/organisms/SlideoutMenu/index.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/organisms/SlideoutMenu/index.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEzE,OAAO,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErE,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAExH,MAAM,WAAW,iBAAiB;IAChC,uGAAuG;IACvG,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,wFAAwF;IACxF,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC5C,uHAAuH;IACvH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0GAA0G;IAC1G,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yDAAyD;IACzD,QAAQ,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,mBAAmB,KAAK,SAAS,CAAC,CAAC;IAClE,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wHAAwH;IACxH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,QAAA,MAAM,IAAI;YAAW,iBAAiB;;CAAiC,CAAC;AAGxE,QAAA,MAAM,OAAO;8CAA6C,qBAAqB,CAAC,KAAK,CAAC,GAAG;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE;;CAYlH,CAAC;AAGF,MAAM,WAAW,mBAAoB,SAAQ,qBAAqB,CAAC,KAAK,CAAC;IACvE,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gIAAgI;IAChI,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uIAAuI;IACvI,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6IAA6I;IAC7I,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,QAAA,MAAM,MAAM;2HAWT,mBAAmB;;CA+CrB,CAAC;AAGF,QAAA,MAAM,MAAM;8CAA6C,qBAAqB,CAAC,KAAK,CAAC,GAAG;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE;;CAYjH,CAAC;AAGF;;;;;;;GAOG;AACH,QAAA,MAAM,YAAY,EAAW,OAAO,IAAI,GAAG;IACzC,OAAO,EAAE,OAAO,iBAAiB,CAAC;IAClC,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,MAAM,CAAC;CACvB,CAAC;AAQF,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -48,7 +48,8 @@ Footer.displayName = 'SlideoutFooter';
|
|
|
48
48
|
/**
|
|
49
49
|
* Panel that slides in from the right edge and floats over the page without dimming or blocking it,
|
|
50
50
|
* for the detail of the record the user just selected. It covers the page area only, never the header, the
|
|
51
|
-
* sidebar or a docked side panel;
|
|
51
|
+
* sidebar or a docked side panel; below `lg` it fills that area and the page under it goes inert until it
|
|
52
|
+
* closes. Compose `Header`, `Content` and `Footer` inside it.
|
|
52
53
|
*
|
|
53
54
|
* @summary non-modal right-edge panel for the selected record's detail
|
|
54
55
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/components/organisms/SlideoutMenu/index.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs","AriaDialogTrigger"],"mappings":";;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/components/organisms/SlideoutMenu/index.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs","AriaDialogTrigger"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Provided by `AppShell`: the element under the box a `SlideoutMenu` covers below `lg`, the page area
|
|
7
|
+
* together with a side panel docked beside it. The panel makes it inert while it covers it, so keyboard
|
|
8
|
+
* and assistive technology skip what nobody can see. Context rather than a prop so a panel anywhere in
|
|
9
|
+
* the shell finds it; the `pageAreaRef` prop serves a host without the shell.
|
|
10
|
+
*/
|
|
11
|
+
const PageAreaContext = React.createContext(undefined);
|
|
12
|
+
|
|
13
|
+
exports.PageAreaContext = PageAreaContext;
|
|
14
|
+
//# sourceMappingURL=PageArea.context.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PageArea.context.cjs","sources":["../../../../../src/components/organisms/SlideoutMenu/providers/PageArea.context.ts"],"sourcesContent":[null],"names":["createContext"],"mappings":";;;;AAKA;;;;;AAKG;MACU,eAAe,GAAGA,mBAAa,CAA4C,SAAS;;"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Provided by `AppShell`: the element under the box a `SlideoutMenu` covers below `lg`, the page area
|
|
4
|
+
* together with a side panel docked beside it. The panel makes it inert while it covers it, so keyboard
|
|
5
|
+
* and assistive technology skip what nobody can see. Context rather than a prop so a panel anywhere in
|
|
6
|
+
* the shell finds it; the `pageAreaRef` prop serves a host without the shell.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PageAreaContext: import("react").Context<RefObject<HTMLElement | null> | undefined>;
|
|
9
|
+
//# sourceMappingURL=PageArea.context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PageArea.context.d.ts","sourceRoot":"","sources":["../../../../../src/components/organisms/SlideoutMenu/providers/PageArea.context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC;;;;;GAKG;AACH,eAAO,MAAM,eAAe,oEAAsE,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Provided by `AppShell`: the element under the box a `SlideoutMenu` covers below `lg`, the page area
|
|
5
|
+
* together with a side panel docked beside it. The panel makes it inert while it covers it, so keyboard
|
|
6
|
+
* and assistive technology skip what nobody can see. Context rather than a prop so a panel anywhere in
|
|
7
|
+
* the shell finds it; the `pageAreaRef` prop serves a host without the shell.
|
|
8
|
+
*/
|
|
9
|
+
const PageAreaContext = createContext(undefined);
|
|
10
|
+
|
|
11
|
+
export { PageAreaContext };
|
|
12
|
+
//# sourceMappingURL=PageArea.context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PageArea.context.js","sources":["../../../../../src/components/organisms/SlideoutMenu/providers/PageArea.context.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAKA;;;;;AAKG;MACU,eAAe,GAAG,aAAa,CAA4C,SAAS;;"}
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var cx = require('@flipdish/ui-library/utilities/cx');
|
|
5
5
|
var testIdUtils = require('@flipdish/ui-library/utilities/testIdUtils');
|
|
6
|
+
var React = require('react');
|
|
6
7
|
var PageCover_context = require('../../organisms/SidePanel/providers/PageCover.context.cjs');
|
|
7
8
|
var OverlayBounds_context = require('../../organisms/SlideoutMenu/providers/OverlayBounds.context.cjs');
|
|
9
|
+
var PageArea_context = require('../../organisms/SlideoutMenu/providers/PageArea.context.cjs');
|
|
8
10
|
var usePageAreaBounds = require('./hooks/usePageAreaBounds.cjs');
|
|
9
11
|
var usePageCover = require('./hooks/usePageCover.cjs');
|
|
10
12
|
|
|
@@ -20,16 +22,18 @@ const AppShell = ({ sidebar, header, panel, children, scrollAreaProps, className
|
|
|
20
22
|
const appShellTestId = testIdUtils.useResolvedTestId({ testId: dataTestId, segment: 'app-shell', rootName: 'app-shell' });
|
|
21
23
|
// A `SidePanel` in the `panel` slot calls the setter while it covers the page area below `lg`.
|
|
22
24
|
const { scrollAreaRef, setPageCovered } = usePageCover.usePageCover(scrollAreaProps?.ref);
|
|
23
|
-
// A `SlideoutMenu` anywhere in the shell reads the page area so it covers that box and nothing else
|
|
25
|
+
// A `SlideoutMenu` anywhere in the shell reads the page area so it covers that box and nothing else, and below `lg` makes the
|
|
26
|
+
// row under that box inert: the page and a side panel covering it, which the slideout then covers in turn.
|
|
24
27
|
const pageAreaBounds = usePageAreaBounds.usePageAreaBounds(scrollAreaRef);
|
|
28
|
+
const pageRowRef = React.useRef(null);
|
|
25
29
|
return (
|
|
26
30
|
// A column below `lg`, so a navigation that renders its own mobile top bar stacks above the content.
|
|
27
|
-
jsxRuntime.jsx("div", { "data-testid": appShellTestId, className: cx.cx('flex h-dvh w-full flex-col overflow-hidden bg-primary lg:flex-row', className), children: jsxRuntime.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
jsxRuntime.jsx("div", { "data-testid": appShellTestId, className: cx.cx('flex h-dvh w-full flex-col overflow-hidden bg-primary lg:flex-row', className), children: jsxRuntime.jsx(OverlayBounds_context.OverlayBoundsContext.Provider, { value: pageAreaBounds, children: jsxRuntime.jsxs(PageArea_context.PageAreaContext.Provider, { value: pageRowRef, children: [sidebar, jsxRuntime.jsxs("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: [header, jsxRuntime.jsxs("div", { ref: pageRowRef, className: "relative flex min-h-0 flex-1 overflow-clip", children: [jsxRuntime.jsx("div", { id: scrollAreaProps?.id, ref: scrollAreaRef, "data-testid": scrollAreaProps?.['data-testid'] ?? testIdUtils.joinTestId(appShellTestId, 'scroll-area'),
|
|
32
|
+
// Focusable so keyboard users can scroll a page that has no focusable content of its own (axe: scrollable-region-focusable).
|
|
33
|
+
// biome-ignore lint/a11y/noNoninteractiveTabindex: a scrollable region must be reachable by keyboard
|
|
34
|
+
tabIndex: 0,
|
|
35
|
+
// isolate keeps the page's own stacked content (sticky headers) under a panel that covers it.
|
|
36
|
+
className: "isolate flex min-w-0 flex-1 flex-col overflow-y-auto outline-focus-ring focus-visible:outline-2 focus-visible:-outline-offset-2", children: children }), jsxRuntime.jsx(PageCover_context.PageCoverContext.Provider, { value: setPageCovered, children: panel })] })] })] }) }) }));
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
exports.AppShell = AppShell;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../../src/components/templates/AppShell/index.tsx"],"sourcesContent":[null],"names":["useResolvedTestId","usePageCover","usePageAreaBounds","_jsx","cx","_jsxs","
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../../src/components/templates/AppShell/index.tsx"],"sourcesContent":[null],"names":["useResolvedTestId","usePageCover","usePageAreaBounds","useRef","_jsx","cx","OverlayBoundsContext","_jsxs","PageAreaContext","joinTestId","PageCoverContext"],"mappings":";;;;;;;;;;;;AA2CA;;;;;;;AAOG;MACU,QAAQ,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAiB,KAAe;AAChJ,IAAA,MAAM,cAAc,GAAGA,6BAAiB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;;AAE7G,IAAA,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,GAAGC,yBAAY,CAAC,eAAe,EAAE,GAAG,CAAC;;;AAG5E,IAAA,MAAM,cAAc,GAAGC,mCAAiB,CAAC,aAAa,CAAC;AACvD,IAAA,MAAM,UAAU,GAAGC,YAAM,CAAiB,IAAI,CAAC;IAE/C;;AAEE,IAAAC,cAAA,CAAA,KAAA,EAAA,EAAA,aAAA,EAAkB,cAAc,EAAE,SAAS,EAAEC,KAAE,CAAC,mEAAmE,EAAE,SAAS,CAAC,EAAA,QAAA,EAC7HD,cAAA,CAACE,0CAAoB,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,cAAc,EAAA,QAAA,EAClDC,eAAA,CAACC,gCAAe,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,UAAU,EAAA,QAAA,EAAA,CACxC,OAAO,EAGRD,yBAAK,SAAS,EAAC,sCAAsC,EAAA,QAAA,EAAA,CAClD,MAAM,EAGPA,eAAA,CAAA,KAAA,EAAA,EAAK,GAAG,EAAE,UAAU,EAAE,SAAS,EAAC,4CAA4C,EAAA,QAAA,EAAA,CAC1EH,cAAA,CAAA,KAAA,EAAA,EACE,EAAE,EAAE,eAAe,EAAE,EAAE,EACvB,GAAG,EAAE,aAAa,EAAA,aAAA,EACL,eAAe,GAAG,aAAa,CAAC,IAAIK,sBAAU,CAAC,cAAc,EAAE,aAAa,CAAC;;;AAG1F,wCAAA,QAAQ,EAAE,CAAC;;wCAEX,SAAS,EAAC,iIAAiI,EAAA,QAAA,EAE1I,QAAQ,GACL,EAENL,cAAA,CAACM,kCAAgB,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,cAAc,EAAA,QAAA,EAAG,KAAK,EAAA,CAA6B,CAAA,EAAA,CACjF,IACF,CAAA,EAAA,CACmB,EAAA,CACG,EAAA,CAC5B;AAEV;;"}
|
|
@@ -15,7 +15,8 @@ export interface AppShellProps {
|
|
|
15
15
|
/**
|
|
16
16
|
* Side panel rendered under the header. From `lg` it sits beside the page area, which narrows to make
|
|
17
17
|
* room. Below `lg` it covers the page area, which is then out of reach of keyboard and assistive
|
|
18
|
-
* technology until the panel closes
|
|
18
|
+
* technology until the panel closes, as the panel itself is while a `SlideoutMenu` covers both. Compose
|
|
19
|
+
* one `<SidePanel>` here and switch its content.
|
|
19
20
|
*/
|
|
20
21
|
panel?: ReactNode;
|
|
21
22
|
/** Page content, normally a `<PageLayout>`. Rendered inside the shell's scroll area. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/templates/AppShell/index.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/templates/AppShell/index.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAQ5C,MAAM,WAAW,uBAAuB;IACtC,wFAAwF;IACxF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yGAAyG;IACzG,GAAG,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC1B,2FAA2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,wGAAwG;IACxG,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,sLAAsL;IACtL,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,wFAAwF;IACxF,QAAQ,EAAE,SAAS,CAAC;IACpB,sGAAsG;IACtG,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,GAAI,6FAA6F,aAAa,KAAG,SA0CrI,CAAC"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import { cx } from '@flipdish/ui-library/utilities/cx';
|
|
3
3
|
import { useResolvedTestId, joinTestId } from '@flipdish/ui-library/utilities/testIdUtils';
|
|
4
|
+
import { useRef } from 'react';
|
|
4
5
|
import { PageCoverContext } from '../../organisms/SidePanel/providers/PageCover.context.js';
|
|
5
6
|
import { OverlayBoundsContext } from '../../organisms/SlideoutMenu/providers/OverlayBounds.context.js';
|
|
7
|
+
import { PageAreaContext } from '../../organisms/SlideoutMenu/providers/PageArea.context.js';
|
|
6
8
|
import { usePageAreaBounds } from './hooks/usePageAreaBounds.js';
|
|
7
9
|
import { usePageCover } from './hooks/usePageCover.js';
|
|
8
10
|
|
|
@@ -18,16 +20,18 @@ const AppShell = ({ sidebar, header, panel, children, scrollAreaProps, className
|
|
|
18
20
|
const appShellTestId = useResolvedTestId({ testId: dataTestId, segment: 'app-shell', rootName: 'app-shell' });
|
|
19
21
|
// A `SidePanel` in the `panel` slot calls the setter while it covers the page area below `lg`.
|
|
20
22
|
const { scrollAreaRef, setPageCovered } = usePageCover(scrollAreaProps?.ref);
|
|
21
|
-
// A `SlideoutMenu` anywhere in the shell reads the page area so it covers that box and nothing else
|
|
23
|
+
// A `SlideoutMenu` anywhere in the shell reads the page area so it covers that box and nothing else, and below `lg` makes the
|
|
24
|
+
// row under that box inert: the page and a side panel covering it, which the slideout then covers in turn.
|
|
22
25
|
const pageAreaBounds = usePageAreaBounds(scrollAreaRef);
|
|
26
|
+
const pageRowRef = useRef(null);
|
|
23
27
|
return (
|
|
24
28
|
// A column below `lg`, so a navigation that renders its own mobile top bar stacks above the content.
|
|
25
|
-
jsx("div", { "data-testid": appShellTestId, className: cx('flex h-dvh w-full flex-col overflow-hidden bg-primary lg:flex-row', className), children:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
jsx("div", { "data-testid": appShellTestId, className: cx('flex h-dvh w-full flex-col overflow-hidden bg-primary lg:flex-row', className), children: jsx(OverlayBoundsContext.Provider, { value: pageAreaBounds, children: jsxs(PageAreaContext.Provider, { value: pageRowRef, children: [sidebar, jsxs("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: [header, jsxs("div", { ref: pageRowRef, className: "relative flex min-h-0 flex-1 overflow-clip", children: [jsx("div", { id: scrollAreaProps?.id, ref: scrollAreaRef, "data-testid": scrollAreaProps?.['data-testid'] ?? joinTestId(appShellTestId, 'scroll-area'),
|
|
30
|
+
// Focusable so keyboard users can scroll a page that has no focusable content of its own (axe: scrollable-region-focusable).
|
|
31
|
+
// biome-ignore lint/a11y/noNoninteractiveTabindex: a scrollable region must be reachable by keyboard
|
|
32
|
+
tabIndex: 0,
|
|
33
|
+
// isolate keeps the page's own stacked content (sticky headers) under a panel that covers it.
|
|
34
|
+
className: "isolate flex min-w-0 flex-1 flex-col overflow-y-auto outline-focus-ring focus-visible:outline-2 focus-visible:-outline-offset-2", children: children }), jsx(PageCoverContext.Provider, { value: setPageCovered, children: panel })] })] })] }) }) }));
|
|
31
35
|
};
|
|
32
36
|
|
|
33
37
|
export { AppShell };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/components/templates/AppShell/index.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/components/templates/AppShell/index.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs"],"mappings":";;;;;;;;;;AA2CA;;;;;;;AAOG;MACU,QAAQ,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAiB,KAAe;AAChJ,IAAA,MAAM,cAAc,GAAG,iBAAiB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;;AAE7G,IAAA,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,YAAY,CAAC,eAAe,EAAE,GAAG,CAAC;;;AAG5E,IAAA,MAAM,cAAc,GAAG,iBAAiB,CAAC,aAAa,CAAC;AACvD,IAAA,MAAM,UAAU,GAAG,MAAM,CAAiB,IAAI,CAAC;IAE/C;;AAEE,IAAAA,GAAA,CAAA,KAAA,EAAA,EAAA,aAAA,EAAkB,cAAc,EAAE,SAAS,EAAE,EAAE,CAAC,mEAAmE,EAAE,SAAS,CAAC,EAAA,QAAA,EAC7HA,GAAA,CAAC,oBAAoB,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,cAAc,EAAA,QAAA,EAClDC,IAAA,CAAC,eAAe,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,UAAU,EAAA,QAAA,EAAA,CACxC,OAAO,EAGRA,cAAK,SAAS,EAAC,sCAAsC,EAAA,QAAA,EAAA,CAClD,MAAM,EAGPA,IAAA,CAAA,KAAA,EAAA,EAAK,GAAG,EAAE,UAAU,EAAE,SAAS,EAAC,4CAA4C,EAAA,QAAA,EAAA,CAC1ED,GAAA,CAAA,KAAA,EAAA,EACE,EAAE,EAAE,eAAe,EAAE,EAAE,EACvB,GAAG,EAAE,aAAa,EAAA,aAAA,EACL,eAAe,GAAG,aAAa,CAAC,IAAI,UAAU,CAAC,cAAc,EAAE,aAAa,CAAC;;;AAG1F,wCAAA,QAAQ,EAAE,CAAC;;wCAEX,SAAS,EAAC,iIAAiI,EAAA,QAAA,EAE1I,QAAQ,GACL,EAENA,GAAA,CAAC,gBAAgB,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,cAAc,EAAA,QAAA,EAAG,KAAK,EAAA,CAA6B,CAAA,EAAA,CACjF,IACF,CAAA,EAAA,CACmB,EAAA,CACG,EAAA,CAC5B;AAEV;;"}
|
|
@@ -1,22 +1,48 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var React = require('react');
|
|
4
|
-
var focus_utils = require('
|
|
4
|
+
var focus_utils = require('../../../utilities/__internal__/focusUtils/focus.utils.cjs');
|
|
5
|
+
var inert_utils = require('../../../utilities/__internal__/inertUtils/inert.utils.cjs');
|
|
5
6
|
|
|
6
7
|
/** The element before the panel when it can take focus, such as `AppShell`'s scroll area. */
|
|
7
8
|
const focusableSiblingBefore = (panel) => {
|
|
8
9
|
const sibling = panel.previousElementSibling;
|
|
9
10
|
return sibling instanceof HTMLElement && sibling.tabIndex >= 0 ? sibling : null;
|
|
10
11
|
};
|
|
12
|
+
/** Whether a hand-back can land: a control, or a region given a `tabindex`, that no cover has taken out of reach. */
|
|
13
|
+
const canTakeFocus = (element) => element instanceof HTMLElement && (element.tabIndex >= 0 || element.hasAttribute('tabindex')) && element.closest('[inert]') === null;
|
|
14
|
+
/**
|
|
15
|
+
* What covers an element under an inert ancestor: the last panel to cover that ancestor, else the sibling after it, as a
|
|
16
|
+
* side panel sits after the page it covers. Nothing when that cannot take focus, such as a plain wrapper.
|
|
17
|
+
*/
|
|
18
|
+
const coverOver = (element) => {
|
|
19
|
+
const covered = element.closest('[inert]');
|
|
20
|
+
if (!(covered instanceof HTMLElement))
|
|
21
|
+
return null;
|
|
22
|
+
const candidate = inert_utils.lastCoverOf(covered) ?? covered.nextElementSibling;
|
|
23
|
+
return canTakeFocus(candidate) ? candidate : null;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* What the panel covers: the given element, unless the panel sits inside it, as overlays do without a scoped anchor, when
|
|
27
|
+
* nothing is; without one, the panel's siblings.
|
|
28
|
+
*/
|
|
29
|
+
const resolveCovered = (panel, coveredRef) => {
|
|
30
|
+
if (!coveredRef)
|
|
31
|
+
return panel.parentElement;
|
|
32
|
+
const covered = coveredRef.current;
|
|
33
|
+
return covered && !covered.contains(panel) ? covered : null;
|
|
34
|
+
};
|
|
11
35
|
/**
|
|
12
36
|
* Hands focus into the panel when it opens and back to the control that opened it when it closes,
|
|
13
|
-
*
|
|
14
|
-
* reroute Tab at the panel's edges, as
|
|
37
|
+
* makes a leaving panel inert, and takes what the panel covers out of reach while it covers it.
|
|
38
|
+
* Managed by hand, not with a focus scope: a scope would also reroute Tab at the panel's edges, as
|
|
39
|
+
* a popover does.
|
|
15
40
|
*
|
|
41
|
+
* @param options See `UsePanelFocusOptions`.
|
|
16
42
|
* @returns a capture-phase focus handler to spread onto the panel element, and `setOpener`, which points the hand-back on
|
|
17
43
|
* close at another control after a content swap
|
|
18
44
|
*/
|
|
19
|
-
const usePanelFocus = ({ panelRef, isPresent, isDesktop, shouldFocusOnMount, contentKey }) => {
|
|
45
|
+
const usePanelFocus = ({ panelRef, isPresent, isDesktop, coveredRef, shouldFocusOnMount, contentKey }) => {
|
|
20
46
|
const openerRef = React.useRef(null);
|
|
21
47
|
const previousIsPresent = React.useRef(null);
|
|
22
48
|
const previousIsDesktop = React.useRef(isDesktop);
|
|
@@ -48,22 +74,44 @@ const usePanelFocus = ({ panelRef, isPresent, isDesktop, shouldFocusOnMount, con
|
|
|
48
74
|
if (isLeaving && hasFocusInside) {
|
|
49
75
|
const opener = openerRef.current;
|
|
50
76
|
// The opener may have unmounted meanwhile, such as a header button that only renders at one breakpoint.
|
|
51
|
-
|
|
77
|
+
const target = opener?.isConnected ? opener : focusableSiblingBefore(panel);
|
|
78
|
+
target?.focus();
|
|
79
|
+
// An opener under a cover, a side panel or another panel over the page below `lg`, refuses focus: what covers it takes it instead.
|
|
80
|
+
if (target && focus_utils.getFocusedElement() !== target)
|
|
81
|
+
coverOver(target)?.focus();
|
|
52
82
|
}
|
|
53
83
|
// A leaving panel must not be reachable while another opens; set last, since inert drops any focus inside it.
|
|
54
84
|
if (!isPresent)
|
|
55
85
|
panel.inert = true;
|
|
56
86
|
}, [panelRef, isPresent, shouldFocusOnMount, focusedBeforeMount]);
|
|
87
|
+
// Before the cover below is applied, so focus leaves the page while the page can still be read as holding it.
|
|
57
88
|
React.useEffect(() => {
|
|
58
89
|
const hasStartedCoveringPage = previousIsDesktop.current && !isDesktop;
|
|
59
90
|
previousIsDesktop.current = isDesktop;
|
|
60
91
|
const panel = panelRef.current;
|
|
61
92
|
const focused = focus_utils.getFocusedElement();
|
|
62
|
-
// The page
|
|
63
|
-
|
|
93
|
+
// The page the panel covers is about to go out of reach, so focus must not be left in it, where inert would drop it on nothing.
|
|
94
|
+
const covered = panel && resolveCovered(panel, coveredRef);
|
|
95
|
+
if (hasStartedCoveringPage && panel && focused && !panel.contains(focused) && covered?.contains(focused)) {
|
|
64
96
|
panel.focus({ preventScroll: true });
|
|
65
97
|
}
|
|
66
|
-
}, [panelRef, isDesktop]);
|
|
98
|
+
}, [panelRef, coveredRef, isDesktop]);
|
|
99
|
+
// React runs every cleanup before any effect in a commit, so the cover lifts before the hand-back above lands focus in the
|
|
100
|
+
// page. Covers are counted per element, so two panels over the same page restore its own `inert` only when the last lifts.
|
|
101
|
+
const isCovering = isPresent && !isDesktop;
|
|
102
|
+
React.useEffect(() => {
|
|
103
|
+
const panel = panelRef.current;
|
|
104
|
+
// Only a given element is covered; the panel's siblings are the layout's to cover, as `AppShell` does for a side panel.
|
|
105
|
+
const covered = isCovering && panel && coveredRef ? resolveCovered(panel, coveredRef) : null;
|
|
106
|
+
if (!panel || !covered)
|
|
107
|
+
return;
|
|
108
|
+
// An area chosen while the panel is open, such as the app root arriving a commit late, may hold focus the hand-off above
|
|
109
|
+
// did not see: it moves into the panel before the area goes out of reach.
|
|
110
|
+
const focused = focus_utils.getFocusedElement();
|
|
111
|
+
if (focused && covered.contains(focused))
|
|
112
|
+
panel.focus({ preventScroll: true });
|
|
113
|
+
return inert_utils.coverElement(covered, panel);
|
|
114
|
+
}, [panelRef, coveredRef, isCovering]);
|
|
67
115
|
// Swapping content unmounts whatever held focus, which drops it on the page. The region takes it, so Tab stays inside and
|
|
68
116
|
// closing can still hand it back. Content that focuses itself on mount (an `autoFocus` input) has done so by now and keeps it.
|
|
69
117
|
const previousContentKey = React.useRef(contentKey);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePanelFocus.cjs","sources":["../../../../src/hooks/__internal__/usePanelFocus/usePanelFocus.ts"],"sourcesContent":[null],"names":["lastCoverOf","useRef","useState","getFocusedElement","useEffect","coverElement","useLayoutEffect","useCallback"],"mappings":"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"}
|
|
@@ -5,6 +5,13 @@ interface UsePanelFocusOptions {
|
|
|
5
5
|
isPresent: boolean;
|
|
6
6
|
/** Whether the panel is docked beside the page rather than covering it. */
|
|
7
7
|
isDesktop: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* What the panel covers when it is not docked, for a panel that is not the page's sibling, such as one portaled over it.
|
|
10
|
+
* Made inert while covered and restored as the panel leaves, before focus is handed back into it; left alone when the
|
|
11
|
+
* panel sits inside it. Read when the cover starts, so the element must stay mounted while the panel is open. Omitted,
|
|
12
|
+
* the page is taken to be the panel's siblings and its cover is the layout's job.
|
|
13
|
+
*/
|
|
14
|
+
coveredRef?: RefObject<HTMLElement | null>;
|
|
8
15
|
/** False for a panel that is open on the first render, which must not pull focus away from the page. */
|
|
9
16
|
shouldFocusOnMount: boolean;
|
|
10
17
|
/** Identifies the content shown; a change means the content, and whatever held focus in it, was replaced. */
|
|
@@ -12,13 +19,15 @@ interface UsePanelFocusOptions {
|
|
|
12
19
|
}
|
|
13
20
|
/**
|
|
14
21
|
* Hands focus into the panel when it opens and back to the control that opened it when it closes,
|
|
15
|
-
*
|
|
16
|
-
* reroute Tab at the panel's edges, as
|
|
22
|
+
* makes a leaving panel inert, and takes what the panel covers out of reach while it covers it.
|
|
23
|
+
* Managed by hand, not with a focus scope: a scope would also reroute Tab at the panel's edges, as
|
|
24
|
+
* a popover does.
|
|
17
25
|
*
|
|
26
|
+
* @param options See `UsePanelFocusOptions`.
|
|
18
27
|
* @returns a capture-phase focus handler to spread onto the panel element, and `setOpener`, which points the hand-back on
|
|
19
28
|
* close at another control after a content swap
|
|
20
29
|
*/
|
|
21
|
-
export declare const usePanelFocus: ({ panelRef, isPresent, isDesktop, shouldFocusOnMount, contentKey }: UsePanelFocusOptions) => {
|
|
30
|
+
export declare const usePanelFocus: ({ panelRef, isPresent, isDesktop, coveredRef, shouldFocusOnMount, contentKey }: UsePanelFocusOptions) => {
|
|
22
31
|
onFocusCapture: (event: FocusEvent<HTMLElement>) => void;
|
|
23
32
|
setOpener: (element: HTMLElement) => void;
|
|
24
33
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePanelFocus.d.ts","sourceRoot":"","sources":["../../../../src/hooks/__internal__/usePanelFocus/usePanelFocus.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAuCnD,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACxC,kEAAkE;IAClE,SAAS,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC3C,wGAAwG;IACxG,kBAAkB,EAAE,OAAO,CAAC;IAC5B,6GAA6G;IAC7G,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,GAAI,gFAAgF,oBAAoB;4BAiFjG,UAAU,CAAC,WAAW,CAAC;yBAMd,WAAW;CAKpD,CAAC"}
|