@mui/material 9.1.1 → 9.1.2

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/Avatar/Avatar.js CHANGED
@@ -118,6 +118,8 @@ function useLoaded(src, srcSet, crossOrigin, referrerPolicy) {
118
118
  if (!src && !srcSet) {
119
119
  return undefined;
120
120
  }
121
+
122
+ // eslint-disable-next-line react-hooks/set-state-in-effect
121
123
  setLoaded(false);
122
124
  let active = true;
123
125
  const image = new Image();
package/Avatar/Avatar.mjs CHANGED
@@ -111,6 +111,8 @@ function useLoaded(src, srcSet, crossOrigin, referrerPolicy) {
111
111
  if (!src && !srcSet) {
112
112
  return undefined;
113
113
  }
114
+
115
+ // eslint-disable-next-line react-hooks/set-state-in-effect
114
116
  setLoaded(false);
115
117
  let active = true;
116
118
  const image = new Image();
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # [Versions](https://mui.com/material-ui/getting-started/versions/)
2
2
 
3
+ ## 9.1.2
4
+
5
+ <!-- generated comparing v9.1.1..master -->
6
+
7
+ _Jun 23, 2026_
8
+
9
+ A big thanks to the 5 contributors who made this release possible.
10
+
11
+ ### `@mui/material@9.1.2`
12
+
13
+ - [autocomplete] Don't submit forms when committing `freeSolo` value with Enter key (#48679) @mj12albert
14
+ - [transitions] Fix RTG import in ESM (#48645) @mj12albert
15
+
16
+ ### `@mui/system@9.1.2`
17
+
18
+ - [InitColorSchemeScript] Fix script tag warning in Next.js 16 dev mode (#48671) @siriwatknp
19
+
20
+ ### Docs
21
+
22
+ - Fix typos in release instructions (#48687) @brijeshb42
23
+ - Update @mui/x-* packages to latest (#48661) @Janpot
24
+
25
+ ### Core
26
+
27
+ - [code-infra] Convert leaf @mui/system .js+.d.ts pairs to TypeScript (part 1) (#48578) @Janpot
28
+ - [code-infra] Bump to latest code-infra packages (#48672) @brijeshb42
29
+ - [code-infra] Resolve remaining minimatch advisory (#48662) @Janpot
30
+ - [code-infra] Bump nx to resolve minimatch advisory (#48658) @Janpot
31
+ - [core] Drop @babel/* pnpm overrides (#48710) @Janpot
32
+ - [docs-infra] Revert "Pin StackBlitz demo vite to v7 and plugin-react to v5" (#48709) @Janpot
33
+ - [docs-infra] Fix code-block copy button broken on direct page load (#48653) @brijeshb42
34
+ - [test] Stabilize Data Grid demo data in Argos screenshots (#48654) @LukasTy
35
+
36
+
37
+ All contributors of this release in alphabetical order: @brijeshb42, @Janpot, @LukasTy, @mj12albert, @siriwatknp
38
+
3
39
  ## 9.1.1
4
40
 
5
41
  <!-- generated comparing v9.1.0..master -->
@@ -330,6 +330,7 @@ const InputBase = /*#__PURE__*/React.forwardRef(function InputBase(inProps, ref)
330
330
  // We need to book keep the focused state manually.
331
331
  React.useEffect(() => {
332
332
  if (!muiFormControl && disabled && focused) {
333
+ // eslint-disable-next-line react-hooks/set-state-in-effect
333
334
  setFocused(false);
334
335
  if (onBlur) {
335
336
  onBlur();
@@ -321,6 +321,7 @@ const InputBase = /*#__PURE__*/React.forwardRef(function InputBase(inProps, ref)
321
321
  // We need to book keep the focused state manually.
322
322
  React.useEffect(() => {
323
323
  if (!muiFormControl && disabled && focused) {
324
+ // eslint-disable-next-line react-hooks/set-state-in-effect
324
325
  setFocused(false);
325
326
  if (onBlur) {
326
327
  onBlur();
package/NoSsr/NoSsr.js CHANGED
@@ -43,6 +43,7 @@ function NoSsr(props) {
43
43
  }, [defer]);
44
44
  React.useEffect(() => {
45
45
  if (defer) {
46
+ // eslint-disable-next-line react-hooks/set-state-in-effect
46
47
  setMountedState(true);
47
48
  }
48
49
  }, [defer]);
package/NoSsr/NoSsr.mjs CHANGED
@@ -36,6 +36,7 @@ function NoSsr(props) {
36
36
  }, [defer]);
37
37
  React.useEffect(() => {
38
38
  if (defer) {
39
+ // eslint-disable-next-line react-hooks/set-state-in-effect
39
40
  setMountedState(true);
40
41
  }
41
42
  }, [defer]);
@@ -92,17 +92,12 @@ const PopperTooltip = /*#__PURE__*/React.forwardRef(function PopperTooltip(props
92
92
  * modifiers.flip is essentially a flip for controlled/uncontrolled behavior
93
93
  */
94
94
  const [placement, setPlacement] = React.useState(rtlPlacement);
95
- const [resolvedAnchorElement, setResolvedAnchorElement] = React.useState(resolveAnchorEl(anchorEl));
95
+ const resolvedAnchorElement = React.useMemo(() => resolveAnchorEl(anchorEl), [anchorEl]);
96
96
  React.useEffect(() => {
97
97
  if (popperRef.current) {
98
98
  popperRef.current.forceUpdate();
99
99
  }
100
100
  });
101
- React.useEffect(() => {
102
- if (anchorEl) {
103
- setResolvedAnchorElement(resolveAnchorEl(anchorEl));
104
- }
105
- }, [anchorEl]);
106
101
  (0, _useEnhancedEffect.default)(() => {
107
102
  if (!resolvedAnchorElement || !open) {
108
103
  return undefined;
@@ -85,17 +85,12 @@ const PopperTooltip = /*#__PURE__*/React.forwardRef(function PopperTooltip(props
85
85
  * modifiers.flip is essentially a flip for controlled/uncontrolled behavior
86
86
  */
87
87
  const [placement, setPlacement] = React.useState(rtlPlacement);
88
- const [resolvedAnchorElement, setResolvedAnchorElement] = React.useState(resolveAnchorEl(anchorEl));
88
+ const resolvedAnchorElement = React.useMemo(() => resolveAnchorEl(anchorEl), [anchorEl]);
89
89
  React.useEffect(() => {
90
90
  if (popperRef.current) {
91
91
  popperRef.current.forceUpdate();
92
92
  }
93
93
  });
94
- React.useEffect(() => {
95
- if (anchorEl) {
96
- setResolvedAnchorElement(resolveAnchorEl(anchorEl));
97
- }
98
- }, [anchorEl]);
99
94
  useEnhancedEffect(() => {
100
95
  if (!resolvedAnchorElement || !open) {
101
96
  return undefined;
package/Tabs/Tabs.js CHANGED
@@ -615,6 +615,7 @@ const Tabs = /*#__PURE__*/React.forwardRef(function Tabs(inProps, ref) {
615
615
  return undefined;
616
616
  }, [scrollable, scrollButtons, updateScrollObserver, childrenProp?.length]);
617
617
  React.useEffect(() => {
618
+ // eslint-disable-next-line react-hooks/set-state-in-effect
618
619
  setMounted(true);
619
620
  }, []);
620
621
  React.useEffect(() => {
package/Tabs/Tabs.mjs CHANGED
@@ -608,6 +608,7 @@ const Tabs = /*#__PURE__*/React.forwardRef(function Tabs(inProps, ref) {
608
608
  return undefined;
609
609
  }, [scrollable, scrollButtons, updateScrollObserver, childrenProp?.length]);
610
610
  React.useEffect(() => {
611
+ // eslint-disable-next-line react-hooks/set-state-in-effect
611
612
  setMounted(true);
612
613
  }, []);
613
614
  React.useEffect(() => {
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/material v9.1.1
2
+ * @mui/material v9.1.2
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/material v9.1.1
2
+ * @mui/material v9.1.2
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -12,11 +12,14 @@ var React = _interopRequireWildcard(require("react"));
12
12
  var _propTypes = _interopRequireDefault(require("prop-types"));
13
13
  var _useEnhancedEffect = _interopRequireDefault(require("@mui/utils/useEnhancedEffect"));
14
14
  var _useValueAsRef = _interopRequireDefault(require("@mui/utils/useValueAsRef"));
15
- var _TransitionGroupContext = _interopRequireDefault(require("react-transition-group/TransitionGroupContext"));
15
+ var _TransitionGroupContext = _interopRequireDefault(require("react-transition-group/cjs/TransitionGroupContext.js"));
16
16
  var _utils = require("../transitions/utils");
17
17
  var _jsxRuntime = require("react/jsx-runtime");
18
18
  // Material UI transitions must still work inside react-transition-group's TransitionGroup.
19
19
  // Import only its context module; do not import its Transition or TransitionGroup components.
20
+ // Use RTG's explicit CJS file for Node ESM/SSR; package.json's `browser` field redirects
21
+ // browser bundles to RTG's ESM file.
22
+ // eslint-disable-next-line import/extensions -- Node ESM needs the explicit .js extension.
20
23
 
21
24
  function resolveTimeouts(timeout) {
22
25
  if (timeout == null) {
@@ -7,7 +7,10 @@ import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
7
7
  import useValueAsRef from '@mui/utils/useValueAsRef';
8
8
  // Material UI transitions must still work inside react-transition-group's TransitionGroup.
9
9
  // Import only its context module; do not import its Transition or TransitionGroup components.
10
- import TransitionGroupContext from 'react-transition-group/TransitionGroupContext';
10
+ // Use RTG's explicit CJS file for Node ESM/SSR; package.json's `browser` field redirects
11
+ // browser bundles to RTG's ESM file.
12
+ // eslint-disable-next-line import/extensions -- Node ESM needs the explicit .js extension.
13
+ import TransitionGroupContext from 'react-transition-group/cjs/TransitionGroupContext.js';
11
14
  import { reflow } from "../transitions/utils.mjs";
12
15
  import { jsx as _jsx } from "react/jsx-runtime";
13
16
  function resolveTimeouts(timeout) {
@@ -5,4 +5,12 @@ declare module 'react-transition-group/TransitionGroupContext' {
5
5
  }
6
6
  const TransitionGroupContext: React.Context<TransitionGroupContextValue | null>;
7
7
  export default TransitionGroupContext;
8
+ }
9
+ declare module 'react-transition-group/cjs/TransitionGroupContext.js' {
10
+ import * as React from 'react';
11
+ interface TransitionGroupContextValue {
12
+ isMounting: boolean;
13
+ }
14
+ const TransitionGroupContext: React.Context<TransitionGroupContextValue | null>;
15
+ export default TransitionGroupContext;
8
16
  }
@@ -5,4 +5,12 @@ declare module 'react-transition-group/TransitionGroupContext' {
5
5
  }
6
6
  const TransitionGroupContext: React.Context<TransitionGroupContextValue | null>;
7
7
  export default TransitionGroupContext;
8
+ }
9
+ declare module 'react-transition-group/cjs/TransitionGroupContext.js' {
10
+ import * as React from 'react';
11
+ interface TransitionGroupContextValue {
12
+ isMounting: boolean;
13
+ }
14
+ const TransitionGroupContext: React.Context<TransitionGroupContextValue | null>;
15
+ export default TransitionGroupContext;
8
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/material",
3
- "version": "9.1.1",
3
+ "version": "9.1.2",
4
4
  "author": "MUI Team",
5
5
  "description": "Material UI is an open-source React component library that implements Google's Material Design. It's comprehensive and can be used in production out of the box.",
6
6
  "license": "MIT",
@@ -33,8 +33,8 @@
33
33
  "prop-types": "^15.8.1",
34
34
  "react-is": "^19.2.6",
35
35
  "react-transition-group": "^4.4.5",
36
- "@mui/core-downloads-tracker": "^9.1.1",
37
- "@mui/system": "^9.1.1",
36
+ "@mui/system": "^9.1.2",
37
+ "@mui/core-downloads-tracker": "^9.1.2",
38
38
  "@mui/types": "^9.1.1",
39
39
  "@mui/utils": "^9.1.1"
40
40
  },
@@ -67,6 +67,9 @@
67
67
  "engines": {
68
68
  "node": ">=14.0.0"
69
69
  },
70
+ "browser": {
71
+ "react-transition-group/cjs/TransitionGroupContext.js": "react-transition-group/esm/TransitionGroupContext.js"
72
+ },
70
73
  "pigment-css": {
71
74
  "vite": {
72
75
  "include": [
@@ -267,6 +267,7 @@ function useAutocomplete(props) {
267
267
  // Ensure the focusedItem is never inconsistent
268
268
  React.useEffect(() => {
269
269
  if (multiple && focusedItem > value.length - 1) {
270
+ // eslint-disable-next-line react-hooks/set-state-in-effect
270
271
  setFocusedItem(-1);
271
272
  focusItem(-1);
272
273
  }
@@ -853,13 +854,13 @@ function useAutocomplete(props) {
853
854
  break;
854
855
  case 'Enter':
855
856
  {
856
- // In freeSolo, only select the highlighted option if the user hasn't
857
- // typed new text (inputPristine) or explicitly interacted with an option
858
- // (keyboard, mouse, or touch any non-null reason). This lets typed
859
- // text win over a programmatic highlight (reason=null, e.g. from
860
- // syncHighlightedIndex matching a previous value) while still honoring
861
- // deliberate user interactions like hovering a suggestion then pressing Enter.
862
- const shouldSelectHighlighted = !freeSolo || inputPristine || highlightReasonRef.current !== null;
857
+ // In freeSolo, once the user has typed new text, Enter should commit that text instead
858
+ // of treating a programmatic highlight as an intentional selection. Programmatic
859
+ // highlights include autoHighlight and syncHighlightedIndex matching a previous value.
860
+ const hasProgrammaticHighlight = popupOpen && highlightedIndexRef.current !== -1 && highlightReasonRef.current === null;
861
+ const shouldCommitFreeSoloOverProgrammaticHighlight = freeSolo && !inputPristine && hasProgrammaticHighlight;
862
+ const shouldSelectHighlighted = !freeSolo || inputPristine || !hasProgrammaticHighlight;
863
+ const shouldPreventSubmitAfterFreeSoloCommit = shouldCommitFreeSoloOverProgrammaticHighlight && !touchScrolledRef.current;
863
864
  if (highlightedIndexRef.current !== -1 && popupOpen && shouldSelectHighlighted &&
864
865
  // After a touch-scroll the highlight is stale (the user scrolled
865
866
  // past it), so skip selection until the next deliberate interaction.
@@ -879,8 +880,8 @@ function useAutocomplete(props) {
879
880
  inputRef.current.setSelectionRange(inputRef.current.value.length, inputRef.current.value.length);
880
881
  }
881
882
  } else if (freeSolo && inputValue !== '' && inputValueIsSelectedValue === false) {
882
- if (multiple) {
883
- // Allow people to add new values before they submit the form.
883
+ if (multiple || shouldPreventSubmitAfterFreeSoloCommit) {
884
+ // Allow people to commit the freeSolo value before they submit the form.
884
885
  event.preventDefault();
885
886
  }
886
887
  selectNewValue(event, inputValue, 'createOption', 'freeSolo');
@@ -259,6 +259,7 @@ function useAutocomplete(props) {
259
259
  // Ensure the focusedItem is never inconsistent
260
260
  React.useEffect(() => {
261
261
  if (multiple && focusedItem > value.length - 1) {
262
+ // eslint-disable-next-line react-hooks/set-state-in-effect
262
263
  setFocusedItem(-1);
263
264
  focusItem(-1);
264
265
  }
@@ -845,13 +846,13 @@ function useAutocomplete(props) {
845
846
  break;
846
847
  case 'Enter':
847
848
  {
848
- // In freeSolo, only select the highlighted option if the user hasn't
849
- // typed new text (inputPristine) or explicitly interacted with an option
850
- // (keyboard, mouse, or touch any non-null reason). This lets typed
851
- // text win over a programmatic highlight (reason=null, e.g. from
852
- // syncHighlightedIndex matching a previous value) while still honoring
853
- // deliberate user interactions like hovering a suggestion then pressing Enter.
854
- const shouldSelectHighlighted = !freeSolo || inputPristine || highlightReasonRef.current !== null;
849
+ // In freeSolo, once the user has typed new text, Enter should commit that text instead
850
+ // of treating a programmatic highlight as an intentional selection. Programmatic
851
+ // highlights include autoHighlight and syncHighlightedIndex matching a previous value.
852
+ const hasProgrammaticHighlight = popupOpen && highlightedIndexRef.current !== -1 && highlightReasonRef.current === null;
853
+ const shouldCommitFreeSoloOverProgrammaticHighlight = freeSolo && !inputPristine && hasProgrammaticHighlight;
854
+ const shouldSelectHighlighted = !freeSolo || inputPristine || !hasProgrammaticHighlight;
855
+ const shouldPreventSubmitAfterFreeSoloCommit = shouldCommitFreeSoloOverProgrammaticHighlight && !touchScrolledRef.current;
855
856
  if (highlightedIndexRef.current !== -1 && popupOpen && shouldSelectHighlighted &&
856
857
  // After a touch-scroll the highlight is stale (the user scrolled
857
858
  // past it), so skip selection until the next deliberate interaction.
@@ -871,8 +872,8 @@ function useAutocomplete(props) {
871
872
  inputRef.current.setSelectionRange(inputRef.current.value.length, inputRef.current.value.length);
872
873
  }
873
874
  } else if (freeSolo && inputValue !== '' && inputValueIsSelectedValue === false) {
874
- if (multiple) {
875
- // Allow people to add new values before they submit the form.
875
+ if (multiple || shouldPreventSubmitAfterFreeSoloCommit) {
876
+ // Allow people to commit the freeSolo value before they submit the form.
876
877
  event.preventDefault();
877
878
  }
878
879
  selectNewValue(event, inputValue, 'createOption', 'freeSolo');
@@ -36,6 +36,7 @@ function useScrollTrigger(options = {}) {
36
36
  const [trigger, setTrigger] = React.useState(() => getTrigger(store, other));
37
37
  React.useEffect(() => {
38
38
  if (target === null) {
39
+ // eslint-disable-next-line react-hooks/set-state-in-effect
39
40
  return setTrigger(false);
40
41
  }
41
42
  const handleScroll = () => {
@@ -30,6 +30,7 @@ export default function useScrollTrigger(options = {}) {
30
30
  const [trigger, setTrigger] = React.useState(() => getTrigger(store, other));
31
31
  React.useEffect(() => {
32
32
  if (target === null) {
33
+ // eslint-disable-next-line react-hooks/set-state-in-effect
33
34
  return setTrigger(false);
34
35
  }
35
36
  const handleScroll = () => {
package/version/index.js CHANGED
@@ -4,9 +4,9 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.version = exports.prerelease = exports.patch = exports.minor = exports.major = exports.default = void 0;
7
- const version = exports.version = "9.1.1";
7
+ const version = exports.version = "9.1.2";
8
8
  const major = exports.major = Number("9");
9
9
  const minor = exports.minor = Number("1");
10
- const patch = exports.patch = Number("1");
10
+ const patch = exports.patch = Number("2");
11
11
  const prerelease = exports.prerelease = undefined;
12
12
  var _default = exports.default = version;
package/version/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- export const version = "9.1.1";
1
+ export const version = "9.1.2";
2
2
  export const major = Number("9");
3
3
  export const minor = Number("1");
4
- export const patch = Number("1");
4
+ export const patch = Number("2");
5
5
  export const prerelease = undefined;
6
6
  export default version;