reshaped 3.4.7 → 3.5.0-rc.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/CHANGELOG.md +0 -28
- package/dist/bundle.css +1 -1
- package/dist/bundle.js +11 -11
- package/dist/components/Autocomplete/tests/Autocomplete.stories.d.ts +0 -1
- package/dist/components/Autocomplete/tests/Autocomplete.stories.js +3 -15
- package/dist/components/Badge/Badge.types.d.ts +4 -2
- package/dist/components/Button/Button.types.d.ts +4 -2
- package/dist/components/MenuItem/MenuItem.types.d.ts +4 -2
- package/dist/components/NumberField/NumberField.module.css +1 -1
- package/dist/components/NumberField/NumberField.types.d.ts +1 -1
- package/dist/components/NumberField/NumberFieldControlled.js +44 -28
- package/dist/components/NumberField/tests/NumberField.stories.d.ts +2 -1
- package/dist/components/NumberField/tests/NumberField.stories.js +30 -5
- package/dist/components/Overlay/Overlay.js +2 -2
- package/dist/components/TextField/TextField.module.css +1 -1
- package/dist/components/Toast/ToastContainer.js +2 -2
- package/dist/components/_private/Flyout/FlyoutControlled.js +2 -2
- package/dist/utilities/a11y/TrapFocus.d.ts +3 -17
- package/dist/utilities/a11y/TrapFocus.js +54 -49
- package/dist/utilities/a11y/tests/TrapFocus.stories.js +20 -20
- package/package.json +31 -31
@@ -16,18 +16,6 @@ export default {
|
|
16
16
|
},
|
17
17
|
},
|
18
18
|
};
|
19
|
-
export const foo = () => {
|
20
|
-
const [value, setValue] = React.useState("");
|
21
|
-
const options = ["Pizza", "Pie", "Ice-cream"];
|
22
|
-
return (<FormControl>
|
23
|
-
<FormControl.Label>Favorite food</FormControl.Label>
|
24
|
-
<Autocomplete name="fruit" placeholder="Pick your option" value={value} onChange={(args) => setValue(args.value)}>
|
25
|
-
{options.map((option) => (<Autocomplete.Item key={option} value={option}>
|
26
|
-
{option}
|
27
|
-
</Autocomplete.Item>))}
|
28
|
-
</Autocomplete>
|
29
|
-
</FormControl>);
|
30
|
-
};
|
31
19
|
export const active = {
|
32
20
|
name: "active, onOpen, onClose",
|
33
21
|
args: {
|
@@ -60,7 +48,7 @@ export const active = {
|
|
60
48
|
play: async ({ canvasElement, args }) => {
|
61
49
|
const canvas = within(canvasElement.ownerDocument.body);
|
62
50
|
const input = canvas.getByRole("combobox");
|
63
|
-
const list = canvas.
|
51
|
+
const list = await canvas.findByRole("listbox");
|
64
52
|
expect(list).toBeInTheDocument();
|
65
53
|
expect(args.handleOpen).not.toHaveBeenCalled();
|
66
54
|
await userEvent.click(document.body);
|
@@ -167,7 +155,7 @@ export const itemData = {
|
|
167
155
|
},
|
168
156
|
play: async ({ canvasElement, args }) => {
|
169
157
|
const canvas = within(canvasElement.ownerDocument.body);
|
170
|
-
const options = canvas.
|
158
|
+
const options = await canvas.findAllByRole("option");
|
171
159
|
await userEvent.click(options[0]);
|
172
160
|
expect(args.handleItemSelect).toHaveBeenLastCalledWith({ value: "Pizza" });
|
173
161
|
await userEvent.click(options[1]);
|
@@ -195,7 +183,7 @@ export const itemDisabled = {
|
|
195
183
|
play: async ({ canvasElement }) => {
|
196
184
|
const canvas = within(canvasElement.ownerDocument.body);
|
197
185
|
const input = canvas.getByRole("combobox");
|
198
|
-
const options = canvas.
|
186
|
+
const options = await canvas.findAllByRole("option");
|
199
187
|
await fireEvent.click(options[1]);
|
200
188
|
expect(options[1]).toBeDisabled();
|
201
189
|
// Check that focus stays on input when clicking on disabled elements
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import type React from "react";
|
2
|
-
import type { ActionableProps } from "../Actionable";
|
2
|
+
import type { ActionableProps, ActionableRef } from "../Actionable";
|
3
3
|
import type { IconProps } from "../Icon";
|
4
4
|
import type * as G from "../../types/global";
|
5
5
|
type BaseProps = {
|
@@ -36,7 +36,9 @@ export type ContainerProps = {
|
|
36
36
|
className?: G.ClassName;
|
37
37
|
attributes?: G.Attributes<"div">;
|
38
38
|
};
|
39
|
-
export type Export = React.ForwardRefExoticComponent<Props
|
39
|
+
export type Export = React.ForwardRefExoticComponent<Props & {
|
40
|
+
ref?: ActionableRef;
|
41
|
+
}> & {
|
40
42
|
Container: React.ComponentType<ContainerProps>;
|
41
43
|
};
|
42
44
|
export {};
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import type React from "react";
|
2
2
|
import type { IconProps } from "../Icon";
|
3
|
-
import type { ActionableProps } from "../Actionable";
|
3
|
+
import type { ActionableProps, ActionableRef } from "../Actionable";
|
4
4
|
import type { AlignerProps as BaseAlignerProps } from "../_private/Aligner";
|
5
5
|
import type * as G from "../../types/global";
|
6
6
|
export type Size = "xlarge" | "large" | "medium" | "small";
|
@@ -28,7 +28,9 @@ export type AlignerProps = BaseAlignerProps & {
|
|
28
28
|
*/
|
29
29
|
position?: BaseAlignerProps["side"];
|
30
30
|
};
|
31
|
-
export type Export = React.ForwardRefExoticComponent<Props
|
31
|
+
export type Export = React.ForwardRefExoticComponent<Props & {
|
32
|
+
ref?: ActionableRef;
|
33
|
+
}> & {
|
32
34
|
Aligner: React.ComponentType<AlignerProps>;
|
33
35
|
Group: React.ComponentType<GroupProps>;
|
34
36
|
};
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import type React from "react";
|
2
2
|
import type { IconProps } from "../Icon";
|
3
|
-
import type { ActionableProps } from "../Actionable";
|
3
|
+
import type { ActionableProps, ActionableRef } from "../Actionable";
|
4
4
|
import type * as G from "../../types/global";
|
5
5
|
export type Size = "small" | "medium" | "large";
|
6
6
|
export type Props = Pick<ActionableProps, "attributes" | "className" | "disabled" | "children" | "href" | "onClick" | "as" | "stopPropagation"> & {
|
@@ -18,6 +18,8 @@ export type AlignerProps = {
|
|
18
18
|
className?: G.ClassName;
|
19
19
|
attributes?: G.Attributes<"div">;
|
20
20
|
};
|
21
|
-
export type Export = React.ForwardRefExoticComponent<Props
|
21
|
+
export type Export = React.ForwardRefExoticComponent<Props & {
|
22
|
+
ref?: ActionableRef;
|
23
|
+
}> & {
|
22
24
|
Aligner: React.ComponentType<AlignerProps>;
|
23
25
|
};
|
@@ -1 +1 @@
|
|
1
|
-
.field{font-variant-numeric:tabular-nums}.controls{--rs-number-field-control-border-color:var(--rs-color-border-neutral);
|
1
|
+
.field{font-variant-numeric:tabular-nums}.controls-wrapper{display:block}.controls{--rs-number-field-control-border-color:var(--rs-color-border-neutral);display:flex;flex-direction:column;inset-block:calc(var(--rs-text-field-p-v) * -1 + var(--rs-unit-x1));inset-inline-end:0;position:absolute;transition:border-color var(--rs-duration-fast) var(--rs-easing-standard)}.controls:has([aria-disabled]+[aria-disabled]){--rs-number-field-control-border-color:var(--rs-color-border-disabled)}.controls--size-small .control,.controls--size-small .controls-wrapper{width:var(--rs-unit-x4)}@media (pointer:coarse) and (hover:none){.controls--size-small .controls-wrapper{width:54px}}.controls--size-medium .control,.controls--size-medium .controls-wrapper{width:var(--rs-unit-x5)}@media (pointer:coarse) and (hover:none){.controls--size-medium .controls-wrapper{width:70px}}.controls--size-large .control{width:var(--rs-unit-x6)}.controls--size-large .controls-wrapper{width:var(--rs-unit-x5)}@media (pointer:coarse) and (hover:none){.controls--size-large .controls-wrapper{width:94px}}.controls--size-xlarge .control{width:var(--rs-unit-x7)}.controls--size-xlarge .controls-wrapper{width:var(--rs-unit-x6)}@media (pointer:coarse) and (hover:none){.controls--size-xlarge .controls-wrapper{width:110px}}.control{align-items:center;display:flex;flex-grow:1;height:50%;justify-content:center;touch-action:manipulation;transition:color var(--rs-duration-fast) var(--rs-easing-standard)}.control[aria-disabled]{color:var(--rs-color-foreground-disabled)}.control:not([aria-disabled]):hover{background:rgba(var(--rs-color-rgb-background-neutral),32%)}.icon--touch{display:none!important}.--outline .controls{border-inline-start:1px solid var(--rs-number-field-control-border-color);inset-inline-end:1px}.--outline .control:first-child{box-shadow:0 1px var(--rs-number-field-control-border-color)}@media (pointer:coarse) and (hover:none){.controls{flex-direction:row-reverse}.control{aspect-ratio:1;box-sizing:content-box;height:100%;touch-action:manipulation;width:auto!important}.icon--touch{display:block!important}.icon--mouse{display:none!important}.--outline .control:first-child{border-inline-start:1px solid var(--rs-color-border-neutral);box-shadow:none}}@media (--rs-viewport-m ){.controls--size-small--m .control,.controls--size-small--m .controls-wrapper{width:var(--rs-unit-x4)}@media (pointer:coarse) and (hover:none){.controls--size-small--m .controls-wrapper{width:54px}}.controls--size-medium--m .control,.controls--size-medium--m .controls-wrapper{width:var(--rs-unit-x5)}@media (pointer:coarse) and (hover:none){.controls--size-medium--m .controls-wrapper{width:70px}}.controls--size-large--m .control{width:var(--rs-unit-x6)}.controls--size-large--m .controls-wrapper{width:var(--rs-unit-x5)}@media (pointer:coarse) and (hover:none){.controls--size-large--m .controls-wrapper{width:94px}}.controls--size-xlarge--m .control{width:var(--rs-unit-x7)}.controls--size-xlarge--m .controls-wrapper{width:var(--rs-unit-x6)}@media (pointer:coarse) and (hover:none){.controls--size-xlarge--m .controls-wrapper{width:110px}}}@media (--rs-viewport-l ){.controls--size-small--l .control,.controls--size-small--l .controls-wrapper{width:var(--rs-unit-x4)}@media (pointer:coarse) and (hover:none){.controls--size-small--l .controls-wrapper{width:54px}}.controls--size-medium--l .control,.controls--size-medium--l .controls-wrapper{width:var(--rs-unit-x5)}@media (pointer:coarse) and (hover:none){.controls--size-medium--l .controls-wrapper{width:70px}}.controls--size-large--l .control{width:var(--rs-unit-x6)}.controls--size-large--l .controls-wrapper{width:var(--rs-unit-x5)}@media (pointer:coarse) and (hover:none){.controls--size-large--l .controls-wrapper{width:94px}}.controls--size-xlarge--l .control{width:var(--rs-unit-x7)}.controls--size-xlarge--l .controls-wrapper{width:var(--rs-unit-x6)}@media (pointer:coarse) and (hover:none){.controls--size-xlarge--l .controls-wrapper{width:110px}}}@media (--rs-viewport-xl ){.controls--size-small--xl .control,.controls--size-small--xl .controls-wrapper{width:var(--rs-unit-x4)}@media (pointer:coarse) and (hover:none){.controls--size-small--xl .controls-wrapper{width:54px}}.controls--size-medium--xl .control,.controls--size-medium--xl .controls-wrapper{width:var(--rs-unit-x5)}@media (pointer:coarse) and (hover:none){.controls--size-medium--xl .controls-wrapper{width:70px}}.controls--size-large--xl .control{width:var(--rs-unit-x6)}.controls--size-large--xl .controls-wrapper{width:var(--rs-unit-x5)}@media (pointer:coarse) and (hover:none){.controls--size-large--xl .controls-wrapper{width:94px}}.controls--size-xlarge--xl .control{width:var(--rs-unit-x7)}.controls--size-xlarge--xl .controls-wrapper{width:var(--rs-unit-x6)}@media (pointer:coarse) and (hover:none){.controls--size-xlarge--xl .controls-wrapper{width:110px}}}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import type { TextFieldBaseProps } from "../TextField";
|
2
2
|
import type * as G from "../../types/global";
|
3
|
-
export type BaseProps = Omit<TextFieldBaseProps, "endSlot" | "onChange"> & {
|
3
|
+
export type BaseProps = Omit<TextFieldBaseProps, "endSlot" | "onChange" | "rounded" | "multiline"> & {
|
4
4
|
onChange?: G.ChangeHandler<number>;
|
5
5
|
increaseAriaLabel: string;
|
6
6
|
decreaseAriaLabel: string;
|
@@ -4,18 +4,19 @@ import React from "react";
|
|
4
4
|
import Actionable from "../Actionable/index.js";
|
5
5
|
import Icon from "../Icon/index.js";
|
6
6
|
import TextField from "../TextField/index.js";
|
7
|
+
import { useFormControl } from "../FormControl/index.js";
|
7
8
|
import IconChevronUp from "../../icons/ChevronUp.js";
|
8
9
|
import IconChevronDown from "../../icons/ChevronDown.js";
|
9
10
|
import IconPlus from "../../icons/Plus.js";
|
10
11
|
import IconMinus from "../../icons/Minus.js";
|
11
12
|
import useElementId from "../../hooks/useElementId.js";
|
12
13
|
import useHotkeys from "../../hooks/useHotkeys.js";
|
14
|
+
import useHandlerRef from "../../hooks/useHandlerRef.js";
|
13
15
|
import * as keys from "../../constants/keys.js";
|
16
|
+
import { responsiveClassNames, responsivePropDependency } from "../../utilities/helpers.js";
|
14
17
|
import s from "./NumberField.module.css";
|
15
|
-
import useHandlerRef from "../../hooks/useHandlerRef.js";
|
16
|
-
import { useFormControl } from "../FormControl/index.js";
|
17
18
|
const NumberFieldControlled = (props) => {
|
18
|
-
const { increaseAriaLabel, decreaseAriaLabel, min, max, step = 1, name, value, onChange, ...textFieldProps } = props;
|
19
|
+
const { increaseAriaLabel, decreaseAriaLabel, min, max, step = 1, name, value, onChange, size = "medium", ...textFieldProps } = props;
|
19
20
|
const formControl = useFormControl();
|
20
21
|
const id = useElementId(textFieldProps.id);
|
21
22
|
const inputId = formControl?.attributes.id || props.inputAttributes?.id || id;
|
@@ -62,8 +63,12 @@ const NumberFieldControlled = (props) => {
|
|
62
63
|
const handleChange = (args) => {
|
63
64
|
if (!args.value.match(/^(-?)[0-9]*(\.?)[0-9]*$/))
|
64
65
|
return;
|
65
|
-
setTextValue(args.value);
|
66
66
|
const numberValue = parseFloat(args.value);
|
67
|
+
if (numberValue > Number.MAX_SAFE_INTEGER)
|
68
|
+
return;
|
69
|
+
if (numberValue < Number.MIN_SAFE_INTEGER)
|
70
|
+
return;
|
71
|
+
setTextValue(args.value);
|
67
72
|
if (isNaN(numberValue))
|
68
73
|
return;
|
69
74
|
commitValue(numberValue);
|
@@ -104,32 +109,43 @@ const NumberFieldControlled = (props) => {
|
|
104
109
|
valueRef.current = value;
|
105
110
|
setTextValue(value?.toString() ?? "");
|
106
111
|
}, [value]);
|
107
|
-
const
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
112
|
+
const mouseIconSize = responsivePropDependency(size, (size) => {
|
113
|
+
return size === "large" || size === "xlarge" ? 4 : 3;
|
114
|
+
});
|
115
|
+
const touchIconSize = responsivePropDependency(size, (size) => {
|
116
|
+
return size === "small" ? 3 : 4;
|
117
|
+
});
|
118
|
+
const controlsNode = (_jsx("span", { className: s["controls-wrapper"], children: _jsxs("span", { className: s.controls, children: [_jsxs(Actionable, { className: s.control, disabled: increaseDisabled, disableFocusRing: true, as: "span", attributes: {
|
119
|
+
"aria-label": increaseAriaLabel,
|
120
|
+
"aria-controls": inputId,
|
121
|
+
role: "button",
|
122
|
+
tabIndex: increaseDisabled ? undefined : -1,
|
123
|
+
onPointerDown: (e) => handleControlPointerDown(e, handleIncrease),
|
124
|
+
onPointerUp: handleControlPointerUp,
|
125
|
+
onPointerLeave: handleControlPointerUp,
|
126
|
+
// Prevent menu from opening on long press
|
127
|
+
onContextMenu: (e) => e.preventDefault(),
|
128
|
+
}, children: [_jsx(Icon, { svg: IconChevronUp, size: mouseIconSize, className: s["icon--mouse"] }), _jsx(Icon, { svg: IconPlus, size: touchIconSize, className: s["icon--touch"] })] }), _jsxs(Actionable, { className: s.control, disabled: decreaseDisabled, disableFocusRing: true, as: "span", attributes: {
|
129
|
+
"aria-label": decreaseAriaLabel,
|
130
|
+
"aria-controls": inputId,
|
131
|
+
role: "button",
|
132
|
+
tabIndex: decreaseDisabled ? undefined : -1,
|
133
|
+
onPointerDown: (e) => handleControlPointerDown(e, handleDecrease),
|
134
|
+
onPointerUp: handleControlPointerUp,
|
135
|
+
onPointerLeave: handleControlPointerUp,
|
136
|
+
// Prevent menu from opening on long press
|
137
|
+
onContextMenu: (e) => e.preventDefault(),
|
138
|
+
}, children: [_jsx(Icon, { svg: IconChevronDown, size: mouseIconSize, className: s["icon--mouse"] }), _jsx(Icon, { svg: IconMinus, size: touchIconSize, className: s["icon--touch"] })] })] }) }));
|
139
|
+
return (_jsx(TextField, { ...textFieldProps, className: [
|
140
|
+
textFieldProps.className,
|
141
|
+
responsiveClassNames(s, "controls--size", size),
|
142
|
+
!(textFieldProps.variant === "faded" || textFieldProps.variant === "headless") &&
|
143
|
+
s["--outline"],
|
144
|
+
], attributes: {
|
129
145
|
...textFieldProps.attributes,
|
130
146
|
role: "group",
|
131
147
|
ref: rootRef,
|
132
|
-
},
|
148
|
+
}, inputAttributes: {
|
133
149
|
...textFieldProps.inputAttributes,
|
134
150
|
ref: inputRef,
|
135
151
|
inputMode: "numeric",
|
@@ -140,7 +156,7 @@ const NumberFieldControlled = (props) => {
|
|
140
156
|
max,
|
141
157
|
step,
|
142
158
|
className: s.field,
|
143
|
-
},
|
159
|
+
}, size: size, id: inputId, hasError: hasError, disabled: disabled, value: textValue, onChange: handleChange, name: name, endSlot: controlsNode }));
|
144
160
|
};
|
145
161
|
NumberFieldControlled.displayName = "NumberFieldControlled";
|
146
162
|
export default NumberFieldControlled;
|
@@ -13,7 +13,8 @@ declare const _default: {
|
|
13
13
|
};
|
14
14
|
};
|
15
15
|
export default _default;
|
16
|
-
export declare const
|
16
|
+
export declare const variant: StoryObj;
|
17
|
+
export declare const size: StoryObj;
|
17
18
|
export declare const disabled: StoryObj<{
|
18
19
|
handleChange: Mock;
|
19
20
|
}>;
|
@@ -11,23 +11,48 @@ export default {
|
|
11
11
|
},
|
12
12
|
},
|
13
13
|
};
|
14
|
-
export const
|
15
|
-
name: "
|
14
|
+
export const variant = {
|
15
|
+
name: "variant",
|
16
16
|
render: () => {
|
17
17
|
return (<Example>
|
18
|
-
<Example.Item title="
|
18
|
+
<Example.Item title="variant: outline">
|
19
19
|
<NumberField name="test-name" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" inputAttributes={{ "aria-label": "Label" }}/>
|
20
20
|
</Example.Item>
|
21
|
+
<Example.Item title="variant: faded">
|
22
|
+
<NumberField variant="faded" name="test-name" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" inputAttributes={{ "aria-label": "Label" }}/>
|
23
|
+
</Example.Item>
|
24
|
+
<Example.Item title="variant: headless">
|
25
|
+
<NumberField variant="headless" name="test-name" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" inputAttributes={{ "aria-label": "Label" }}/>
|
26
|
+
</Example.Item>
|
21
27
|
</Example>);
|
22
28
|
},
|
23
29
|
play: async ({ canvas }) => {
|
24
|
-
const input = canvas.
|
30
|
+
const input = canvas.getAllByRole("textbox")[0];
|
25
31
|
const [increaseButton, decreaseButton] = canvas.getAllByRole("button");
|
26
32
|
expect(input).toHaveAttribute("name", "test-name");
|
27
33
|
expect(increaseButton).toHaveAccessibleName("Increase");
|
28
34
|
expect(decreaseButton).toHaveAccessibleName("Decrease");
|
29
35
|
},
|
30
36
|
};
|
37
|
+
export const size = {
|
38
|
+
name: "size",
|
39
|
+
render: () => {
|
40
|
+
return (<Example>
|
41
|
+
<Example.Item title="size: small">
|
42
|
+
<NumberField size="small" name="test-name" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" suffix="m2" inputAttributes={{ "aria-label": "Label" }}/>
|
43
|
+
</Example.Item>
|
44
|
+
<Example.Item title="size: medium">
|
45
|
+
<NumberField size="medium" name="test-name" suffix="m2" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" inputAttributes={{ "aria-label": "Label" }}/>
|
46
|
+
</Example.Item>
|
47
|
+
<Example.Item title="size: large">
|
48
|
+
<NumberField size="large" name="test-name" suffix="m2" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" inputAttributes={{ "aria-label": "Label" }}/>
|
49
|
+
</Example.Item>
|
50
|
+
<Example.Item title="size: xlarge">
|
51
|
+
<NumberField size="xlarge" name="test-name" suffix="m2" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" inputAttributes={{ "aria-label": "Label" }}/>
|
52
|
+
</Example.Item>
|
53
|
+
</Example>);
|
54
|
+
},
|
55
|
+
};
|
31
56
|
export const disabled = {
|
32
57
|
name: "disabled",
|
33
58
|
args: {
|
@@ -176,7 +201,7 @@ export const valueChanges = {
|
|
176
201
|
name: "test: keyboard",
|
177
202
|
render: () => (<Example>
|
178
203
|
<Example.Item title="keyboard">
|
179
|
-
<NumberField name="name" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease"/>
|
204
|
+
<NumberField name="name" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" inputAttributes={{ "aria-label": "Label" }}/>
|
180
205
|
</Example.Item>
|
181
206
|
</Example>),
|
182
207
|
play: async ({ canvas }) => {
|
@@ -97,7 +97,7 @@ const Overlay = (props) => {
|
|
97
97
|
React.useEffect(() => {
|
98
98
|
if (!rendered || !contentRef.current)
|
99
99
|
return;
|
100
|
-
const trapFocus = new TrapFocus(
|
100
|
+
const trapFocus = new TrapFocus();
|
101
101
|
const containerEl = containerRef?.current;
|
102
102
|
if (containerEl) {
|
103
103
|
originalOverflowRef.current = containerEl.style.overflow;
|
@@ -105,7 +105,7 @@ const Overlay = (props) => {
|
|
105
105
|
containerEl.style.isolation = "isolate";
|
106
106
|
setOffset([containerEl.scrollLeft, containerEl.scrollTop]);
|
107
107
|
}
|
108
|
-
trapFocus.trap({
|
108
|
+
trapFocus.trap(contentRef.current, {
|
109
109
|
initialFocusEl: contentRef.current.querySelector("[role=dialog][tabindex='-1']"),
|
110
110
|
});
|
111
111
|
onOpenRef.current?.();
|
@@ -1 +1 @@
|
|
1
|
-
.root{--rs-p-h:var(--rs-text-field-gap);align-items:center;background:var(--rs-color-background-elevation-base);border:1px solid var(--rs-color-border-neutral);border-radius:var(--rs-text-field-radius);display:flex;gap:var(--rs-text-field-gap);padding:0 calc(var(--rs-text-field-gap) - 1px);position:relative;row-gap:var(--rs-unit-x1);z-index:0}.root.--focused,.root:has(label:active),.root:not(:has(button:focus,a:focus,[tabindex="0"]:focus,[role=button]:focus)):focus-within{border-color:var(--rs-color-border-primary);box-shadow:0 0 0 1px var(--rs-color-border-primary)}.root.--multiline{flex-wrap:wrap}.root.--multiline .input{width:auto}.root.--rounded{border-radius:999px}.root.--rounded .affix:first-child,.root.--rounded .icon:first-child{padding-inline-start:var(--rs-unit-x1)}.root.--rounded .input:first-child{padding-inline-start:calc(var(--rs-text-field-gap) + var(--rs-unit-x1))}.input{background:none;border:none;box-sizing:border-box;color:var(--rs-color-foreground-neutral);flex-grow:1;font-family:var(--rs-font-family-body);font-weight:var(--rs-font-weight-regular);margin:0 calc(var(--rs-text-field-gap) * -1);outline:none;padding-block:calc(var(--rs-text-field-p-v) - 1px);padding-inline:var(--rs-text-field-gap);position:relative;width:100%;z-index:1}.input:-webkit-autofill{-webkit-background-clip:text;-webkit-text-fill-color:var(--rs-color-foreground-neutral)}.affix,.input{font-size:var(--rs-text-field-font-size);letter-spacing:var(--rs-text-field-letter-spacing);line-height:var(--rs-text-field-line-height)}.affix,.icon{cursor:text}.affix,.icon,.slot{align-items:center;display:flex;flex-shrink:0;max-width:100%;min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) - 2px);position:relative;z-index:5}.slot--position-end:last-child{margin-inline-end:calc(var(--rs-text-field-gap) * -1);padding-inline-end:var(--rs-text-field-action-inset)}.affix{color:var(--rs-color-foreground-neutral-faded)}.affix.affix--position-start{padding-inline-end:var(--rs-text-field-gap)}.affix.affix--position-start:after{border-inline-end:1px solid var(--rs-color-border-neutral-faded);content:"";inset-block:var(--rs-unit-x1);inset-inline-end:0;position:absolute}.affix.affix--position-end{padding-inline-start:var(--rs-text-field-gap)}.affix.affix--position-end:after{border-inline-start:1px solid var(--rs-color-border-neutral-faded);content:"";inset-block:var(--rs-unit-x1);inset-inline-start:0;position:absolute}.root.--disabled{background:var(--rs-color-background-disabled-faded);border-color:var(--rs-color-border-disabled)}.root.--disabled,.root.--disabled .input{color:var(--rs-color-foreground-disabled);cursor:not-allowed}.--size-small{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-medium{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-large{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-
|
1
|
+
.root{--rs-p-h:var(--rs-text-field-gap);align-items:center;background:var(--rs-color-background-elevation-base);border:1px solid var(--rs-color-border-neutral);border-radius:var(--rs-text-field-radius);display:flex;gap:var(--rs-text-field-gap);padding:0 calc(var(--rs-text-field-gap) - 1px);position:relative;row-gap:var(--rs-unit-x1);z-index:0}.root.--focused,.root:has(label:active),.root:not(:has(button:focus,a:focus,[tabindex="0"]:focus,[role=button]:focus)):focus-within{border-color:var(--rs-color-border-primary);box-shadow:0 0 0 1px var(--rs-color-border-primary)}.root.--multiline{flex-wrap:wrap}.root.--multiline .input{width:auto}.root.--rounded{border-radius:999px}.root.--rounded .affix:first-child,.root.--rounded .icon:first-child{padding-inline-start:var(--rs-unit-x1)}.root.--rounded .input:first-child{padding-inline-start:calc(var(--rs-text-field-gap) + var(--rs-unit-x1))}.input{background:none;border:none;box-sizing:border-box;color:var(--rs-color-foreground-neutral);flex-grow:1;font-family:var(--rs-font-family-body);font-weight:var(--rs-font-weight-regular);margin:0 calc(var(--rs-text-field-gap) * -1);outline:none;padding-block:calc(var(--rs-text-field-p-v) - 1px);padding-inline:var(--rs-text-field-gap);position:relative;width:100%;z-index:1}.input:-webkit-autofill{-webkit-background-clip:text;-webkit-text-fill-color:var(--rs-color-foreground-neutral)}.affix,.input{font-size:var(--rs-text-field-font-size);letter-spacing:var(--rs-text-field-letter-spacing);line-height:var(--rs-text-field-line-height)}.affix,.icon{cursor:text}.affix,.icon,.slot{align-items:center;display:flex;flex-shrink:0;max-width:100%;min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) - 2px);position:relative;z-index:5}.slot--position-end:last-child{margin-inline-end:calc(var(--rs-text-field-gap) * -1);padding-inline-end:var(--rs-text-field-action-inset)}.affix{color:var(--rs-color-foreground-neutral-faded)}.affix.affix--position-start{padding-inline-end:var(--rs-text-field-gap)}.affix.affix--position-start:after{border-inline-end:1px solid var(--rs-color-border-neutral-faded);content:"";inset-block:var(--rs-unit-x1);inset-inline-end:0;position:absolute}.affix.affix--position-end{padding-inline-start:var(--rs-text-field-gap)}.affix.affix--position-end:after{border-inline-start:1px solid var(--rs-color-border-neutral-faded);content:"";inset-block:var(--rs-unit-x1);inset-inline-start:0;position:absolute}.root.--disabled{background:var(--rs-color-background-disabled-faded);border-color:var(--rs-color-border-disabled)}.root.--disabled,.root.--disabled .input{color:var(--rs-color-foreground-disabled);cursor:not-allowed}.--size-small{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-medium{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-large{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2)}.--size-xlarge{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1)}.root.--variant-faded{background:var(--rs-color-background-neutral-faded);border-color:transparent}.root.--variant-faded.--focused,.root.--variant-faded:has(label:active),.root.--variant-faded:not(:has(button:focus,a:focus,[tabindex="0"]:focus,[role=button]:focus)):focus-within{border-color:var(--rs-color-border-primary)}.root.--variant-headless{background:transparent;border-color:transparent}.root.--variant-headless.--status-error,.root.--variant-headless.--status-error.--focused,.root.--variant-headless.--status-error:focus-within,.root.--variant-headless:focus-within{border-color:transparent;box-shadow:none}.root.--status-error{border-color:var(--rs-color-border-critical)}.root.--status-error.--focused,.root.--status-error:focus-within{border-color:var(--rs-color-border-primary)}@media (--rs-viewport-s ) and (hover:none){.input{font-size:var(--rs-font-size-body-2)!important}}@media (--rs-viewport-m ){.--size-small--m{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-medium--m{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-large--m{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2)}.--size-xlarge--m{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1)}}@media (--rs-viewport-l ){.--size-small--l{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-medium--l{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-large--l{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2)}.--size-xlarge--l{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1)}}@media (--rs-viewport-xl ){.--size-small--xl{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-medium--xl{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-large--xl{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2)}.--size-xlarge--xl{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1)}}
|
@@ -59,9 +59,9 @@ const ToastContainer = (props) => {
|
|
59
59
|
React.useEffect(() => {
|
60
60
|
if (!wrapperRef.current)
|
61
61
|
return;
|
62
|
-
const trapFocus = new TrapFocus(
|
62
|
+
const trapFocus = new TrapFocus();
|
63
63
|
if (visible) {
|
64
|
-
trapFocus.trap({
|
64
|
+
trapFocus.trap(wrapperRef.current, {
|
65
65
|
includeTrigger: true,
|
66
66
|
mode: "content-menu",
|
67
67
|
});
|
@@ -231,8 +231,8 @@ const FlyoutControlled = (props) => {
|
|
231
231
|
return;
|
232
232
|
if (trapFocusRef.current?.trapped)
|
233
233
|
return;
|
234
|
-
trapFocusRef.current = new TrapFocus(
|
235
|
-
trapFocusRef.current.trap({
|
234
|
+
trapFocusRef.current = new TrapFocus();
|
235
|
+
trapFocusRef.current.trap(flyoutElRef.current, {
|
236
236
|
mode: trapFocusMode,
|
237
237
|
initialFocusEl: initialFocusRef?.current,
|
238
238
|
includeTrigger: triggerType === "hover" && trapFocusMode !== "dialog" && !isSubmenu,
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import Chain from "../Chain";
|
2
|
-
import TrapScreenReader from "./TrapScreenReader";
|
3
2
|
import type { FocusableElement, TrapMode } from "./types";
|
4
3
|
type ReleaseOptions = {
|
5
4
|
withoutFocusReturn?: boolean;
|
@@ -11,28 +10,15 @@ type TrapOptions = {
|
|
11
10
|
mode?: TrapMode;
|
12
11
|
};
|
13
12
|
declare class TrapFocus {
|
13
|
+
#private;
|
14
14
|
static chain: Chain<TrapFocus>;
|
15
|
-
chainId?: number;
|
16
|
-
root: HTMLElement;
|
17
|
-
trigger: FocusableElement | null;
|
18
|
-
options: TrapOptions & {
|
19
|
-
pseudoFocus?: boolean;
|
20
|
-
};
|
21
15
|
trapped?: boolean;
|
22
|
-
|
23
|
-
mutationObserver: MutationObserver | null;
|
24
|
-
constructor(root: HTMLElement);
|
25
|
-
/**
|
26
|
-
* Handle keyboard navigation while focus is trapped
|
27
|
-
*/
|
28
|
-
handleKeyDown: (event: KeyboardEvent) => void;
|
29
|
-
addListeners: () => void;
|
30
|
-
removeListeners: () => void;
|
16
|
+
constructor();
|
31
17
|
/**
|
32
18
|
* Trap the focus, add observer and keyboard event listeners
|
33
19
|
* and create a chain item
|
34
20
|
*/
|
35
|
-
trap: (options?: TrapOptions) => void;
|
21
|
+
trap: (root: HTMLElement, options?: TrapOptions) => void;
|
36
22
|
/**
|
37
23
|
* Disabled the trap focus for the element,
|
38
24
|
* cleanup all observers/handlers and trap for the previous element in the chain
|
@@ -1,3 +1,4 @@
|
|
1
|
+
var _a;
|
1
2
|
import Chain from "../Chain.js";
|
2
3
|
import * as keys from "../../constants/keys.js";
|
3
4
|
import TrapScreenReader from "./TrapScreenReader.js";
|
@@ -6,26 +7,25 @@ import { getShadowRoot } from "../dom/index.js";
|
|
6
7
|
import { checkKeyboardMode } from "./keyboardMode.js";
|
7
8
|
class TrapFocus {
|
8
9
|
static chain = new Chain();
|
9
|
-
chainId;
|
10
|
-
root;
|
11
|
-
trigger = null;
|
12
|
-
options = {};
|
10
|
+
#chainId;
|
11
|
+
#root = null;
|
12
|
+
#trigger = null;
|
13
|
+
#options = {};
|
13
14
|
trapped;
|
14
|
-
screenReaderTrap;
|
15
|
-
mutationObserver = null;
|
16
|
-
constructor(
|
17
|
-
this.root = root;
|
18
|
-
this.screenReaderTrap = new TrapScreenReader(root);
|
19
|
-
}
|
15
|
+
#screenReaderTrap = null;
|
16
|
+
#mutationObserver = null;
|
17
|
+
constructor() { }
|
20
18
|
/**
|
21
19
|
* Handle keyboard navigation while focus is trapped
|
22
20
|
*/
|
23
|
-
handleKeyDown = (event) => {
|
21
|
+
#handleKeyDown = (event) => {
|
24
22
|
if (event.defaultPrevented)
|
25
23
|
return;
|
26
|
-
if (
|
24
|
+
if (_a.chain.tailId !== this.#chainId)
|
27
25
|
return;
|
28
|
-
|
26
|
+
if (!this.#root)
|
27
|
+
return;
|
28
|
+
const { mode, onRelease, pseudoFocus, includeTrigger } = this.#options;
|
29
29
|
let navigationMode = "tabs";
|
30
30
|
if (mode === "action-menu" || mode === "selection-menu" || mode === "action-bar") {
|
31
31
|
navigationMode = "arrows";
|
@@ -39,12 +39,12 @@ class TrapFocus {
|
|
39
39
|
const isNextArrow = navigationMode === "arrows" && key === (mode === "action-bar" ? keys.RIGHT : keys.DOWN);
|
40
40
|
const isPrev = (isPrevTab && navigationMode === "tabs") || isPrevArrow;
|
41
41
|
const isNext = (isNextTab && navigationMode === "tabs") || isNextArrow;
|
42
|
-
const isFocusedOnTrigger = getActiveElement(this
|
42
|
+
const isFocusedOnTrigger = getActiveElement(this.#root) === this.#trigger;
|
43
43
|
const focusData = getFocusData({
|
44
|
-
root: this
|
44
|
+
root: this.#root,
|
45
45
|
target: isPrev ? "prev" : "next",
|
46
46
|
options: {
|
47
|
-
additionalElement: includeTrigger ? this
|
47
|
+
additionalElement: includeTrigger ? this.#trigger : undefined,
|
48
48
|
circular: mode !== "action-menu" && mode !== "action-bar",
|
49
49
|
},
|
50
50
|
});
|
@@ -70,53 +70,57 @@ class TrapFocus {
|
|
70
70
|
return;
|
71
71
|
focusElement(focusData.el, { pseudoFocus });
|
72
72
|
};
|
73
|
-
addListeners = () => {
|
74
|
-
const shadowRoot = getShadowRoot(this
|
73
|
+
#addListeners = () => {
|
74
|
+
const shadowRoot = getShadowRoot(this.#root);
|
75
75
|
const el = shadowRoot ?? document;
|
76
|
-
el.addEventListener("keydown", this
|
76
|
+
el.addEventListener("keydown", this.#handleKeyDown);
|
77
77
|
};
|
78
|
-
removeListeners = () => {
|
79
|
-
const shadowRoot = getShadowRoot(this
|
78
|
+
#removeListeners = () => {
|
79
|
+
const shadowRoot = getShadowRoot(this.#root);
|
80
80
|
const el = shadowRoot ?? document;
|
81
|
-
el.removeEventListener("keydown", this
|
81
|
+
el.removeEventListener("keydown", this.#handleKeyDown);
|
82
82
|
};
|
83
83
|
/**
|
84
84
|
* Trap the focus, add observer and keyboard event listeners
|
85
85
|
* and create a chain item
|
86
86
|
*/
|
87
|
-
trap = (options = {}) => {
|
87
|
+
trap = (root, options = {}) => {
|
88
88
|
const { mode = "dialog", includeTrigger, initialFocusEl } = options;
|
89
|
-
|
90
|
-
|
89
|
+
this.#root = root;
|
90
|
+
this.#screenReaderTrap = new TrapScreenReader(root);
|
91
|
+
const trigger = getActiveElement(this.#root);
|
92
|
+
const focusable = getFocusableElements(this.#root, {
|
91
93
|
additionalElement: includeTrigger ? trigger : undefined,
|
92
94
|
});
|
93
95
|
const pseudoFocus = mode === "selection-menu";
|
94
|
-
this
|
95
|
-
this
|
96
|
-
this
|
97
|
-
|
96
|
+
this.#options = { ...options, pseudoFocus };
|
97
|
+
this.#trigger = trigger;
|
98
|
+
this.#mutationObserver = new MutationObserver(() => {
|
99
|
+
if (!this.#root)
|
100
|
+
return;
|
101
|
+
const currentActiveElement = getActiveElement(this.#root);
|
98
102
|
// Focus stayed inside the wrapper, no need to refocus
|
99
|
-
if (this
|
103
|
+
if (this.#root.contains(currentActiveElement))
|
100
104
|
return;
|
101
|
-
const focusable = getFocusableElements(this
|
105
|
+
const focusable = getFocusableElements(this.#root, {
|
102
106
|
additionalElement: includeTrigger ? trigger : undefined,
|
103
107
|
});
|
104
108
|
if (!focusable.length)
|
105
109
|
return;
|
106
110
|
focusElement(focusable[0], { pseudoFocus });
|
107
111
|
});
|
108
|
-
this
|
109
|
-
this
|
112
|
+
this.#removeListeners();
|
113
|
+
this.#mutationObserver.observe(this.#root, { childList: true, subtree: true });
|
110
114
|
// Don't trap in case there is nothing to focus inside
|
111
115
|
if (!focusable.length && !initialFocusEl)
|
112
116
|
return;
|
113
|
-
this
|
117
|
+
this.#addListeners();
|
114
118
|
if (mode === "dialog")
|
115
|
-
this
|
119
|
+
this.#screenReaderTrap.trap();
|
116
120
|
// Don't add back to the chain if we're traversing back
|
117
|
-
const tailItem =
|
118
|
-
if (!tailItem || this
|
119
|
-
this
|
121
|
+
const tailItem = _a.chain.tailId && _a.chain.get(_a.chain.tailId);
|
122
|
+
if (!tailItem || this.#root !== tailItem.data.#root) {
|
123
|
+
this.#chainId = _a.chain.add(this);
|
120
124
|
focusElement(initialFocusEl || focusable[0], { pseudoFocus });
|
121
125
|
}
|
122
126
|
this.trapped = true;
|
@@ -127,21 +131,22 @@ class TrapFocus {
|
|
127
131
|
*/
|
128
132
|
release = (releaseOptions = {}) => {
|
129
133
|
const { withoutFocusReturn } = releaseOptions;
|
130
|
-
if (!this.trapped || !this
|
134
|
+
if (!this.trapped || !this.#chainId || !this.#root)
|
131
135
|
return;
|
132
136
|
this.trapped = false;
|
133
|
-
if (this
|
134
|
-
this
|
137
|
+
if (this.#trigger && !withoutFocusReturn) {
|
138
|
+
this.#trigger.focus({ preventScroll: !checkKeyboardMode() });
|
135
139
|
}
|
136
|
-
|
137
|
-
this
|
138
|
-
this
|
139
|
-
this
|
140
|
-
const previousItem =
|
141
|
-
if (previousItem) {
|
142
|
-
const trapInstance = new
|
143
|
-
trapInstance.trap(previousItem.data.options);
|
140
|
+
_a.chain.removePreviousTill(this.#chainId, (item) => document.body.contains(item.data.#trigger));
|
141
|
+
this.#mutationObserver?.disconnect();
|
142
|
+
this.#removeListeners();
|
143
|
+
this.#screenReaderTrap?.release();
|
144
|
+
const previousItem = _a.chain.tailId && _a.chain.get(_a.chain.tailId);
|
145
|
+
if (previousItem && previousItem.data.#root) {
|
146
|
+
const trapInstance = new _a();
|
147
|
+
trapInstance.trap(previousItem.data.#root, previousItem.data.#options);
|
144
148
|
}
|
145
149
|
};
|
146
150
|
}
|
151
|
+
_a = TrapFocus;
|
147
152
|
export default TrapFocus;
|