@mui/utils 6.1.5 → 6.1.7

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,6 +1,112 @@
1
1
  # [Versions](https://mui.com/versions/)
2
2
 
3
- ## 6.1.5
3
+ ## v6.1.7
4
+
5
+ <!-- generated comparing v6.1.6..master -->
6
+
7
+ _Nov 13, 2024_
8
+
9
+ A big thanks to the 13 contributors who made this release possible.
10
+ This release includes fixes as well as documentation improvements.
11
+
12
+ ### `@mui/material@6.1.7`
13
+
14
+ - Fix default props theme scoping (#44340) @siriwatknp
15
+ - Support theme scoping in `useMediaQuery` (#44339) @siriwatknp
16
+ - [Grid] Fix regression spacing prop with string value (#44376) @siriwatknp
17
+
18
+ ### `@mui/styled-engine-sc@6.1.7`
19
+
20
+ - Fix missing `@types/hoist-non-react-statics` causing `styled` returns any (#44397) @megos
21
+
22
+ ### Docs
23
+
24
+ - Replace 'Experimental APIs - Toolpad' with 'Toolpad (Beta)' (#44388) @prakhargupta1
25
+ - Fix Pigment CSS install (#44353) @oliviertassinari
26
+ - Fix dashboard menu warning (#44317) @siriwatknp
27
+ - Add runtime theme section for Material Pigment CSS (#44137) @siriwatknp
28
+ - Add hash to `key` to remove noise from console (#44289) @sai6855
29
+ - Revise Example Projects and Related Projects pages (#44191) @samuelsycamore
30
+ - [material-ui] Fix typo in typography theme set up for templates (#44338) @navedqb
31
+ - [material-ui] Add StackBlitz/CodeSandbox buttons to template cards (#44253) @zanivan
32
+ - [material-ui] Fix Sign-in/Sign-up templates layout (#44281) @zanivan
33
+ - [material-ui] Remove noise in template (#44260) @oliviertassinari
34
+ - [material-ui][Rating] Add uncontrolled example to Basic Rating demo (#44386) @sai6855
35
+ - [material-ui][TextField] Replace InputProps with slotProps.input in demo (#44288) @sai6855
36
+
37
+ ### Core
38
+
39
+ - [blog] Follow media asset guidelines (#44374) @oliviertassinari
40
+ - [code-infra] Changes for test util to work in `vitest` (#43625) @JCQuintas
41
+ - Remove old marked JS options (#44375) @ZeeshanTamboli
42
+ - Fix webpack capitalization (#44352) @oliviertassinari
43
+ - Fix Next.js link 404 (710cd95) @oliviertassinari
44
+ - Update Gold sponsoring backlinks (#44316) @oliviertassinari
45
+ - Fix tools-public.mui.com redirection (9196fa5) @oliviertassinari
46
+ - Remove blank AlertTitle test file (#44282) @ZeeshanTamboli
47
+ - [docs-infra] Fix ad in RTL (#44345) @oliviertassinari
48
+ - [docs-infra] Enforce punctuation on descriptions (#44292) @oliviertassinari
49
+ - [docs-infra] Add CodeSandbox and StackBlitz to vale vocab (6db477a) @oliviertassinari
50
+ - [docs-infra] Fix correct spelling of VS Code (#44277) @oliviertassinari
51
+ - [docs-infra] Add a `rawDescriptions` option (#44390) @vladmoroz
52
+ - [examples] Add missing `clsx` dependency (#43526) @Janpot
53
+ - [infra] Fix @renovate[bot] appearing in changelog (#44275) @mnajdova
54
+
55
+ All contributors of this release in alphabetical order: @Janpot, @JCQuintas, @megos, @mnajdova, @navedqb, @oliviertassinari, @prakhargupta1, @sai6855, @samuelsycamore, @siriwatknp, @vladmoroz, @zanivan, @ZeeshanTamboli
56
+
57
+ ## v6.1.6
58
+
59
+ <!-- generated comparing v6.1.5..master -->
60
+
61
+ _Oct 30, 2024_
62
+
63
+ A big thanks to the 13 contributors who made this release possible.
64
+
65
+ ### `@mui/material@6.1.6`
66
+
67
+ - [Autocomplete] Add missing `onMouseDown` type to AutocompleteRenderInputParams (#44183) @sai6855
68
+ - [Avatar] Fix AvatarGroup spacing (#44208) @aarongarciah
69
+ - [AvatarGroup] Fix spacing CSS variable (#44202) @navedqb
70
+ - [Divider] Fix CSS specificity order (#44204) @o-alexandrov
71
+ - [Slider] Fix value prop type warning (#44131) @joshkel
72
+ - Replace `useThemeProps` with `useDefaultProps` (#44193) @siriwatknp
73
+
74
+ ### `@mui/material-nextjs@6.1.6`
75
+
76
+ - Support Next 15.0.0 (#42428) @nphmuller
77
+
78
+ ### `@mui/lab@6.0.0-beta.14`
79
+
80
+ - [Tabs] Fix type of TabPanel component (#44207) @blackcow1987
81
+
82
+ ### `@mui/codemod@6.1.6`
83
+
84
+ - Fix system props default import specifier (#44170) @siriwatknp
85
+
86
+ ### `@mui/utils@6.1.6`
87
+
88
+ - Bring back useIsFocusVisible (#44256) @aarongarciah
89
+ - Bring back getReactNodeRef (#44248) @aarongarciah
90
+
91
+ ### Docs
92
+
93
+ - [material-ui][Avatar] Add AvatarGroup spacing demo (#44209) @aarongarciah
94
+ - Fix a typo in CONTRIBUTING.md (#44200) @prakhargupta1
95
+ - Mark the Hidden component as deprecated in the sidenav (#44068) @jimmycallin
96
+ - Use () when referencing functions (#44184) @oliviertassinari
97
+ - Follow types description convention (#44187) @oliviertassinari
98
+
99
+ ### Core
100
+
101
+ - Lock file maintenance (#43947)
102
+ - Run @mui/icon-material src:icons (#44097) @oliviertassinari
103
+ - [test][material-ui] Add tests for Pigment Grid and Stack (#44132) @DiegoAndai
104
+ - [test] Distinguish private with public tests API (#44188) @oliviertassinari
105
+ - [docs-infra] Add recursively the relative modules in the demos (#44150) @mnajdova
106
+
107
+ All contributors of this release in alphabetical order: @aarongarciah, @blackcow1987, @DiegoAndai, @jimmycallin, @joshkel, @mnajdova, @navedqb, @nphmuller, @o-alexandrov, @oliviertassinari, @prakhargupta1, @sai6855, @siriwatknp
108
+
109
+ ## v6.1.5
4
110
 
5
111
  <!-- generated comparing v6.1.4..master -->
6
112
 
@@ -32,7 +138,7 @@ A big thanks to the 9 contributors who made this release possible.
32
138
  - [material-ui][TextField] Dynamically modify the eye password button aria-label (#44122) @ChinoUkaegbu
33
139
  - [icons] Run pnpm docs:mdicons:synonyms (#44098) @oliviertassinari
34
140
  - [joy-ui] Update Overview copy to match Readme (#44136) @samuelsycamore
35
- - Add CodeSandbox/Stackblitz to the rest of the templates (#43708) @siriwatknp
141
+ - Add CodeSandbox/StackBlitz to the rest of the templates (#43708) @siriwatknp
36
142
  - Update Figma link to fix 301 (a7b7d9c) @oliviertassinari
37
143
  - Link Toolpad from Core repo (#44111) @prakhargupta1
38
144
  - Remove HighlightedCode max-width (#43731) @Janpot
@@ -202,7 +308,7 @@ A big thanks to the 13 contributors who made this release possible.
202
308
  - Uniformity in version range @oliviertassinari
203
309
  - Replace `toBeAriaHidden` matcher with `toBeInaccessible` in tests (#43870) @ZeeshanTamboli
204
310
  - [docs-infra] Strengthen CSP (#43711) @oliviertassinari
205
- - [docs-infra] Open Codesandbox demo with fontsize=12 (#43860) @siriwatknp
311
+ - [docs-infra] Open CodeSandbox demo with fontsize=12 (#43860) @siriwatknp
206
312
  - [icons] Reduce Material Icon page size (#43911) @oliviertassinari
207
313
  - [test] Point Istanbul to correct URL (#43935) @sai6855
208
314
  - [test] Sync React.version parse logic with codebase (#43820) @oliviertassinari
@@ -358,7 +464,7 @@ A big thanks to the 11 contributors who made this release possible.
358
464
 
359
465
  ### `@mui/material@6.0.2`
360
466
 
361
- - Fix `createTheme` with just color schemes (#43518) @siriwatknp
467
+ - Fix `createTheme()` with just color schemes (#43518) @siriwatknp
362
468
  - [Menu,Popover] Fix Backdrop props descriptions (#43503) @Michael-Hutchinson
363
469
  - [MenuList] Do not react to an event with modifier key pressed (#43505) @MateuszGroth
364
470
 
@@ -1100,7 +1206,7 @@ A big thanks to the 18 contributors who made this release possible.
1100
1206
 
1101
1207
  ### Docs
1102
1208
 
1103
- - Add `theme.applyStyles` and migrate docs (#42498) @siriwatknp
1209
+ - Add `theme.applyStyles()` and migrate docs (#42498) @siriwatknp
1104
1210
  - Fix dashboard template console error (#42594) @oliviertassinari
1105
1211
  - Migrate system props to `sx` prop (#42475) @siriwatknp
1106
1212
  - [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.5
2
+ * @mui/utils v6.1.7
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.5
2
+ * @mui/utils v6.1.7
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.5",
3
+ "version": "6.1.7",
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
+ }