@flipdish/ui-library 0.43.2 → 0.45.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 +9 -1
- package/dist/components/organisms/SidePanel/components/SidePanelRegion.cjs.map +1 -1
- package/dist/components/organisms/SidePanel/components/SidePanelRegion.d.ts.map +1 -1
- package/dist/components/organisms/SidePanel/components/SidePanelRegion.js +9 -1
- package/dist/components/organisms/SidePanel/components/SidePanelRegion.js.map +1 -1
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.cjs +36 -9
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.cjs.map +1 -1
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.d.ts +2 -2
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.d.ts.map +1 -1
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.js +37 -10
- package/dist/components/organisms/SlideoutMenu/components/SlideoutShell.js.map +1 -1
- package/dist/components/organisms/SlideoutMenu/hooks/useCloseWhenCovered.cjs +31 -0
- package/dist/components/organisms/SlideoutMenu/hooks/useCloseWhenCovered.cjs.map +1 -0
- package/dist/components/organisms/SlideoutMenu/hooks/useCloseWhenCovered.d.ts +15 -0
- package/dist/components/organisms/SlideoutMenu/hooks/useCloseWhenCovered.d.ts.map +1 -0
- package/dist/components/organisms/SlideoutMenu/hooks/useCloseWhenCovered.js +29 -0
- package/dist/components/organisms/SlideoutMenu/hooks/useCloseWhenCovered.js.map +1 -0
- package/dist/components/organisms/SlideoutMenu/hooks/useSlideoutState.cjs +3 -2
- package/dist/components/organisms/SlideoutMenu/hooks/useSlideoutState.cjs.map +1 -1
- package/dist/components/organisms/SlideoutMenu/hooks/useSlideoutState.d.ts.map +1 -1
- package/dist/components/organisms/SlideoutMenu/hooks/useSlideoutState.js +4 -3
- package/dist/components/organisms/SlideoutMenu/hooks/useSlideoutState.js.map +1 -1
- package/dist/components/organisms/SlideoutMenu/index.cjs +7 -1
- package/dist/components/organisms/SlideoutMenu/index.cjs.map +1 -1
- package/dist/components/organisms/SlideoutMenu/index.d.ts +19 -2
- package/dist/components/organisms/SlideoutMenu/index.d.ts.map +1 -1
- package/dist/components/organisms/SlideoutMenu/index.js +7 -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/hooks/__internal__/usePanelFocus/usePanelFocus.cjs +156 -0
- package/dist/hooks/__internal__/usePanelFocus/usePanelFocus.cjs.map +1 -0
- package/dist/hooks/__internal__/usePanelFocus/usePanelFocus.d.ts +36 -0
- package/dist/hooks/__internal__/usePanelFocus/usePanelFocus.d.ts.map +1 -0
- package/dist/hooks/__internal__/usePanelFocus/usePanelFocus.js +154 -0
- package/dist/hooks/__internal__/usePanelFocus/usePanelFocus.js.map +1 -0
- package/dist/providers/ThemeProvider/Theme.context.cjs +17 -0
- package/dist/providers/ThemeProvider/Theme.context.cjs.map +1 -0
- package/dist/providers/ThemeProvider/Theme.context.d.ts +16 -0
- package/dist/providers/ThemeProvider/Theme.context.d.ts.map +1 -0
- package/dist/providers/ThemeProvider/Theme.context.js +14 -0
- package/dist/providers/ThemeProvider/Theme.context.js.map +1 -0
- package/dist/providers/ThemeProvider/index.cjs +3 -3
- package/dist/providers/ThemeProvider/index.cjs.map +1 -1
- package/dist/providers/ThemeProvider/index.d.ts +1 -7
- package/dist/providers/ThemeProvider/index.d.ts.map +1 -1
- package/dist/providers/ThemeProvider/index.js +2 -2
- 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__/pageCoverUtils/pageCover.utils.cjs +30 -0
- package/dist/utilities/__internal__/pageCoverUtils/pageCover.utils.cjs.map +1 -0
- package/dist/utilities/__internal__/pageCoverUtils/pageCover.utils.d.ts +19 -0
- package/dist/utilities/__internal__/pageCoverUtils/pageCover.utils.d.ts.map +1 -0
- package/dist/utilities/__internal__/pageCoverUtils/pageCover.utils.js +26 -0
- package/dist/utilities/__internal__/pageCoverUtils/pageCover.utils.js.map +1 -0
- package/package.json +1 -1
- package/dist/components/organisms/SidePanel/hooks/usePanelFocus.cjs +0 -92
- package/dist/components/organisms/SidePanel/hooks/usePanelFocus.cjs.map +0 -1
- package/dist/components/organisms/SidePanel/hooks/usePanelFocus.d.ts +0 -26
- package/dist/components/organisms/SidePanel/hooks/usePanelFocus.d.ts.map +0 -1
- package/dist/components/organisms/SidePanel/hooks/usePanelFocus.js +0 -90
- package/dist/components/organisms/SidePanel/hooks/usePanelFocus.js.map +0 -1
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var React = require('react');
|
|
4
|
-
var focus_utils = require('../../../../utilities/__internal__/focusUtils/focus.utils.cjs');
|
|
5
|
-
|
|
6
|
-
/** The element before the panel when it can take focus, such as `AppShell`'s scroll area. */
|
|
7
|
-
const focusableSiblingBefore = (panel) => {
|
|
8
|
-
const sibling = panel.previousElementSibling;
|
|
9
|
-
return sibling instanceof HTMLElement && sibling.tabIndex >= 0 ? sibling : null;
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Hands focus into the panel when it opens and back to the control that opened it when it closes,
|
|
13
|
-
* and makes a leaving panel inert. Managed by hand, not with a focus scope: a scope would also
|
|
14
|
-
* reroute Tab at the panel's edges, as a popover does.
|
|
15
|
-
*
|
|
16
|
-
* @returns a capture-phase focus handler to spread onto the panel element, and `setOpener`, which points the hand-back on
|
|
17
|
-
* close at another control after a content swap
|
|
18
|
-
*/
|
|
19
|
-
const usePanelFocus = ({ panelRef, isPresent, isDesktop, shouldFocusOnMount, contentKey }) => {
|
|
20
|
-
const openerRef = React.useRef(null);
|
|
21
|
-
const previousIsPresent = React.useRef(null);
|
|
22
|
-
const previousIsDesktop = React.useRef(isDesktop);
|
|
23
|
-
// Read while rendering, before the content mounts: an `autoFocus` input takes focus during commit, ahead of any effect.
|
|
24
|
-
const [focusedBeforeMount] = React.useState(focus_utils.getFocusedElement);
|
|
25
|
-
React.useEffect(() => {
|
|
26
|
-
const panel = panelRef.current;
|
|
27
|
-
if (!panel)
|
|
28
|
-
return;
|
|
29
|
-
const previous = previousIsPresent.current;
|
|
30
|
-
previousIsPresent.current = isPresent;
|
|
31
|
-
// Entering covers a fresh open and a reopen that interrupts the close animation.
|
|
32
|
-
const isEntering = isPresent && (previous === false || (previous === null && shouldFocusOnMount));
|
|
33
|
-
const isLeaving = !isPresent && previous === true;
|
|
34
|
-
const focused = focus_utils.getFocusedElement();
|
|
35
|
-
const hasFocusInside = focused !== null && panel.contains(focused);
|
|
36
|
-
// A panel reopened mid-close is still inert from leaving, and an inert element cannot take focus.
|
|
37
|
-
if (isPresent)
|
|
38
|
-
panel.inert = false;
|
|
39
|
-
if (isEntering) {
|
|
40
|
-
// On mount the content may already hold focus, so the opener is what was focused before it. A reopen mid-close finds focus still outside.
|
|
41
|
-
openerRef.current = previous === null ? focusedBeforeMount : focused;
|
|
42
|
-
// Content that focused itself (an `autoFocus` input) keeps focus; otherwise the region takes it.
|
|
43
|
-
// preventScroll: below `lg` the panel starts off-screen, and scrolling it into view would shift the layout.
|
|
44
|
-
if (!hasFocusInside)
|
|
45
|
-
panel.focus({ preventScroll: true });
|
|
46
|
-
}
|
|
47
|
-
// Hand focus back only if it is still in the panel, so a user who has moved on is left alone.
|
|
48
|
-
if (isLeaving && hasFocusInside) {
|
|
49
|
-
const opener = openerRef.current;
|
|
50
|
-
// The opener may have unmounted meanwhile, such as a header button that only renders at one breakpoint.
|
|
51
|
-
(opener?.isConnected ? opener : focusableSiblingBefore(panel))?.focus();
|
|
52
|
-
}
|
|
53
|
-
// A leaving panel must not be reachable while another opens; set last, since inert drops any focus inside it.
|
|
54
|
-
if (!isPresent)
|
|
55
|
-
panel.inert = true;
|
|
56
|
-
}, [panelRef, isPresent, shouldFocusOnMount, focusedBeforeMount]);
|
|
57
|
-
React.useEffect(() => {
|
|
58
|
-
const hasStartedCoveringPage = previousIsDesktop.current && !isDesktop;
|
|
59
|
-
previousIsDesktop.current = isDesktop;
|
|
60
|
-
const panel = panelRef.current;
|
|
61
|
-
const focused = focus_utils.getFocusedElement();
|
|
62
|
-
// The page beside the panel is about to go out of reach, so focus must not be left in it.
|
|
63
|
-
if (hasStartedCoveringPage && panel && focused && !panel.contains(focused) && panel.parentElement?.contains(focused)) {
|
|
64
|
-
panel.focus({ preventScroll: true });
|
|
65
|
-
}
|
|
66
|
-
}, [panelRef, isDesktop]);
|
|
67
|
-
// Swapping content unmounts whatever held focus, which drops it on the page. The region takes it, so Tab stays inside and
|
|
68
|
-
// closing can still hand it back. Content that focuses itself on mount (an `autoFocus` input) has done so by now and keeps it.
|
|
69
|
-
const previousContentKey = React.useRef(contentKey);
|
|
70
|
-
React.useLayoutEffect(() => {
|
|
71
|
-
if (previousContentKey.current === contentKey)
|
|
72
|
-
return;
|
|
73
|
-
previousContentKey.current = contentKey;
|
|
74
|
-
const panel = panelRef.current;
|
|
75
|
-
if (panel && isPresent && focus_utils.getFocusedElement() === null)
|
|
76
|
-
panel.focus({ preventScroll: true });
|
|
77
|
-
}, [panelRef, contentKey, isPresent]);
|
|
78
|
-
// A panel that was open on load has no opener, so closing hands focus back to wherever it first came in from.
|
|
79
|
-
const onFocusCapture = (event) => {
|
|
80
|
-
const from = event.relatedTarget;
|
|
81
|
-
if (openerRef.current === null && from instanceof HTMLElement && !event.currentTarget.contains(from))
|
|
82
|
-
openerRef.current = from;
|
|
83
|
-
};
|
|
84
|
-
// A panel whose content swaps while open follows the user's latest gesture, so closing lands on the row they are on, not the first one.
|
|
85
|
-
const setOpener = React.useCallback((element) => {
|
|
86
|
-
openerRef.current = element;
|
|
87
|
-
}, []);
|
|
88
|
-
return { onFocusCapture, setOpener };
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
exports.usePanelFocus = usePanelFocus;
|
|
92
|
-
//# sourceMappingURL=usePanelFocus.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"usePanelFocus.cjs","sources":["../../../../../src/components/organisms/SidePanel/hooks/usePanelFocus.ts"],"sourcesContent":[null],"names":["useRef","useState","getFocusedElement","useEffect","useLayoutEffect","useCallback"],"mappings":"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"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { FocusEvent, RefObject } from 'react';
|
|
2
|
-
interface UsePanelFocusOptions {
|
|
3
|
-
panelRef: RefObject<HTMLElement | null>;
|
|
4
|
-
/** From `useIsPresent`: false once the panel is animating out. */
|
|
5
|
-
isPresent: boolean;
|
|
6
|
-
/** Whether the panel is docked beside the page rather than covering it. */
|
|
7
|
-
isDesktop: boolean;
|
|
8
|
-
/** False for a panel that is open on the first render, which must not pull focus away from the page. */
|
|
9
|
-
shouldFocusOnMount: boolean;
|
|
10
|
-
/** Identifies the content shown; a change means the content, and whatever held focus in it, was replaced. */
|
|
11
|
-
contentKey?: string;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Hands focus into the panel when it opens and back to the control that opened it when it closes,
|
|
15
|
-
* and makes a leaving panel inert. Managed by hand, not with a focus scope: a scope would also
|
|
16
|
-
* reroute Tab at the panel's edges, as a popover does.
|
|
17
|
-
*
|
|
18
|
-
* @returns a capture-phase focus handler to spread onto the panel element, and `setOpener`, which points the hand-back on
|
|
19
|
-
* close at another control after a content swap
|
|
20
|
-
*/
|
|
21
|
-
export declare const usePanelFocus: ({ panelRef, isPresent, isDesktop, shouldFocusOnMount, contentKey }: UsePanelFocusOptions) => {
|
|
22
|
-
onFocusCapture: (event: FocusEvent<HTMLElement>) => void;
|
|
23
|
-
setOpener: (element: HTMLElement) => void;
|
|
24
|
-
};
|
|
25
|
-
export {};
|
|
26
|
-
//# sourceMappingURL=usePanelFocus.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"usePanelFocus.d.ts","sourceRoot":"","sources":["../../../../../src/components/organisms/SidePanel/hooks/usePanelFocus.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAWnD,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,wGAAwG;IACxG,kBAAkB,EAAE,OAAO,CAAC;IAC5B,6GAA6G;IAC7G,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GAAI,oEAAoE,oBAAoB;4BA6DrF,UAAU,CAAC,WAAW,CAAC;yBAMd,WAAW;CAKpD,CAAC"}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { useRef, useState, useEffect, useLayoutEffect, useCallback } from 'react';
|
|
2
|
-
import { getFocusedElement } from '../../../../utilities/__internal__/focusUtils/focus.utils.js';
|
|
3
|
-
|
|
4
|
-
/** The element before the panel when it can take focus, such as `AppShell`'s scroll area. */
|
|
5
|
-
const focusableSiblingBefore = (panel) => {
|
|
6
|
-
const sibling = panel.previousElementSibling;
|
|
7
|
-
return sibling instanceof HTMLElement && sibling.tabIndex >= 0 ? sibling : null;
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Hands focus into the panel when it opens and back to the control that opened it when it closes,
|
|
11
|
-
* and makes a leaving panel inert. Managed by hand, not with a focus scope: a scope would also
|
|
12
|
-
* reroute Tab at the panel's edges, as a popover does.
|
|
13
|
-
*
|
|
14
|
-
* @returns a capture-phase focus handler to spread onto the panel element, and `setOpener`, which points the hand-back on
|
|
15
|
-
* close at another control after a content swap
|
|
16
|
-
*/
|
|
17
|
-
const usePanelFocus = ({ panelRef, isPresent, isDesktop, shouldFocusOnMount, contentKey }) => {
|
|
18
|
-
const openerRef = useRef(null);
|
|
19
|
-
const previousIsPresent = useRef(null);
|
|
20
|
-
const previousIsDesktop = useRef(isDesktop);
|
|
21
|
-
// Read while rendering, before the content mounts: an `autoFocus` input takes focus during commit, ahead of any effect.
|
|
22
|
-
const [focusedBeforeMount] = useState(getFocusedElement);
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
const panel = panelRef.current;
|
|
25
|
-
if (!panel)
|
|
26
|
-
return;
|
|
27
|
-
const previous = previousIsPresent.current;
|
|
28
|
-
previousIsPresent.current = isPresent;
|
|
29
|
-
// Entering covers a fresh open and a reopen that interrupts the close animation.
|
|
30
|
-
const isEntering = isPresent && (previous === false || (previous === null && shouldFocusOnMount));
|
|
31
|
-
const isLeaving = !isPresent && previous === true;
|
|
32
|
-
const focused = getFocusedElement();
|
|
33
|
-
const hasFocusInside = focused !== null && panel.contains(focused);
|
|
34
|
-
// A panel reopened mid-close is still inert from leaving, and an inert element cannot take focus.
|
|
35
|
-
if (isPresent)
|
|
36
|
-
panel.inert = false;
|
|
37
|
-
if (isEntering) {
|
|
38
|
-
// On mount the content may already hold focus, so the opener is what was focused before it. A reopen mid-close finds focus still outside.
|
|
39
|
-
openerRef.current = previous === null ? focusedBeforeMount : focused;
|
|
40
|
-
// Content that focused itself (an `autoFocus` input) keeps focus; otherwise the region takes it.
|
|
41
|
-
// preventScroll: below `lg` the panel starts off-screen, and scrolling it into view would shift the layout.
|
|
42
|
-
if (!hasFocusInside)
|
|
43
|
-
panel.focus({ preventScroll: true });
|
|
44
|
-
}
|
|
45
|
-
// Hand focus back only if it is still in the panel, so a user who has moved on is left alone.
|
|
46
|
-
if (isLeaving && hasFocusInside) {
|
|
47
|
-
const opener = openerRef.current;
|
|
48
|
-
// The opener may have unmounted meanwhile, such as a header button that only renders at one breakpoint.
|
|
49
|
-
(opener?.isConnected ? opener : focusableSiblingBefore(panel))?.focus();
|
|
50
|
-
}
|
|
51
|
-
// A leaving panel must not be reachable while another opens; set last, since inert drops any focus inside it.
|
|
52
|
-
if (!isPresent)
|
|
53
|
-
panel.inert = true;
|
|
54
|
-
}, [panelRef, isPresent, shouldFocusOnMount, focusedBeforeMount]);
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
const hasStartedCoveringPage = previousIsDesktop.current && !isDesktop;
|
|
57
|
-
previousIsDesktop.current = isDesktop;
|
|
58
|
-
const panel = panelRef.current;
|
|
59
|
-
const focused = getFocusedElement();
|
|
60
|
-
// The page beside the panel is about to go out of reach, so focus must not be left in it.
|
|
61
|
-
if (hasStartedCoveringPage && panel && focused && !panel.contains(focused) && panel.parentElement?.contains(focused)) {
|
|
62
|
-
panel.focus({ preventScroll: true });
|
|
63
|
-
}
|
|
64
|
-
}, [panelRef, isDesktop]);
|
|
65
|
-
// Swapping content unmounts whatever held focus, which drops it on the page. The region takes it, so Tab stays inside and
|
|
66
|
-
// closing can still hand it back. Content that focuses itself on mount (an `autoFocus` input) has done so by now and keeps it.
|
|
67
|
-
const previousContentKey = useRef(contentKey);
|
|
68
|
-
useLayoutEffect(() => {
|
|
69
|
-
if (previousContentKey.current === contentKey)
|
|
70
|
-
return;
|
|
71
|
-
previousContentKey.current = contentKey;
|
|
72
|
-
const panel = panelRef.current;
|
|
73
|
-
if (panel && isPresent && getFocusedElement() === null)
|
|
74
|
-
panel.focus({ preventScroll: true });
|
|
75
|
-
}, [panelRef, contentKey, isPresent]);
|
|
76
|
-
// A panel that was open on load has no opener, so closing hands focus back to wherever it first came in from.
|
|
77
|
-
const onFocusCapture = (event) => {
|
|
78
|
-
const from = event.relatedTarget;
|
|
79
|
-
if (openerRef.current === null && from instanceof HTMLElement && !event.currentTarget.contains(from))
|
|
80
|
-
openerRef.current = from;
|
|
81
|
-
};
|
|
82
|
-
// A panel whose content swaps while open follows the user's latest gesture, so closing lands on the row they are on, not the first one.
|
|
83
|
-
const setOpener = useCallback((element) => {
|
|
84
|
-
openerRef.current = element;
|
|
85
|
-
}, []);
|
|
86
|
-
return { onFocusCapture, setOpener };
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
export { usePanelFocus };
|
|
90
|
-
//# sourceMappingURL=usePanelFocus.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"usePanelFocus.js","sources":["../../../../../src/components/organisms/SidePanel/hooks/usePanelFocus.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|