@mui/utils 6.1.4 → 6.1.6

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 CHANGED
@@ -1,5 +1,105 @@
1
1
  # [Versions](https://mui.com/versions/)
2
2
 
3
+ ## 6.1.6
4
+
5
+ <!-- generated comparing v6.1.5..master -->
6
+
7
+ _Oct 30, 2024_
8
+
9
+ A big thanks to the 13 contributors who made this release possible.
10
+
11
+ ### `@mui/material@6.1.6`
12
+
13
+ - [Autocomplete] Add missing `onMouseDown` type to AutocompleteRenderInputParams (#44183) @sai6855
14
+ - [Avatar] Fix AvatarGroup spacing (#44208) @aarongarciah
15
+ - [AvatarGroup] Fix spacing CSS variable (#44202) @navedqb
16
+ - [Divider] Fix CSS specificity order (#44204) @o-alexandrov
17
+ - [Slider] Fix value prop type warning (#44131) @joshkel
18
+ - Replace `useThemeProps` with `useDefaultProps` (#44193) @siriwatknp
19
+
20
+ ### `@mui/material-nextjs@6.1.6`
21
+
22
+ - Support Next 15.0.0 (#42428) @nphmuller
23
+
24
+ ### `@mui/lab@6.0.0-beta.14`
25
+
26
+ - [Tabs] Fix type of TabPanel component (#44207) @blackcow1987
27
+
28
+ ### `@mui/codemod@6.1.6`
29
+
30
+ - Fix system props default import specifier (#44170) @siriwatknp
31
+
32
+ ### `@mui/utils@6.1.6`
33
+
34
+ - Bring back useIsFocusVisible (#44256) @aarongarciah
35
+ - Bring back getReactNodeRef (#44248) @aarongarciah
36
+
37
+ ### Docs
38
+
39
+ - [material-ui][Avatar] Add AvatarGroup spacing demo (#44209) @aarongarciah
40
+ - Fix a typo in CONTRIBUTING.md (#44200) @prakhargupta1
41
+ - Mark the Hidden component as deprecated in the sidenav (#44068) @jimmycallin
42
+ - Use () when referencing functions (#44184) @oliviertassinari
43
+ - Follow types description convention (#44187) @oliviertassinari
44
+
45
+ ### Core
46
+
47
+ - Lock file maintenance (#43947)
48
+ - Run @mui/icon-material src:icons (#44097) @oliviertassinari
49
+ - [test][material-ui] Add tests for Pigment Grid and Stack (#44132) @DiegoAndai
50
+ - [test] Distinguish private with public tests API (#44188) @oliviertassinari
51
+ - [docs-infra] Add recursively the relative modules in the demos (#44150) @mnajdova
52
+
53
+ All contributors of this release in alphabetical order: @aarongarciah, @blackcow1987, @DiegoAndai, @jimmycallin, @joshkel, @mnajdova, @navedqb, @nphmuller, @o-alexandrov, @oliviertassinari, @prakhargupta1, @sai6855, @siriwatknp
54
+
55
+ ## 6.1.5
56
+
57
+ <!-- generated comparing v6.1.4..master -->
58
+
59
+ _Oct 22, 2024_
60
+
61
+ A big thanks to the 9 contributors who made this release possible.
62
+
63
+ ### `@mui/material@6.1.5`
64
+
65
+ - [Autocomplete] Fix bug with child chip button events propagating to parent (#43982) @snapwich
66
+ - [Autocomplete] Fix Autocomplete crashing if ownerState is used in styleOverrides (#43994) @sai6855
67
+ - [Checkbox] Fix disableRipple regression (#44099) @siriwatknp
68
+ - [Dialog] Add the aria-modal="true" by default (#44118) @mnajdova
69
+ - [IconButton] Fix disableRipple behavior when disableRipple is set in MuiButtonBase theme (#43714) @sai6855
70
+ - [pigment-css] Support project without enabling CSS variables (#44171) @siriwatknp
71
+ - Make the palette always return new light and dark object (#44059) @siriwatknp
72
+
73
+ ### `@mui/system@6.1.5`
74
+
75
+ - Add `defaultMode` to `InitColorSchemeScript` (#44139) @siriwatknp
76
+
77
+ ### `@mui/codemod@6.1.5`
78
+
79
+ - [Grid2] Add removal `zeroMinWidth` prop to codemod (#44178) @sai6855
80
+
81
+ ### Docs
82
+
83
+ - [material-ui][FormControlLabel] Don't use unintuitive label position on chec… (#44119) @mnajdova
84
+ - [material-ui][TextField] Dynamically modify the eye password button aria-label (#44122) @ChinoUkaegbu
85
+ - [icons] Run pnpm docs:mdicons:synonyms (#44098) @oliviertassinari
86
+ - [joy-ui] Update Overview copy to match Readme (#44136) @samuelsycamore
87
+ - Add CodeSandbox/Stackblitz to the rest of the templates (#43708) @siriwatknp
88
+ - Update Figma link to fix 301 (a7b7d9c) @oliviertassinari
89
+ - Link Toolpad from Core repo (#44111) @prakhargupta1
90
+ - Remove HighlightedCode max-width (#43731) @Janpot
91
+
92
+ ### Core
93
+
94
+ - [code-infra] Widen eslint file patterns (#44148) @Janpot
95
+ - [code-infra] Fix icon builder tests (#44143) @Janpot
96
+ - [code-infra] Fix dependabot vulnerabilities (#44124) @Janpot
97
+ - [core] Reference commits in changelog when no PR (#44115) @oliviertassinari
98
+ - [examples] Fix Pigment CSS Vite example (#44074) @oliviertassinari
99
+ - Fix fast-xml-parser vulnerability (#44121) @Janpot
100
+
101
+ All contributors of this release in alphabetical order: @ChinoUkaegbu, @Janpot, @mnajdova, @oliviertassinari, @prakhargupta1, @sai6855, @samuelsycamore, @siriwatknp, @snapwich
102
+
3
103
  ## v6.1.4
4
104
 
5
105
  <!-- generated comparing v6.1.3..master -->
@@ -310,7 +410,7 @@ A big thanks to the 11 contributors who made this release possible.
310
410
 
311
411
  ### `@mui/material@6.0.2`
312
412
 
313
- - Fix `createTheme` with just color schemes (#43518) @siriwatknp
413
+ - Fix `createTheme()` with just color schemes (#43518) @siriwatknp
314
414
  - [Menu,Popover] Fix Backdrop props descriptions (#43503) @Michael-Hutchinson
315
415
  - [MenuList] Do not react to an event with modifier key pressed (#43505) @MateuszGroth
316
416
 
@@ -1052,7 +1152,7 @@ A big thanks to the 18 contributors who made this release possible.
1052
1152
 
1053
1153
  ### Docs
1054
1154
 
1055
- - Add `theme.applyStyles` and migrate docs (#42498) @siriwatknp
1155
+ - Add `theme.applyStyles()` and migrate docs (#42498) @siriwatknp
1056
1156
  - Fix dashboard template console error (#42594) @oliviertassinari
1057
1157
  - Migrate system props to `sx` prop (#42475) @siriwatknp
1058
1158
  - [material-ui]Fix duplicated sentence (#42521) @alexfauquette
@@ -0,0 +1,23 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * Returns the ref of a React node handling differences between React 19 and older versions.
5
+ * It will return null if the node is not a valid React element.
6
+ *
7
+ * @param element React.ReactNode
8
+ * @returns React.Ref<any> | null
9
+ *
10
+ * @deprecated Use getReactElementRef instead
11
+ */
12
+ export default function getReactNodeRef(element) {
13
+ if (!element || ! /*#__PURE__*/React.isValidElement(element)) {
14
+ return null;
15
+ }
16
+
17
+ // 'ref' is passed as prop in React 19, whereas 'ref' is directly attached to children in older versions
18
+ return element.props.propertyIsEnumerable('ref') ? element.props.ref :
19
+ // @ts-expect-error element.ref is not included in the ReactElement type
20
+ // We cannot check for it, but isValidElement is true at this point
21
+ // https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/70189
22
+ element.ref;
23
+ }
@@ -0,0 +1 @@
1
+ export { default } from "./getReactNodeRef.js";
package/esm/index.js CHANGED
@@ -27,6 +27,7 @@ export { default as unstable_useForkRef } from "./useForkRef/index.js";
27
27
  export { default as unstable_useLazyRef } from "./useLazyRef/index.js";
28
28
  export { default as unstable_useTimeout, Timeout as unstable_Timeout } from "./useTimeout/index.js";
29
29
  export { default as unstable_useOnMount } from "./useOnMount/index.js";
30
+ export { default as unstable_useIsFocusVisible } from "./useIsFocusVisible/index.js";
30
31
  export { default as unstable_isFocusVisible } from "./isFocusVisible/index.js";
31
32
  export { default as unstable_getScrollbarSize } from "./getScrollbarSize/index.js";
32
33
  export { default as usePreviousProps } from "./usePreviousProps/index.js";
@@ -44,5 +45,6 @@ export { default as clamp } from "./clamp/index.js";
44
45
  export { default as unstable_useSlotProps } from "./useSlotProps/index.js";
45
46
  export { default as unstable_resolveComponentProps } from "./resolveComponentProps/index.js";
46
47
  export { default as unstable_extractEventHandlers } from "./extractEventHandlers/index.js";
48
+ export { default as unstable_getReactNodeRef } from "./getReactNodeRef/index.js";
47
49
  export { default as unstable_getReactElementRef } from "./getReactElementRef/index.js";
48
50
  export * from "./types.js";
@@ -0,0 +1,2 @@
1
+ export { default } from "./useIsFocusVisible.js";
2
+ export * from "./useIsFocusVisible.js";
@@ -0,0 +1,163 @@
1
+ 'use client';
2
+
3
+ // based on https://github.com/WICG/focus-visible/blob/v4.1.5/src/focus-visible.js
4
+ import * as React from 'react';
5
+ import { Timeout } from "../useTimeout/useTimeout.js";
6
+ let hadKeyboardEvent = true;
7
+ let hadFocusVisibleRecently = false;
8
+ const hadFocusVisibleRecentlyTimeout = new Timeout();
9
+ const inputTypesWhitelist = {
10
+ text: true,
11
+ search: true,
12
+ url: true,
13
+ tel: true,
14
+ email: true,
15
+ password: true,
16
+ number: true,
17
+ date: true,
18
+ month: true,
19
+ week: true,
20
+ time: true,
21
+ datetime: true,
22
+ 'datetime-local': true
23
+ };
24
+
25
+ /**
26
+ * Computes whether the given element should automatically trigger the
27
+ * `focus-visible` class being added, i.e. whether it should always match
28
+ * `:focus-visible` when focused.
29
+ * @param {Element} node
30
+ * @returns {boolean}
31
+ */
32
+ function focusTriggersKeyboardModality(node) {
33
+ const {
34
+ type,
35
+ tagName
36
+ } = node;
37
+ if (tagName === 'INPUT' && inputTypesWhitelist[type] && !node.readOnly) {
38
+ return true;
39
+ }
40
+ if (tagName === 'TEXTAREA' && !node.readOnly) {
41
+ return true;
42
+ }
43
+ if (node.isContentEditable) {
44
+ return true;
45
+ }
46
+ return false;
47
+ }
48
+
49
+ /**
50
+ * Keep track of our keyboard modality state with `hadKeyboardEvent`.
51
+ * If the most recent user interaction was via the keyboard;
52
+ * and the key press did not include a meta, alt/option, or control key;
53
+ * then the modality is keyboard. Otherwise, the modality is not keyboard.
54
+ * @param {KeyboardEvent} event
55
+ */
56
+ function handleKeyDown(event) {
57
+ if (event.metaKey || event.altKey || event.ctrlKey) {
58
+ return;
59
+ }
60
+ hadKeyboardEvent = true;
61
+ }
62
+
63
+ /**
64
+ * If at any point a user clicks with a pointing device, ensure that we change
65
+ * the modality away from keyboard.
66
+ * This avoids the situation where a user presses a key on an already focused
67
+ * element, and then clicks on a different element, focusing it with a
68
+ * pointing device, while we still think we're in keyboard modality.
69
+ */
70
+ function handlePointerDown() {
71
+ hadKeyboardEvent = false;
72
+ }
73
+ function handleVisibilityChange() {
74
+ if (this.visibilityState === 'hidden') {
75
+ // If the tab becomes active again, the browser will handle calling focus
76
+ // on the element (Safari actually calls it twice).
77
+ // If this tab change caused a blur on an element with focus-visible,
78
+ // re-apply the class when the user switches back to the tab.
79
+ if (hadFocusVisibleRecently) {
80
+ hadKeyboardEvent = true;
81
+ }
82
+ }
83
+ }
84
+ function prepare(doc) {
85
+ doc.addEventListener('keydown', handleKeyDown, true);
86
+ doc.addEventListener('mousedown', handlePointerDown, true);
87
+ doc.addEventListener('pointerdown', handlePointerDown, true);
88
+ doc.addEventListener('touchstart', handlePointerDown, true);
89
+ doc.addEventListener('visibilitychange', handleVisibilityChange, true);
90
+ }
91
+ export function teardown(doc) {
92
+ doc.removeEventListener('keydown', handleKeyDown, true);
93
+ doc.removeEventListener('mousedown', handlePointerDown, true);
94
+ doc.removeEventListener('pointerdown', handlePointerDown, true);
95
+ doc.removeEventListener('touchstart', handlePointerDown, true);
96
+ doc.removeEventListener('visibilitychange', handleVisibilityChange, true);
97
+ }
98
+ function isFocusVisible(event) {
99
+ const {
100
+ target
101
+ } = event;
102
+ try {
103
+ return target.matches(':focus-visible');
104
+ } catch (error) {
105
+ // Browsers not implementing :focus-visible will throw a SyntaxError.
106
+ // We use our own heuristic for those browsers.
107
+ // Rethrow might be better if it's not the expected error but do we really
108
+ // want to crash if focus-visible malfunctioned?
109
+ }
110
+
111
+ // No need for validFocusTarget check. The user does that by attaching it to
112
+ // focusable events only.
113
+ return hadKeyboardEvent || focusTriggersKeyboardModality(target);
114
+ }
115
+ export default function useIsFocusVisible() {
116
+ const ref = React.useCallback(node => {
117
+ if (node != null) {
118
+ prepare(node.ownerDocument);
119
+ }
120
+ }, []);
121
+ const isFocusVisibleRef = React.useRef(false);
122
+
123
+ /**
124
+ * Should be called if a blur event is fired
125
+ */
126
+ function handleBlurVisible() {
127
+ // checking against potential state variable does not suffice if we focus and blur synchronously.
128
+ // React wouldn't have time to trigger a re-render so `focusVisible` would be stale.
129
+ // Ideally we would adjust `isFocusVisible(event)` to look at `relatedTarget` for blur events.
130
+ // This doesn't work in IE11 due to https://github.com/facebook/react/issues/3751
131
+ // TODO: check again if React releases their internal changes to focus event handling (https://github.com/facebook/react/pull/19186).
132
+ if (isFocusVisibleRef.current) {
133
+ // To detect a tab/window switch, we look for a blur event followed
134
+ // rapidly by a visibility change.
135
+ // If we don't see a visibility change within 100ms, it's probably a
136
+ // regular focus change.
137
+ hadFocusVisibleRecently = true;
138
+ hadFocusVisibleRecentlyTimeout.start(100, () => {
139
+ hadFocusVisibleRecently = false;
140
+ });
141
+ isFocusVisibleRef.current = false;
142
+ return true;
143
+ }
144
+ return false;
145
+ }
146
+
147
+ /**
148
+ * Should be called if a blur event is fired
149
+ */
150
+ function handleFocusVisible(event) {
151
+ if (isFocusVisible(event)) {
152
+ isFocusVisibleRef.current = true;
153
+ return true;
154
+ }
155
+ return false;
156
+ }
157
+ return {
158
+ isFocusVisibleRef,
159
+ onFocus: handleFocusVisible,
160
+ onBlur: handleBlurVisible,
161
+ ref
162
+ };
163
+ }
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Returns the ref of a React node handling differences between React 19 and older versions.
4
+ * It will return null if the node is not a valid React element.
5
+ *
6
+ * @param element React.ReactNode
7
+ * @returns React.Ref<any> | null
8
+ *
9
+ * @deprecated Use getReactElementRef instead
10
+ */
11
+ export default function getReactNodeRef(element: React.ReactNode): React.Ref<any> | null;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = getReactNodeRef;
8
+ var React = _interopRequireWildcard(require("react"));
9
+ /**
10
+ * Returns the ref of a React node handling differences between React 19 and older versions.
11
+ * It will return null if the node is not a valid React element.
12
+ *
13
+ * @param element React.ReactNode
14
+ * @returns React.Ref<any> | null
15
+ *
16
+ * @deprecated Use getReactElementRef instead
17
+ */
18
+ function getReactNodeRef(element) {
19
+ if (!element || ! /*#__PURE__*/React.isValidElement(element)) {
20
+ return null;
21
+ }
22
+
23
+ // 'ref' is passed as prop in React 19, whereas 'ref' is directly attached to children in older versions
24
+ return element.props.propertyIsEnumerable('ref') ? element.props.ref :
25
+ // @ts-expect-error element.ref is not included in the ReactElement type
26
+ // We cannot check for it, but isValidElement is true at this point
27
+ // https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/70189
28
+ element.ref;
29
+ }
@@ -0,0 +1 @@
1
+ export { default } from './getReactNodeRef';
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ Object.defineProperty(exports, "default", {
8
+ enumerable: true,
9
+ get: function () {
10
+ return _getReactNodeRef.default;
11
+ }
12
+ });
13
+ var _getReactNodeRef = _interopRequireDefault(require("./getReactNodeRef"));
@@ -0,0 +1,6 @@
1
+ {
2
+ "sideEffects": false,
3
+ "module": "../esm/getReactNodeRef/index.js",
4
+ "main": "./index.js",
5
+ "types": "./index.d.ts"
6
+ }
package/index.d.ts CHANGED
@@ -27,6 +27,7 @@ export { default as unstable_useForkRef } from './useForkRef';
27
27
  export { default as unstable_useLazyRef } from './useLazyRef';
28
28
  export { default as unstable_useTimeout, Timeout as unstable_Timeout } from './useTimeout';
29
29
  export { default as unstable_useOnMount } from './useOnMount';
30
+ export { default as unstable_useIsFocusVisible } from './useIsFocusVisible';
30
31
  export { default as unstable_isFocusVisible } from './isFocusVisible';
31
32
  export { default as unstable_getScrollbarSize } from './getScrollbarSize';
32
33
  export { default as usePreviousProps } from './usePreviousProps';
@@ -45,5 +46,6 @@ export { default as unstable_useSlotProps } from './useSlotProps';
45
46
  export type { UseSlotPropsParameters, UseSlotPropsResult } from './useSlotProps';
46
47
  export { default as unstable_resolveComponentProps } from './resolveComponentProps';
47
48
  export { default as unstable_extractEventHandlers } from './extractEventHandlers';
49
+ export { default as unstable_getReactNodeRef } from './getReactNodeRef';
48
50
  export { default as unstable_getReactElementRef } from './getReactElementRef';
49
51
  export * from './types';
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/utils v6.1.4
2
+ * @mui/utils v6.1.6
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -43,6 +43,7 @@ var _exportNames = {
43
43
  unstable_useTimeout: true,
44
44
  unstable_Timeout: true,
45
45
  unstable_useOnMount: true,
46
+ unstable_useIsFocusVisible: true,
46
47
  unstable_isFocusVisible: true,
47
48
  unstable_getScrollbarSize: true,
48
49
  usePreviousProps: true,
@@ -59,6 +60,7 @@ var _exportNames = {
59
60
  unstable_useSlotProps: true,
60
61
  unstable_resolveComponentProps: true,
61
62
  unstable_extractEventHandlers: true,
63
+ unstable_getReactNodeRef: true,
62
64
  unstable_getReactElementRef: true
63
65
  };
64
66
  Object.defineProperty(exports, "HTMLElementType", {
@@ -217,6 +219,12 @@ Object.defineProperty(exports, "unstable_getReactElementRef", {
217
219
  return _getReactElementRef.default;
218
220
  }
219
221
  });
222
+ Object.defineProperty(exports, "unstable_getReactNodeRef", {
223
+ enumerable: true,
224
+ get: function () {
225
+ return _getReactNodeRef.default;
226
+ }
227
+ });
220
228
  Object.defineProperty(exports, "unstable_getScrollbarSize", {
221
229
  enumerable: true,
222
230
  get: function () {
@@ -307,6 +315,12 @@ Object.defineProperty(exports, "unstable_useId", {
307
315
  return _useId.default;
308
316
  }
309
317
  });
318
+ Object.defineProperty(exports, "unstable_useIsFocusVisible", {
319
+ enumerable: true,
320
+ get: function () {
321
+ return _useIsFocusVisible.default;
322
+ }
323
+ });
310
324
  Object.defineProperty(exports, "unstable_useLazyRef", {
311
325
  enumerable: true,
312
326
  get: function () {
@@ -371,6 +385,7 @@ var _useForkRef = _interopRequireDefault(require("./useForkRef"));
371
385
  var _useLazyRef = _interopRequireDefault(require("./useLazyRef"));
372
386
  var _useTimeout = _interopRequireWildcard(require("./useTimeout"));
373
387
  var _useOnMount = _interopRequireDefault(require("./useOnMount"));
388
+ var _useIsFocusVisible = _interopRequireDefault(require("./useIsFocusVisible"));
374
389
  var _isFocusVisible = _interopRequireDefault(require("./isFocusVisible"));
375
390
  var _getScrollbarSize = _interopRequireDefault(require("./getScrollbarSize"));
376
391
  var _usePreviousProps = _interopRequireDefault(require("./usePreviousProps"));
@@ -397,6 +412,7 @@ var _clamp = _interopRequireDefault(require("./clamp"));
397
412
  var _useSlotProps = _interopRequireDefault(require("./useSlotProps"));
398
413
  var _resolveComponentProps = _interopRequireDefault(require("./resolveComponentProps"));
399
414
  var _extractEventHandlers = _interopRequireDefault(require("./extractEventHandlers"));
415
+ var _getReactNodeRef = _interopRequireDefault(require("./getReactNodeRef"));
400
416
  var _getReactElementRef = _interopRequireDefault(require("./getReactElementRef"));
401
417
  var _types = require("./types");
402
418
  Object.keys(_types).forEach(function (key) {
@@ -0,0 +1,23 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * Returns the ref of a React node handling differences between React 19 and older versions.
5
+ * It will return null if the node is not a valid React element.
6
+ *
7
+ * @param element React.ReactNode
8
+ * @returns React.Ref<any> | null
9
+ *
10
+ * @deprecated Use getReactElementRef instead
11
+ */
12
+ export default function getReactNodeRef(element) {
13
+ if (!element || ! /*#__PURE__*/React.isValidElement(element)) {
14
+ return null;
15
+ }
16
+
17
+ // 'ref' is passed as prop in React 19, whereas 'ref' is directly attached to children in older versions
18
+ return element.props.propertyIsEnumerable('ref') ? element.props.ref :
19
+ // @ts-expect-error element.ref is not included in the ReactElement type
20
+ // We cannot check for it, but isValidElement is true at this point
21
+ // https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/70189
22
+ element.ref;
23
+ }
@@ -0,0 +1 @@
1
+ export { default } from "./getReactNodeRef.js";
package/modern/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/utils v6.1.4
2
+ * @mui/utils v6.1.6
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -34,6 +34,7 @@ export { default as unstable_useForkRef } from "./useForkRef/index.js";
34
34
  export { default as unstable_useLazyRef } from "./useLazyRef/index.js";
35
35
  export { default as unstable_useTimeout, Timeout as unstable_Timeout } from "./useTimeout/index.js";
36
36
  export { default as unstable_useOnMount } from "./useOnMount/index.js";
37
+ export { default as unstable_useIsFocusVisible } from "./useIsFocusVisible/index.js";
37
38
  export { default as unstable_isFocusVisible } from "./isFocusVisible/index.js";
38
39
  export { default as unstable_getScrollbarSize } from "./getScrollbarSize/index.js";
39
40
  export { default as usePreviousProps } from "./usePreviousProps/index.js";
@@ -51,5 +52,6 @@ export { default as clamp } from "./clamp/index.js";
51
52
  export { default as unstable_useSlotProps } from "./useSlotProps/index.js";
52
53
  export { default as unstable_resolveComponentProps } from "./resolveComponentProps/index.js";
53
54
  export { default as unstable_extractEventHandlers } from "./extractEventHandlers/index.js";
55
+ export { default as unstable_getReactNodeRef } from "./getReactNodeRef/index.js";
54
56
  export { default as unstable_getReactElementRef } from "./getReactElementRef/index.js";
55
57
  export * from "./types.js";
@@ -0,0 +1,2 @@
1
+ export { default } from "./useIsFocusVisible.js";
2
+ export * from "./useIsFocusVisible.js";
@@ -0,0 +1,163 @@
1
+ 'use client';
2
+
3
+ // based on https://github.com/WICG/focus-visible/blob/v4.1.5/src/focus-visible.js
4
+ import * as React from 'react';
5
+ import { Timeout } from "../useTimeout/useTimeout.js";
6
+ let hadKeyboardEvent = true;
7
+ let hadFocusVisibleRecently = false;
8
+ const hadFocusVisibleRecentlyTimeout = new Timeout();
9
+ const inputTypesWhitelist = {
10
+ text: true,
11
+ search: true,
12
+ url: true,
13
+ tel: true,
14
+ email: true,
15
+ password: true,
16
+ number: true,
17
+ date: true,
18
+ month: true,
19
+ week: true,
20
+ time: true,
21
+ datetime: true,
22
+ 'datetime-local': true
23
+ };
24
+
25
+ /**
26
+ * Computes whether the given element should automatically trigger the
27
+ * `focus-visible` class being added, i.e. whether it should always match
28
+ * `:focus-visible` when focused.
29
+ * @param {Element} node
30
+ * @returns {boolean}
31
+ */
32
+ function focusTriggersKeyboardModality(node) {
33
+ const {
34
+ type,
35
+ tagName
36
+ } = node;
37
+ if (tagName === 'INPUT' && inputTypesWhitelist[type] && !node.readOnly) {
38
+ return true;
39
+ }
40
+ if (tagName === 'TEXTAREA' && !node.readOnly) {
41
+ return true;
42
+ }
43
+ if (node.isContentEditable) {
44
+ return true;
45
+ }
46
+ return false;
47
+ }
48
+
49
+ /**
50
+ * Keep track of our keyboard modality state with `hadKeyboardEvent`.
51
+ * If the most recent user interaction was via the keyboard;
52
+ * and the key press did not include a meta, alt/option, or control key;
53
+ * then the modality is keyboard. Otherwise, the modality is not keyboard.
54
+ * @param {KeyboardEvent} event
55
+ */
56
+ function handleKeyDown(event) {
57
+ if (event.metaKey || event.altKey || event.ctrlKey) {
58
+ return;
59
+ }
60
+ hadKeyboardEvent = true;
61
+ }
62
+
63
+ /**
64
+ * If at any point a user clicks with a pointing device, ensure that we change
65
+ * the modality away from keyboard.
66
+ * This avoids the situation where a user presses a key on an already focused
67
+ * element, and then clicks on a different element, focusing it with a
68
+ * pointing device, while we still think we're in keyboard modality.
69
+ */
70
+ function handlePointerDown() {
71
+ hadKeyboardEvent = false;
72
+ }
73
+ function handleVisibilityChange() {
74
+ if (this.visibilityState === 'hidden') {
75
+ // If the tab becomes active again, the browser will handle calling focus
76
+ // on the element (Safari actually calls it twice).
77
+ // If this tab change caused a blur on an element with focus-visible,
78
+ // re-apply the class when the user switches back to the tab.
79
+ if (hadFocusVisibleRecently) {
80
+ hadKeyboardEvent = true;
81
+ }
82
+ }
83
+ }
84
+ function prepare(doc) {
85
+ doc.addEventListener('keydown', handleKeyDown, true);
86
+ doc.addEventListener('mousedown', handlePointerDown, true);
87
+ doc.addEventListener('pointerdown', handlePointerDown, true);
88
+ doc.addEventListener('touchstart', handlePointerDown, true);
89
+ doc.addEventListener('visibilitychange', handleVisibilityChange, true);
90
+ }
91
+ export function teardown(doc) {
92
+ doc.removeEventListener('keydown', handleKeyDown, true);
93
+ doc.removeEventListener('mousedown', handlePointerDown, true);
94
+ doc.removeEventListener('pointerdown', handlePointerDown, true);
95
+ doc.removeEventListener('touchstart', handlePointerDown, true);
96
+ doc.removeEventListener('visibilitychange', handleVisibilityChange, true);
97
+ }
98
+ function isFocusVisible(event) {
99
+ const {
100
+ target
101
+ } = event;
102
+ try {
103
+ return target.matches(':focus-visible');
104
+ } catch (error) {
105
+ // Browsers not implementing :focus-visible will throw a SyntaxError.
106
+ // We use our own heuristic for those browsers.
107
+ // Rethrow might be better if it's not the expected error but do we really
108
+ // want to crash if focus-visible malfunctioned?
109
+ }
110
+
111
+ // No need for validFocusTarget check. The user does that by attaching it to
112
+ // focusable events only.
113
+ return hadKeyboardEvent || focusTriggersKeyboardModality(target);
114
+ }
115
+ export default function useIsFocusVisible() {
116
+ const ref = React.useCallback(node => {
117
+ if (node != null) {
118
+ prepare(node.ownerDocument);
119
+ }
120
+ }, []);
121
+ const isFocusVisibleRef = React.useRef(false);
122
+
123
+ /**
124
+ * Should be called if a blur event is fired
125
+ */
126
+ function handleBlurVisible() {
127
+ // checking against potential state variable does not suffice if we focus and blur synchronously.
128
+ // React wouldn't have time to trigger a re-render so `focusVisible` would be stale.
129
+ // Ideally we would adjust `isFocusVisible(event)` to look at `relatedTarget` for blur events.
130
+ // This doesn't work in IE11 due to https://github.com/facebook/react/issues/3751
131
+ // TODO: check again if React releases their internal changes to focus event handling (https://github.com/facebook/react/pull/19186).
132
+ if (isFocusVisibleRef.current) {
133
+ // To detect a tab/window switch, we look for a blur event followed
134
+ // rapidly by a visibility change.
135
+ // If we don't see a visibility change within 100ms, it's probably a
136
+ // regular focus change.
137
+ hadFocusVisibleRecently = true;
138
+ hadFocusVisibleRecentlyTimeout.start(100, () => {
139
+ hadFocusVisibleRecently = false;
140
+ });
141
+ isFocusVisibleRef.current = false;
142
+ return true;
143
+ }
144
+ return false;
145
+ }
146
+
147
+ /**
148
+ * Should be called if a blur event is fired
149
+ */
150
+ function handleFocusVisible(event) {
151
+ if (isFocusVisible(event)) {
152
+ isFocusVisibleRef.current = true;
153
+ return true;
154
+ }
155
+ return false;
156
+ }
157
+ return {
158
+ isFocusVisibleRef,
159
+ onFocus: handleFocusVisible,
160
+ onBlur: handleBlurVisible,
161
+ ref
162
+ };
163
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/utils",
3
- "version": "6.1.4",
3
+ "version": "6.1.6",
4
4
  "private": false,
5
5
  "author": "MUI Team",
6
6
  "description": "Utility functions for React components.",
@@ -26,12 +26,12 @@
26
26
  "url": "https://opencollective.com/mui-org"
27
27
  },
28
28
  "dependencies": {
29
- "@babel/runtime": "^7.25.7",
29
+ "@babel/runtime": "^7.26.0",
30
30
  "@types/prop-types": "^15.7.13",
31
31
  "clsx": "^2.1.1",
32
32
  "prop-types": "^15.8.1",
33
33
  "react-is": "^18.3.1",
34
- "@mui/types": "^7.2.18"
34
+ "@mui/types": "^7.2.19"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
@@ -0,0 +1,2 @@
1
+ export { default } from './useIsFocusVisible';
2
+ export * from './useIsFocusVisible';
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ var _exportNames = {};
8
+ Object.defineProperty(exports, "default", {
9
+ enumerable: true,
10
+ get: function () {
11
+ return _useIsFocusVisible.default;
12
+ }
13
+ });
14
+ var _useIsFocusVisible = _interopRequireWildcard(require("./useIsFocusVisible"));
15
+ Object.keys(_useIsFocusVisible).forEach(function (key) {
16
+ if (key === "default" || key === "__esModule") return;
17
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
18
+ if (key in exports && exports[key] === _useIsFocusVisible[key]) return;
19
+ Object.defineProperty(exports, key, {
20
+ enumerable: true,
21
+ get: function () {
22
+ return _useIsFocusVisible[key];
23
+ }
24
+ });
25
+ });
@@ -0,0 +1,6 @@
1
+ {
2
+ "sideEffects": false,
3
+ "module": "../esm/useIsFocusVisible/index.js",
4
+ "main": "./index.js",
5
+ "types": "./index.d.ts"
6
+ }
@@ -0,0 +1,9 @@
1
+ import * as React from 'react';
2
+ export declare function teardown(doc: Document): void;
3
+ export interface UseIsFocusVisibleResult {
4
+ isFocusVisibleRef: React.MutableRefObject<boolean>;
5
+ onBlur: (event: React.FocusEvent<any>) => void;
6
+ onFocus: (event: React.FocusEvent<any>) => void;
7
+ ref: React.RefCallback<Element>;
8
+ }
9
+ export default function useIsFocusVisible(): UseIsFocusVisibleResult;
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ 'use client';
3
+
4
+ // based on https://github.com/WICG/focus-visible/blob/v4.1.5/src/focus-visible.js
5
+ var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
6
+ Object.defineProperty(exports, "__esModule", {
7
+ value: true
8
+ });
9
+ exports.default = useIsFocusVisible;
10
+ exports.teardown = teardown;
11
+ var React = _interopRequireWildcard(require("react"));
12
+ var _useTimeout = require("../useTimeout/useTimeout");
13
+ let hadKeyboardEvent = true;
14
+ let hadFocusVisibleRecently = false;
15
+ const hadFocusVisibleRecentlyTimeout = new _useTimeout.Timeout();
16
+ const inputTypesWhitelist = {
17
+ text: true,
18
+ search: true,
19
+ url: true,
20
+ tel: true,
21
+ email: true,
22
+ password: true,
23
+ number: true,
24
+ date: true,
25
+ month: true,
26
+ week: true,
27
+ time: true,
28
+ datetime: true,
29
+ 'datetime-local': true
30
+ };
31
+
32
+ /**
33
+ * Computes whether the given element should automatically trigger the
34
+ * `focus-visible` class being added, i.e. whether it should always match
35
+ * `:focus-visible` when focused.
36
+ * @param {Element} node
37
+ * @returns {boolean}
38
+ */
39
+ function focusTriggersKeyboardModality(node) {
40
+ const {
41
+ type,
42
+ tagName
43
+ } = node;
44
+ if (tagName === 'INPUT' && inputTypesWhitelist[type] && !node.readOnly) {
45
+ return true;
46
+ }
47
+ if (tagName === 'TEXTAREA' && !node.readOnly) {
48
+ return true;
49
+ }
50
+ if (node.isContentEditable) {
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+
56
+ /**
57
+ * Keep track of our keyboard modality state with `hadKeyboardEvent`.
58
+ * If the most recent user interaction was via the keyboard;
59
+ * and the key press did not include a meta, alt/option, or control key;
60
+ * then the modality is keyboard. Otherwise, the modality is not keyboard.
61
+ * @param {KeyboardEvent} event
62
+ */
63
+ function handleKeyDown(event) {
64
+ if (event.metaKey || event.altKey || event.ctrlKey) {
65
+ return;
66
+ }
67
+ hadKeyboardEvent = true;
68
+ }
69
+
70
+ /**
71
+ * If at any point a user clicks with a pointing device, ensure that we change
72
+ * the modality away from keyboard.
73
+ * This avoids the situation where a user presses a key on an already focused
74
+ * element, and then clicks on a different element, focusing it with a
75
+ * pointing device, while we still think we're in keyboard modality.
76
+ */
77
+ function handlePointerDown() {
78
+ hadKeyboardEvent = false;
79
+ }
80
+ function handleVisibilityChange() {
81
+ if (this.visibilityState === 'hidden') {
82
+ // If the tab becomes active again, the browser will handle calling focus
83
+ // on the element (Safari actually calls it twice).
84
+ // If this tab change caused a blur on an element with focus-visible,
85
+ // re-apply the class when the user switches back to the tab.
86
+ if (hadFocusVisibleRecently) {
87
+ hadKeyboardEvent = true;
88
+ }
89
+ }
90
+ }
91
+ function prepare(doc) {
92
+ doc.addEventListener('keydown', handleKeyDown, true);
93
+ doc.addEventListener('mousedown', handlePointerDown, true);
94
+ doc.addEventListener('pointerdown', handlePointerDown, true);
95
+ doc.addEventListener('touchstart', handlePointerDown, true);
96
+ doc.addEventListener('visibilitychange', handleVisibilityChange, true);
97
+ }
98
+ function teardown(doc) {
99
+ doc.removeEventListener('keydown', handleKeyDown, true);
100
+ doc.removeEventListener('mousedown', handlePointerDown, true);
101
+ doc.removeEventListener('pointerdown', handlePointerDown, true);
102
+ doc.removeEventListener('touchstart', handlePointerDown, true);
103
+ doc.removeEventListener('visibilitychange', handleVisibilityChange, true);
104
+ }
105
+ function isFocusVisible(event) {
106
+ const {
107
+ target
108
+ } = event;
109
+ try {
110
+ return target.matches(':focus-visible');
111
+ } catch (error) {
112
+ // Browsers not implementing :focus-visible will throw a SyntaxError.
113
+ // We use our own heuristic for those browsers.
114
+ // Rethrow might be better if it's not the expected error but do we really
115
+ // want to crash if focus-visible malfunctioned?
116
+ }
117
+
118
+ // No need for validFocusTarget check. The user does that by attaching it to
119
+ // focusable events only.
120
+ return hadKeyboardEvent || focusTriggersKeyboardModality(target);
121
+ }
122
+ function useIsFocusVisible() {
123
+ const ref = React.useCallback(node => {
124
+ if (node != null) {
125
+ prepare(node.ownerDocument);
126
+ }
127
+ }, []);
128
+ const isFocusVisibleRef = React.useRef(false);
129
+
130
+ /**
131
+ * Should be called if a blur event is fired
132
+ */
133
+ function handleBlurVisible() {
134
+ // checking against potential state variable does not suffice if we focus and blur synchronously.
135
+ // React wouldn't have time to trigger a re-render so `focusVisible` would be stale.
136
+ // Ideally we would adjust `isFocusVisible(event)` to look at `relatedTarget` for blur events.
137
+ // This doesn't work in IE11 due to https://github.com/facebook/react/issues/3751
138
+ // TODO: check again if React releases their internal changes to focus event handling (https://github.com/facebook/react/pull/19186).
139
+ if (isFocusVisibleRef.current) {
140
+ // To detect a tab/window switch, we look for a blur event followed
141
+ // rapidly by a visibility change.
142
+ // If we don't see a visibility change within 100ms, it's probably a
143
+ // regular focus change.
144
+ hadFocusVisibleRecently = true;
145
+ hadFocusVisibleRecentlyTimeout.start(100, () => {
146
+ hadFocusVisibleRecently = false;
147
+ });
148
+ isFocusVisibleRef.current = false;
149
+ return true;
150
+ }
151
+ return false;
152
+ }
153
+
154
+ /**
155
+ * Should be called if a blur event is fired
156
+ */
157
+ function handleFocusVisible(event) {
158
+ if (isFocusVisible(event)) {
159
+ isFocusVisibleRef.current = true;
160
+ return true;
161
+ }
162
+ return false;
163
+ }
164
+ return {
165
+ isFocusVisibleRef,
166
+ onFocus: handleFocusVisible,
167
+ onBlur: handleBlurVisible,
168
+ ref
169
+ };
170
+ }