@mui/material 5.16.14 → 5.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # [Versions](https://mui.com/versions/)
2
2
 
3
+ ## v5.17.0
4
+
5
+ _Mar 18, 2025_
6
+
7
+ A big thanks to the 2 contributors who made this release possible.
8
+
9
+ ### `@mui/material@5.17.0`
10
+
11
+ - [TextareaAutosize] Temporarily disconnect ResizeObserver to avoid loop error (#44540) (#45238) @DiegoAndai
12
+ - Support nested theme when upper theme is CSS vars theme (#45604) @siriwatknp
13
+
14
+ All contributors of this release in alphabetical order: @DiegoAndai, @siriwatknp
15
+
3
16
  ## v5.16.14
4
17
 
5
18
  <!-- generated comparing v5.16.13..v5.x -->
@@ -5,7 +5,7 @@ import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWith
5
5
  const _excluded = ["onChange", "maxRows", "minRows", "style", "value"];
6
6
  import * as React from 'react';
7
7
  import PropTypes from 'prop-types';
8
- import { unstable_debounce as debounce, unstable_useForkRef as useForkRef, unstable_useEnhancedEffect as useEnhancedEffect, unstable_ownerWindow as ownerWindow } from '@mui/utils';
8
+ import { unstable_debounce as debounce, unstable_useForkRef as useForkRef, unstable_useEnhancedEffect as useEnhancedEffect, unstable_useEventCallback as useEventCallback, unstable_ownerWindow as ownerWindow } from '@mui/utils';
9
9
  import { jsx as _jsx } from "react/jsx-runtime";
10
10
  import { jsxs as _jsxs } from "react/jsx-runtime";
11
11
  function getStyleValue(value) {
@@ -26,8 +26,15 @@ const styles = {
26
26
  transform: 'translateZ(0)'
27
27
  }
28
28
  };
29
+ function isObjectEmpty(object) {
30
+ // eslint-disable-next-line
31
+ for (const _ in object) {
32
+ return false;
33
+ }
34
+ return true;
35
+ }
29
36
  function isEmpty(obj) {
30
- return obj === undefined || obj === null || Object.keys(obj).length === 0 || obj.outerHeightStyle === 0 && !obj.overflowing;
37
+ return isObjectEmpty(obj) || obj.outerHeightStyle === 0 && !obj.overflowing;
31
38
  }
32
39
 
33
40
  /**
@@ -52,14 +59,18 @@ const TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize
52
59
  const {
53
60
  current: isControlled
54
61
  } = React.useRef(value != null);
55
- const inputRef = React.useRef(null);
56
- const handleRef = useForkRef(forwardedRef, inputRef);
62
+ const textareaRef = React.useRef(null);
63
+ const handleRef = useForkRef(forwardedRef, textareaRef);
57
64
  const heightRef = React.useRef(null);
58
- const shadowRef = React.useRef(null);
65
+ const hiddenTextareaRef = React.useRef(null);
59
66
  const calculateTextareaStyles = React.useCallback(() => {
60
- const input = inputRef.current;
61
- const containerWindow = ownerWindow(input);
62
- const computedStyle = containerWindow.getComputedStyle(input);
67
+ const textarea = textareaRef.current;
68
+ const hiddenTextarea = hiddenTextareaRef.current;
69
+ if (!textarea || !hiddenTextarea) {
70
+ return undefined;
71
+ }
72
+ const containerWindow = ownerWindow(textarea);
73
+ const computedStyle = containerWindow.getComputedStyle(textarea);
63
74
 
64
75
  // If input's width is shrunk and it's not visible, don't sync height.
65
76
  if (computedStyle.width === '0px') {
@@ -68,25 +79,24 @@ const TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize
68
79
  overflowing: false
69
80
  };
70
81
  }
71
- const inputShallow = shadowRef.current;
72
- inputShallow.style.width = computedStyle.width;
73
- inputShallow.value = input.value || props.placeholder || 'x';
74
- if (inputShallow.value.slice(-1) === '\n') {
82
+ hiddenTextarea.style.width = computedStyle.width;
83
+ hiddenTextarea.value = textarea.value || props.placeholder || 'x';
84
+ if (hiddenTextarea.value.slice(-1) === '\n') {
75
85
  // Certain fonts which overflow the line height will cause the textarea
76
86
  // to report a different scrollHeight depending on whether the last line
77
87
  // is empty. Make it non-empty to avoid this issue.
78
- inputShallow.value += ' ';
88
+ hiddenTextarea.value += ' ';
79
89
  }
80
90
  const boxSizing = computedStyle.boxSizing;
81
91
  const padding = getStyleValue(computedStyle.paddingBottom) + getStyleValue(computedStyle.paddingTop);
82
92
  const border = getStyleValue(computedStyle.borderBottomWidth) + getStyleValue(computedStyle.borderTopWidth);
83
93
 
84
94
  // The height of the inner content
85
- const innerHeight = inputShallow.scrollHeight;
95
+ const innerHeight = hiddenTextarea.scrollHeight;
86
96
 
87
97
  // Measure height of a textarea with a single row
88
- inputShallow.value = 'x';
89
- const singleRowHeight = inputShallow.scrollHeight;
98
+ hiddenTextarea.value = 'x';
99
+ const singleRowHeight = hiddenTextarea.scrollHeight;
90
100
 
91
101
  // The height of the outer content
92
102
  let outerHeight = innerHeight;
@@ -106,52 +116,63 @@ const TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize
106
116
  overflowing
107
117
  };
108
118
  }, [maxRows, minRows, props.placeholder]);
119
+ const didHeightChange = useEventCallback(() => {
120
+ const textarea = textareaRef.current;
121
+ const textareaStyles = calculateTextareaStyles();
122
+ if (!textarea || !textareaStyles || isEmpty(textareaStyles)) {
123
+ return false;
124
+ }
125
+ const outerHeightStyle = textareaStyles.outerHeightStyle;
126
+ return heightRef.current != null && heightRef.current !== outerHeightStyle;
127
+ });
109
128
  const syncHeight = React.useCallback(() => {
129
+ const textarea = textareaRef.current;
110
130
  const textareaStyles = calculateTextareaStyles();
111
- if (isEmpty(textareaStyles)) {
131
+ if (!textarea || !textareaStyles || isEmpty(textareaStyles)) {
112
132
  return;
113
133
  }
114
134
  const outerHeightStyle = textareaStyles.outerHeightStyle;
115
- const input = inputRef.current;
116
135
  if (heightRef.current !== outerHeightStyle) {
117
136
  heightRef.current = outerHeightStyle;
118
- input.style.height = `${outerHeightStyle}px`;
137
+ textarea.style.height = `${outerHeightStyle}px`;
119
138
  }
120
- input.style.overflow = textareaStyles.overflowing ? 'hidden' : '';
139
+ textarea.style.overflow = textareaStyles.overflowing ? 'hidden' : '';
121
140
  }, [calculateTextareaStyles]);
141
+ const frameRef = React.useRef(-1);
122
142
  useEnhancedEffect(() => {
123
- const handleResize = () => {
124
- syncHeight();
125
- };
126
- // Workaround a "ResizeObserver loop completed with undelivered notifications" error
127
- // in test.
128
- // Note that we might need to use this logic in production per https://github.com/WICG/resize-observer/issues/38
129
- // Also see https://github.com/mui/mui-x/issues/8733
130
- let rAF;
131
- const rAFHandleResize = () => {
132
- cancelAnimationFrame(rAF);
133
- rAF = requestAnimationFrame(() => {
134
- handleResize();
135
- });
136
- };
137
- const debounceHandleResize = debounce(handleResize);
138
- const input = inputRef.current;
139
- const containerWindow = ownerWindow(input);
140
- containerWindow.addEventListener('resize', debounceHandleResize);
143
+ const debouncedHandleResize = debounce(syncHeight);
144
+ const textarea = textareaRef == null ? void 0 : textareaRef.current;
145
+ if (!textarea) {
146
+ return undefined;
147
+ }
148
+ const containerWindow = ownerWindow(textarea);
149
+ containerWindow.addEventListener('resize', debouncedHandleResize);
141
150
  let resizeObserver;
142
151
  if (typeof ResizeObserver !== 'undefined') {
143
- resizeObserver = new ResizeObserver(process.env.NODE_ENV === 'test' ? rAFHandleResize : handleResize);
144
- resizeObserver.observe(input);
152
+ resizeObserver = new ResizeObserver(() => {
153
+ if (didHeightChange()) {
154
+ // avoid "ResizeObserver loop completed with undelivered notifications" error
155
+ // by temporarily unobserving the textarea element while manipulating the height
156
+ // and reobserving one frame later
157
+ resizeObserver.unobserve(textarea);
158
+ cancelAnimationFrame(frameRef.current);
159
+ syncHeight();
160
+ frameRef.current = requestAnimationFrame(() => {
161
+ resizeObserver.observe(textarea);
162
+ });
163
+ }
164
+ });
165
+ resizeObserver.observe(textarea);
145
166
  }
146
167
  return () => {
147
- debounceHandleResize.clear();
148
- cancelAnimationFrame(rAF);
149
- containerWindow.removeEventListener('resize', debounceHandleResize);
168
+ debouncedHandleResize.clear();
169
+ cancelAnimationFrame(frameRef.current);
170
+ containerWindow.removeEventListener('resize', debouncedHandleResize);
150
171
  if (resizeObserver) {
151
172
  resizeObserver.disconnect();
152
173
  }
153
174
  };
154
- }, [calculateTextareaStyles, syncHeight]);
175
+ }, [calculateTextareaStyles, syncHeight, didHeightChange]);
155
176
  useEnhancedEffect(() => {
156
177
  syncHeight();
157
178
  });
@@ -176,7 +197,7 @@ const TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize
176
197
  "aria-hidden": true,
177
198
  className: props.className,
178
199
  readOnly: true,
179
- ref: shadowRef,
200
+ ref: hiddenTextareaRef,
180
201
  tabIndex: -1,
181
202
  style: _extends({}, styles.shadow, style, {
182
203
  paddingTop: 0,
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/material v5.16.14
2
+ * @mui/material v5.17.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -4,7 +4,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
4
4
  import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
5
5
  import * as React from 'react';
6
6
  import PropTypes from 'prop-types';
7
- import { unstable_debounce as debounce, unstable_useForkRef as useForkRef, unstable_useEnhancedEffect as useEnhancedEffect, unstable_ownerWindow as ownerWindow } from '@mui/utils';
7
+ import { unstable_debounce as debounce, unstable_useForkRef as useForkRef, unstable_useEnhancedEffect as useEnhancedEffect, unstable_useEventCallback as useEventCallback, unstable_ownerWindow as ownerWindow } from '@mui/utils';
8
8
  import { jsx as _jsx } from "react/jsx-runtime";
9
9
  import { jsxs as _jsxs } from "react/jsx-runtime";
10
10
  function getStyleValue(value) {
@@ -25,8 +25,15 @@ var styles = {
25
25
  transform: 'translateZ(0)'
26
26
  }
27
27
  };
28
+ function isObjectEmpty(object) {
29
+ // eslint-disable-next-line
30
+ for (var _ in object) {
31
+ return false;
32
+ }
33
+ return true;
34
+ }
28
35
  function isEmpty(obj) {
29
- return obj === undefined || obj === null || Object.keys(obj).length === 0 || obj.outerHeightStyle === 0 && !obj.overflowing;
36
+ return isObjectEmpty(obj) || obj.outerHeightStyle === 0 && !obj.overflowing;
30
37
  }
31
38
 
32
39
  /**
@@ -49,14 +56,18 @@ var TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize(p
49
56
  other = _objectWithoutProperties(props, ["onChange", "maxRows", "minRows", "style", "value"]);
50
57
  var _React$useRef = React.useRef(value != null),
51
58
  isControlled = _React$useRef.current;
52
- var inputRef = React.useRef(null);
53
- var handleRef = useForkRef(forwardedRef, inputRef);
59
+ var textareaRef = React.useRef(null);
60
+ var handleRef = useForkRef(forwardedRef, textareaRef);
54
61
  var heightRef = React.useRef(null);
55
- var shadowRef = React.useRef(null);
62
+ var hiddenTextareaRef = React.useRef(null);
56
63
  var calculateTextareaStyles = React.useCallback(function () {
57
- var input = inputRef.current;
58
- var containerWindow = ownerWindow(input);
59
- var computedStyle = containerWindow.getComputedStyle(input);
64
+ var textarea = textareaRef.current;
65
+ var hiddenTextarea = hiddenTextareaRef.current;
66
+ if (!textarea || !hiddenTextarea) {
67
+ return undefined;
68
+ }
69
+ var containerWindow = ownerWindow(textarea);
70
+ var computedStyle = containerWindow.getComputedStyle(textarea);
60
71
 
61
72
  // If input's width is shrunk and it's not visible, don't sync height.
62
73
  if (computedStyle.width === '0px') {
@@ -65,25 +76,24 @@ var TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize(p
65
76
  overflowing: false
66
77
  };
67
78
  }
68
- var inputShallow = shadowRef.current;
69
- inputShallow.style.width = computedStyle.width;
70
- inputShallow.value = input.value || props.placeholder || 'x';
71
- if (inputShallow.value.slice(-1) === '\n') {
79
+ hiddenTextarea.style.width = computedStyle.width;
80
+ hiddenTextarea.value = textarea.value || props.placeholder || 'x';
81
+ if (hiddenTextarea.value.slice(-1) === '\n') {
72
82
  // Certain fonts which overflow the line height will cause the textarea
73
83
  // to report a different scrollHeight depending on whether the last line
74
84
  // is empty. Make it non-empty to avoid this issue.
75
- inputShallow.value += ' ';
85
+ hiddenTextarea.value += ' ';
76
86
  }
77
87
  var boxSizing = computedStyle.boxSizing;
78
88
  var padding = getStyleValue(computedStyle.paddingBottom) + getStyleValue(computedStyle.paddingTop);
79
89
  var border = getStyleValue(computedStyle.borderBottomWidth) + getStyleValue(computedStyle.borderTopWidth);
80
90
 
81
91
  // The height of the inner content
82
- var innerHeight = inputShallow.scrollHeight;
92
+ var innerHeight = hiddenTextarea.scrollHeight;
83
93
 
84
94
  // Measure height of a textarea with a single row
85
- inputShallow.value = 'x';
86
- var singleRowHeight = inputShallow.scrollHeight;
95
+ hiddenTextarea.value = 'x';
96
+ var singleRowHeight = hiddenTextarea.scrollHeight;
87
97
 
88
98
  // The height of the outer content
89
99
  var outerHeight = innerHeight;
@@ -103,52 +113,63 @@ var TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize(p
103
113
  overflowing: overflowing
104
114
  };
105
115
  }, [maxRows, minRows, props.placeholder]);
116
+ var didHeightChange = useEventCallback(function () {
117
+ var textarea = textareaRef.current;
118
+ var textareaStyles = calculateTextareaStyles();
119
+ if (!textarea || !textareaStyles || isEmpty(textareaStyles)) {
120
+ return false;
121
+ }
122
+ var outerHeightStyle = textareaStyles.outerHeightStyle;
123
+ return heightRef.current != null && heightRef.current !== outerHeightStyle;
124
+ });
106
125
  var syncHeight = React.useCallback(function () {
126
+ var textarea = textareaRef.current;
107
127
  var textareaStyles = calculateTextareaStyles();
108
- if (isEmpty(textareaStyles)) {
128
+ if (!textarea || !textareaStyles || isEmpty(textareaStyles)) {
109
129
  return;
110
130
  }
111
131
  var outerHeightStyle = textareaStyles.outerHeightStyle;
112
- var input = inputRef.current;
113
132
  if (heightRef.current !== outerHeightStyle) {
114
133
  heightRef.current = outerHeightStyle;
115
- input.style.height = "".concat(outerHeightStyle, "px");
134
+ textarea.style.height = "".concat(outerHeightStyle, "px");
116
135
  }
117
- input.style.overflow = textareaStyles.overflowing ? 'hidden' : '';
136
+ textarea.style.overflow = textareaStyles.overflowing ? 'hidden' : '';
118
137
  }, [calculateTextareaStyles]);
138
+ var frameRef = React.useRef(-1);
119
139
  useEnhancedEffect(function () {
120
- var handleResize = function handleResize() {
121
- syncHeight();
122
- };
123
- // Workaround a "ResizeObserver loop completed with undelivered notifications" error
124
- // in test.
125
- // Note that we might need to use this logic in production per https://github.com/WICG/resize-observer/issues/38
126
- // Also see https://github.com/mui/mui-x/issues/8733
127
- var rAF;
128
- var rAFHandleResize = function rAFHandleResize() {
129
- cancelAnimationFrame(rAF);
130
- rAF = requestAnimationFrame(function () {
131
- handleResize();
132
- });
133
- };
134
- var debounceHandleResize = debounce(handleResize);
135
- var input = inputRef.current;
136
- var containerWindow = ownerWindow(input);
137
- containerWindow.addEventListener('resize', debounceHandleResize);
140
+ var debouncedHandleResize = debounce(syncHeight);
141
+ var textarea = textareaRef == null ? void 0 : textareaRef.current;
142
+ if (!textarea) {
143
+ return undefined;
144
+ }
145
+ var containerWindow = ownerWindow(textarea);
146
+ containerWindow.addEventListener('resize', debouncedHandleResize);
138
147
  var resizeObserver;
139
148
  if (typeof ResizeObserver !== 'undefined') {
140
- resizeObserver = new ResizeObserver(process.env.NODE_ENV === 'test' ? rAFHandleResize : handleResize);
141
- resizeObserver.observe(input);
149
+ resizeObserver = new ResizeObserver(function () {
150
+ if (didHeightChange()) {
151
+ // avoid "ResizeObserver loop completed with undelivered notifications" error
152
+ // by temporarily unobserving the textarea element while manipulating the height
153
+ // and reobserving one frame later
154
+ resizeObserver.unobserve(textarea);
155
+ cancelAnimationFrame(frameRef.current);
156
+ syncHeight();
157
+ frameRef.current = requestAnimationFrame(function () {
158
+ resizeObserver.observe(textarea);
159
+ });
160
+ }
161
+ });
162
+ resizeObserver.observe(textarea);
142
163
  }
143
164
  return function () {
144
- debounceHandleResize.clear();
145
- cancelAnimationFrame(rAF);
146
- containerWindow.removeEventListener('resize', debounceHandleResize);
165
+ debouncedHandleResize.clear();
166
+ cancelAnimationFrame(frameRef.current);
167
+ containerWindow.removeEventListener('resize', debouncedHandleResize);
147
168
  if (resizeObserver) {
148
169
  resizeObserver.disconnect();
149
170
  }
150
171
  };
151
- }, [calculateTextareaStyles, syncHeight]);
172
+ }, [calculateTextareaStyles, syncHeight, didHeightChange]);
152
173
  useEnhancedEffect(function () {
153
174
  syncHeight();
154
175
  });
@@ -173,7 +194,7 @@ var TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize(p
173
194
  "aria-hidden": true,
174
195
  className: props.className,
175
196
  readOnly: true,
176
- ref: shadowRef,
197
+ ref: hiddenTextareaRef,
177
198
  tabIndex: -1,
178
199
  style: _extends({}, styles.shadow, style, {
179
200
  paddingTop: 0,
package/legacy/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/material v5.16.14
2
+ * @mui/material v5.17.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -11,9 +11,21 @@ export default function ThemeProvider(_ref) {
11
11
  var themeInput = _ref.theme,
12
12
  props = _objectWithoutProperties(_ref, ["theme"]);
13
13
  var scopedTheme = themeInput[THEME_ID];
14
+ var finalTheme = scopedTheme || themeInput;
15
+ if (typeof themeInput !== 'function') {
16
+ if (scopedTheme && !scopedTheme.vars) {
17
+ finalTheme = _extends({}, scopedTheme, {
18
+ vars: null
19
+ });
20
+ } else if (themeInput && !themeInput.vars) {
21
+ finalTheme = _extends({}, themeInput, {
22
+ vars: null
23
+ });
24
+ }
25
+ }
14
26
  return /*#__PURE__*/_jsx(SystemThemeProvider, _extends({}, props, {
15
27
  themeId: scopedTheme ? THEME_ID : undefined,
16
- theme: scopedTheme || themeInput
28
+ theme: finalTheme
17
29
  }));
18
30
  }
19
31
  process.env.NODE_ENV !== "production" ? ThemeProvider.propTypes = {
@@ -26,7 +26,10 @@ function createTheme() {
26
26
  typographyInput = _options$typography === void 0 ? {} : _options$typography,
27
27
  shapeInput = options.shape,
28
28
  other = _objectWithoutProperties(options, ["breakpoints", "mixins", "spacing", "palette", "transitions", "typography", "shape"]);
29
- if (options.vars) {
29
+ if (options.vars &&
30
+ // The error should throw only for the root theme creation because user is not allowed to use a custom node `vars`.
31
+ // `generateCssVars` is the closest identifier for checking that the `options` is a result of `extendTheme` with CSS variables so that user can create new theme for nested ThemeProvider.
32
+ options.generateCssVars === undefined) {
30
33
  throw new Error(process.env.NODE_ENV !== "production" ? "MUI: `vars` is a private field used for CSS variables support.\nPlease use another name." : _formatMuiErrorMessage(18));
31
34
  }
32
35
  var palette = createPalette(paletteInput);
@@ -1,7 +1,7 @@
1
- export var version = "5.16.14";
1
+ export var version = "5.17.0";
2
2
  export var major = Number("5");
3
- export var minor = Number("16");
4
- export var patch = Number("14");
3
+ export var minor = Number("17");
4
+ export var patch = Number("0");
5
5
  export var preReleaseLabel = undefined || null;
6
6
  export var preReleaseNumber = Number(undefined) || null;
7
7
  export default version;
@@ -5,7 +5,7 @@ import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWith
5
5
  const _excluded = ["onChange", "maxRows", "minRows", "style", "value"];
6
6
  import * as React from 'react';
7
7
  import PropTypes from 'prop-types';
8
- import { unstable_debounce as debounce, unstable_useForkRef as useForkRef, unstable_useEnhancedEffect as useEnhancedEffect, unstable_ownerWindow as ownerWindow } from '@mui/utils';
8
+ import { unstable_debounce as debounce, unstable_useForkRef as useForkRef, unstable_useEnhancedEffect as useEnhancedEffect, unstable_useEventCallback as useEventCallback, unstable_ownerWindow as ownerWindow } from '@mui/utils';
9
9
  import { jsx as _jsx } from "react/jsx-runtime";
10
10
  import { jsxs as _jsxs } from "react/jsx-runtime";
11
11
  function getStyleValue(value) {
@@ -26,8 +26,15 @@ const styles = {
26
26
  transform: 'translateZ(0)'
27
27
  }
28
28
  };
29
+ function isObjectEmpty(object) {
30
+ // eslint-disable-next-line
31
+ for (const _ in object) {
32
+ return false;
33
+ }
34
+ return true;
35
+ }
29
36
  function isEmpty(obj) {
30
- return obj === undefined || obj === null || Object.keys(obj).length === 0 || obj.outerHeightStyle === 0 && !obj.overflowing;
37
+ return isObjectEmpty(obj) || obj.outerHeightStyle === 0 && !obj.overflowing;
31
38
  }
32
39
 
33
40
  /**
@@ -52,14 +59,18 @@ const TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize
52
59
  const {
53
60
  current: isControlled
54
61
  } = React.useRef(value != null);
55
- const inputRef = React.useRef(null);
56
- const handleRef = useForkRef(forwardedRef, inputRef);
62
+ const textareaRef = React.useRef(null);
63
+ const handleRef = useForkRef(forwardedRef, textareaRef);
57
64
  const heightRef = React.useRef(null);
58
- const shadowRef = React.useRef(null);
65
+ const hiddenTextareaRef = React.useRef(null);
59
66
  const calculateTextareaStyles = React.useCallback(() => {
60
- const input = inputRef.current;
61
- const containerWindow = ownerWindow(input);
62
- const computedStyle = containerWindow.getComputedStyle(input);
67
+ const textarea = textareaRef.current;
68
+ const hiddenTextarea = hiddenTextareaRef.current;
69
+ if (!textarea || !hiddenTextarea) {
70
+ return undefined;
71
+ }
72
+ const containerWindow = ownerWindow(textarea);
73
+ const computedStyle = containerWindow.getComputedStyle(textarea);
63
74
 
64
75
  // If input's width is shrunk and it's not visible, don't sync height.
65
76
  if (computedStyle.width === '0px') {
@@ -68,25 +79,24 @@ const TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize
68
79
  overflowing: false
69
80
  };
70
81
  }
71
- const inputShallow = shadowRef.current;
72
- inputShallow.style.width = computedStyle.width;
73
- inputShallow.value = input.value || props.placeholder || 'x';
74
- if (inputShallow.value.slice(-1) === '\n') {
82
+ hiddenTextarea.style.width = computedStyle.width;
83
+ hiddenTextarea.value = textarea.value || props.placeholder || 'x';
84
+ if (hiddenTextarea.value.slice(-1) === '\n') {
75
85
  // Certain fonts which overflow the line height will cause the textarea
76
86
  // to report a different scrollHeight depending on whether the last line
77
87
  // is empty. Make it non-empty to avoid this issue.
78
- inputShallow.value += ' ';
88
+ hiddenTextarea.value += ' ';
79
89
  }
80
90
  const boxSizing = computedStyle.boxSizing;
81
91
  const padding = getStyleValue(computedStyle.paddingBottom) + getStyleValue(computedStyle.paddingTop);
82
92
  const border = getStyleValue(computedStyle.borderBottomWidth) + getStyleValue(computedStyle.borderTopWidth);
83
93
 
84
94
  // The height of the inner content
85
- const innerHeight = inputShallow.scrollHeight;
95
+ const innerHeight = hiddenTextarea.scrollHeight;
86
96
 
87
97
  // Measure height of a textarea with a single row
88
- inputShallow.value = 'x';
89
- const singleRowHeight = inputShallow.scrollHeight;
98
+ hiddenTextarea.value = 'x';
99
+ const singleRowHeight = hiddenTextarea.scrollHeight;
90
100
 
91
101
  // The height of the outer content
92
102
  let outerHeight = innerHeight;
@@ -106,52 +116,63 @@ const TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize
106
116
  overflowing
107
117
  };
108
118
  }, [maxRows, minRows, props.placeholder]);
119
+ const didHeightChange = useEventCallback(() => {
120
+ const textarea = textareaRef.current;
121
+ const textareaStyles = calculateTextareaStyles();
122
+ if (!textarea || !textareaStyles || isEmpty(textareaStyles)) {
123
+ return false;
124
+ }
125
+ const outerHeightStyle = textareaStyles.outerHeightStyle;
126
+ return heightRef.current != null && heightRef.current !== outerHeightStyle;
127
+ });
109
128
  const syncHeight = React.useCallback(() => {
129
+ const textarea = textareaRef.current;
110
130
  const textareaStyles = calculateTextareaStyles();
111
- if (isEmpty(textareaStyles)) {
131
+ if (!textarea || !textareaStyles || isEmpty(textareaStyles)) {
112
132
  return;
113
133
  }
114
134
  const outerHeightStyle = textareaStyles.outerHeightStyle;
115
- const input = inputRef.current;
116
135
  if (heightRef.current !== outerHeightStyle) {
117
136
  heightRef.current = outerHeightStyle;
118
- input.style.height = `${outerHeightStyle}px`;
137
+ textarea.style.height = `${outerHeightStyle}px`;
119
138
  }
120
- input.style.overflow = textareaStyles.overflowing ? 'hidden' : '';
139
+ textarea.style.overflow = textareaStyles.overflowing ? 'hidden' : '';
121
140
  }, [calculateTextareaStyles]);
141
+ const frameRef = React.useRef(-1);
122
142
  useEnhancedEffect(() => {
123
- const handleResize = () => {
124
- syncHeight();
125
- };
126
- // Workaround a "ResizeObserver loop completed with undelivered notifications" error
127
- // in test.
128
- // Note that we might need to use this logic in production per https://github.com/WICG/resize-observer/issues/38
129
- // Also see https://github.com/mui/mui-x/issues/8733
130
- let rAF;
131
- const rAFHandleResize = () => {
132
- cancelAnimationFrame(rAF);
133
- rAF = requestAnimationFrame(() => {
134
- handleResize();
135
- });
136
- };
137
- const debounceHandleResize = debounce(handleResize);
138
- const input = inputRef.current;
139
- const containerWindow = ownerWindow(input);
140
- containerWindow.addEventListener('resize', debounceHandleResize);
143
+ const debouncedHandleResize = debounce(syncHeight);
144
+ const textarea = textareaRef?.current;
145
+ if (!textarea) {
146
+ return undefined;
147
+ }
148
+ const containerWindow = ownerWindow(textarea);
149
+ containerWindow.addEventListener('resize', debouncedHandleResize);
141
150
  let resizeObserver;
142
151
  if (typeof ResizeObserver !== 'undefined') {
143
- resizeObserver = new ResizeObserver(process.env.NODE_ENV === 'test' ? rAFHandleResize : handleResize);
144
- resizeObserver.observe(input);
152
+ resizeObserver = new ResizeObserver(() => {
153
+ if (didHeightChange()) {
154
+ // avoid "ResizeObserver loop completed with undelivered notifications" error
155
+ // by temporarily unobserving the textarea element while manipulating the height
156
+ // and reobserving one frame later
157
+ resizeObserver.unobserve(textarea);
158
+ cancelAnimationFrame(frameRef.current);
159
+ syncHeight();
160
+ frameRef.current = requestAnimationFrame(() => {
161
+ resizeObserver.observe(textarea);
162
+ });
163
+ }
164
+ });
165
+ resizeObserver.observe(textarea);
145
166
  }
146
167
  return () => {
147
- debounceHandleResize.clear();
148
- cancelAnimationFrame(rAF);
149
- containerWindow.removeEventListener('resize', debounceHandleResize);
168
+ debouncedHandleResize.clear();
169
+ cancelAnimationFrame(frameRef.current);
170
+ containerWindow.removeEventListener('resize', debouncedHandleResize);
150
171
  if (resizeObserver) {
151
172
  resizeObserver.disconnect();
152
173
  }
153
174
  };
154
- }, [calculateTextareaStyles, syncHeight]);
175
+ }, [calculateTextareaStyles, syncHeight, didHeightChange]);
155
176
  useEnhancedEffect(() => {
156
177
  syncHeight();
157
178
  });
@@ -176,7 +197,7 @@ const TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize
176
197
  "aria-hidden": true,
177
198
  className: props.className,
178
199
  readOnly: true,
179
- ref: shadowRef,
200
+ ref: hiddenTextareaRef,
180
201
  tabIndex: -1,
181
202
  style: _extends({}, styles.shadow, style, {
182
203
  paddingTop: 0,
package/modern/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/material v5.16.14
2
+ * @mui/material v5.17.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the