@navikt/ds-react 5.10.4 → 5.11.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/_docs.json CHANGED
@@ -3507,6 +3507,25 @@
3507
3507
  }
3508
3508
  ],
3509
3509
  "required": false,
3510
+ "type": {
3511
+ "name": "boolean | \"vertical\" | \"horizontal\""
3512
+ }
3513
+ },
3514
+ "UNSAFE_autoScrollbar": {
3515
+ "defaultValue": null,
3516
+ "description": "Textarea will stop growing and get a scrollbar when there's no more room to grow.\nRequires `display:flex` on the parent.\nExperimental feature that may be removed or get breaking changes in a minor version.",
3517
+ "name": "UNSAFE_autoScrollbar",
3518
+ "parent": {
3519
+ "fileName": "src/form/Textarea.tsx",
3520
+ "name": "TextareaProps"
3521
+ },
3522
+ "declarations": [
3523
+ {
3524
+ "fileName": "src/form/Textarea.tsx",
3525
+ "name": "TextareaProps"
3526
+ }
3527
+ ],
3528
+ "required": false,
3510
3529
  "type": {
3511
3530
  "name": "boolean"
3512
3531
  }
@@ -10411,6 +10430,25 @@
10411
10430
  "name": "number"
10412
10431
  }
10413
10432
  },
10433
+ "autoScrollbar": {
10434
+ "defaultValue": null,
10435
+ "description": "If true, textarea will never get `overflow:hidden`",
10436
+ "name": "autoScrollbar",
10437
+ "parent": {
10438
+ "fileName": "src/util/TextareaAutoSize.tsx",
10439
+ "name": "TextareaAutosizeProps"
10440
+ },
10441
+ "declarations": [
10442
+ {
10443
+ "fileName": "src/util/TextareaAutoSize.tsx",
10444
+ "name": "TextareaAutosizeProps"
10445
+ }
10446
+ ],
10447
+ "required": false,
10448
+ "type": {
10449
+ "name": "boolean"
10450
+ }
10451
+ },
10414
10452
  "className": {
10415
10453
  "defaultValue": null,
10416
10454
  "description": "",
@@ -60,7 +60,7 @@ const useFormField_1 = require("./useFormField");
60
60
  exports.Textarea = (0, react_1.forwardRef)((props, ref) => {
61
61
  var _a, _b, _c;
62
62
  const { inputProps, errorId, showErrorMsg, hasError, size, inputDescriptionId, } = (0, useFormField_1.useFormField)(props, "textarea");
63
- const { label, className, description, maxLength, hideLabel = false, resize, i18n, readOnly } = props, rest = __rest(props, ["label", "className", "description", "maxLength", "hideLabel", "resize", "i18n", "readOnly"]);
63
+ const { label, className, description, maxLength, hideLabel = false, resize, UNSAFE_autoScrollbar, i18n, readOnly } = props, rest = __rest(props, ["label", "className", "description", "maxLength", "hideLabel", "resize", "UNSAFE_autoScrollbar", "i18n", "readOnly"]);
64
64
  const maxLengthId = (0, util_1.useId)();
65
65
  const hasMaxLength = maxLength !== undefined && maxLength > 0;
66
66
  const [controlledValue, setControlledValue] = (0, react_1.useState)((_a = props === null || props === void 0 ? void 0 : props.defaultValue) !== null && _a !== void 0 ? _a : "");
@@ -79,7 +79,8 @@ exports.Textarea = (0, react_1.forwardRef)((props, ref) => {
79
79
  "navds-form-field--readonly": readOnly,
80
80
  "navds-textarea--readonly": readOnly,
81
81
  "navds-textarea--error": hasError,
82
- "navds-textarea--resize": resize,
82
+ "navds-textarea--autoscrollbar": UNSAFE_autoScrollbar,
83
+ [`navds-textarea--resize-${resize === true ? "both" : resize}`]: resize,
83
84
  }) },
84
85
  react_1.default.createElement(typography_1.Label, { htmlFor: inputProps.id, size: size, className: (0, clsx_1.default)("navds-form-field__label", {
85
86
  "navds-sr-only": hideLabel,
@@ -92,7 +93,7 @@ exports.Textarea = (0, react_1.forwardRef)((props, ref) => {
92
93
  react_1.default.createElement("div", { className: "navds-textarea__wrapper" },
93
94
  react_1.default.createElement(TextareaAutoSize_1.default, Object.assign({}, (0, util_1.omit)(rest, ["error", "errorId", "size"]), inputProps, { onChange: (e) => props.onChange
94
95
  ? props.onChange(e)
95
- : setControlledValue(e.target.value), minRows: getMinRows(), ref: ref, readOnly: readOnly, className: (0, clsx_1.default)("navds-textarea__input", "navds-body-short", `navds-body-short--${size !== null && size !== void 0 ? size : "medium"}`) }, (describedBy ? { "aria-describedby": describedBy } : {}))),
96
+ : setControlledValue(e.target.value), minRows: getMinRows(), autoScrollbar: UNSAFE_autoScrollbar, ref: ref, readOnly: readOnly, className: (0, clsx_1.default)("navds-textarea__input", "navds-body-short", `navds-body-short--${size !== null && size !== void 0 ? size : "medium"}`) }, (describedBy ? { "aria-describedby": describedBy } : {}))),
96
97
  hasMaxLength && !readOnly && !inputProps.disabled && (react_1.default.createElement(react_1.default.Fragment, null,
97
98
  react_1.default.createElement("span", { id: maxLengthId, className: "navds-sr-only" }, `Tekstområde med plass til ${maxLength} tegn.`),
98
99
  react_1.default.createElement(TextareaCounter_1.default, { maxLength: maxLength, currentLength: (_c = (_b = props.value) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : controlledValue === null || controlledValue === void 0 ? void 0 : controlledValue.length, size: size, i18n: i18n })))),
@@ -1,18 +1,57 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
29
  const clsx_1 = __importDefault(require("clsx"));
7
- const react_1 = __importDefault(require("react"));
30
+ const react_1 = __importStar(require("react"));
8
31
  const typography_1 = require("../typography");
32
+ const debounce_1 = __importDefault(require("../util/debounce"));
9
33
  const TextareaCounter = ({ maxLength, currentLength, size, i18n }) => {
10
- var _a, _b;
11
34
  const difference = maxLength - currentLength;
12
- return (react_1.default.createElement(typography_1.BodyShort, { className: (0, clsx_1.default)("navds-textarea__counter", {
13
- "navds-textarea__counter--error": difference < 0,
14
- }), role: difference < 20 ? "status" : undefined, size: size }, difference < 0
35
+ const [debouncedDiff, setDebouncedDiff] = (0, react_1.useState)(difference);
36
+ (0, react_1.useEffect)(() => {
37
+ const debounceFunc = (0, debounce_1.default)(() => {
38
+ setDebouncedDiff(difference);
39
+ }, 2000);
40
+ debounceFunc();
41
+ return () => {
42
+ debounceFunc.clear();
43
+ };
44
+ }, [difference]);
45
+ return (react_1.default.createElement(react_1.default.Fragment, null,
46
+ difference < 20 && (react_1.default.createElement("span", { role: "status", className: "navds-textarea__sr-counter navds-sr-only" }, getCounterText(debouncedDiff, i18n))),
47
+ react_1.default.createElement(typography_1.BodyShort, { className: (0, clsx_1.default)("navds-textarea__counter", {
48
+ "navds-textarea__counter--error": difference < 0,
49
+ }), size: size }, getCounterText(difference, i18n))));
50
+ };
51
+ const getCounterText = (difference, i18n) => {
52
+ var _a, _b;
53
+ return difference < 0
15
54
  ? `${Math.abs(difference)} ${(_a = i18n === null || i18n === void 0 ? void 0 : i18n.counterTooMuch) !== null && _a !== void 0 ? _a : "tegn for mye"}`
16
- : `${difference} ${(_b = i18n === null || i18n === void 0 ? void 0 : i18n.counterLeft) !== null && _b !== void 0 ? _b : "tegn igjen"}`));
55
+ : `${difference} ${(_b = i18n === null || i18n === void 0 ? void 0 : i18n.counterLeft) !== null && _b !== void 0 ? _b : "tegn igjen"}`;
17
56
  };
18
57
  exports.default = TextareaCounter;
@@ -37,53 +37,75 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
- /* https://github.com/mui/material-ui/blob/master/packages/mui-base/src/TextareaAutosize/TextareaAutosize.js */
40
+ /* https://github.com/mui/material-ui/blob/master/packages/mui-base/src/TextareaAutosize/TextareaAutosize.tsx */
41
41
  const react_1 = __importStar(require("react"));
42
42
  const react_dom_1 = __importDefault(require("react-dom"));
43
43
  const util_1 = require("../util");
44
+ const updateState = (prevState, newState, renders) => {
45
+ const { outerHeightStyle, overflow } = newState;
46
+ // Need a large enough difference to update the height.
47
+ // This prevents infinite rendering loop.
48
+ if (renders.current < 20 &&
49
+ ((outerHeightStyle > 0 &&
50
+ Math.abs((prevState.outerHeightStyle || 0) - outerHeightStyle) > 1) ||
51
+ prevState.overflow !== overflow)) {
52
+ renders.current += 1;
53
+ return {
54
+ overflow,
55
+ outerHeightStyle,
56
+ };
57
+ }
58
+ if (process.env.NODE_ENV !== "production") {
59
+ if (renders.current === 20) {
60
+ console.error([
61
+ "Textarea: Too many re-renders. The layout is unstable.",
62
+ "TextareaAutosize limits the number of renders to prevent an infinite loop.",
63
+ ].join("\n"));
64
+ }
65
+ }
66
+ return prevState;
67
+ };
44
68
  /**
45
- * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerDocument.ts
46
- * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerWindow.ts
69
+ * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerDocument/ownerDocument.ts
70
+ * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerWindow/ownerWindow.ts
47
71
  */
48
72
  const ownerWindow = (node) => {
49
73
  const doc = (node && node.ownerDocument) || document;
50
74
  return doc.defaultView || window;
51
75
  };
52
- function getStyleValue(computedStyle, property) {
53
- return parseInt(computedStyle[property], 10) || 0;
76
+ function getStyleValue(value) {
77
+ return parseInt(value, 10) || 0;
54
78
  }
55
79
  const TextareaAutosize = (0, react_1.forwardRef)((_a, ref) => {
56
- var { className, onChange, maxRows, minRows = 1, style, value } = _a, other = __rest(_a, ["className", "onChange", "maxRows", "minRows", "style", "value"]);
80
+ var { className, onChange, maxRows, minRows = 1, autoScrollbar, style, value } = _a, other = __rest(_a, ["className", "onChange", "maxRows", "minRows", "autoScrollbar", "style", "value"]);
57
81
  const { current: isControlled } = (0, react_1.useRef)(value != null);
58
82
  const inputRef = (0, react_1.useRef)(null);
59
83
  const handleRef = (0, react_1.useMemo)(() => (0, util_1.mergeRefs)([inputRef, ref]), [ref]);
60
84
  const shadowRef = (0, react_1.useRef)(null);
61
85
  const renders = (0, react_1.useRef)(0);
62
- const [state, setState] = (0, react_1.useState)({});
86
+ const [state, setState] = (0, react_1.useState)({ outerHeightStyle: 0 });
63
87
  const getUpdatedState = react_1.default.useCallback(() => {
64
- if (!inputRef.current || !shadowRef.current)
65
- return;
66
88
  const input = inputRef.current;
67
89
  const containerWindow = ownerWindow(input);
68
90
  const computedStyle = containerWindow.getComputedStyle(input);
69
91
  // If input's width is shrunk and it's not visible, don't sync height.
70
92
  if (computedStyle.width === "0px") {
71
- return;
93
+ return { outerHeightStyle: 0 };
72
94
  }
73
95
  const inputShallow = shadowRef.current;
74
96
  inputShallow.style.width = computedStyle.width;
75
- inputShallow.value = input.value || (other === null || other === void 0 ? void 0 : other.placeholder) || "x";
97
+ inputShallow.value = input.value || other.placeholder || "x";
76
98
  if (inputShallow.value.slice(-1) === "\n") {
77
99
  // Certain fonts which overflow the line height will cause the textarea
78
100
  // to report a different scrollHeight depending on whether the last line
79
101
  // is empty. Make it non-empty to avoid this issue.
80
102
  inputShallow.value += " ";
81
103
  }
82
- const boxSizing = computedStyle["box-sizing"];
83
- const padding = getStyleValue(computedStyle, "padding-bottom") +
84
- getStyleValue(computedStyle, "padding-top");
85
- const border = getStyleValue(computedStyle, "border-bottom-width") +
86
- getStyleValue(computedStyle, "border-top-width");
104
+ const boxSizing = computedStyle.boxSizing;
105
+ const padding = getStyleValue(computedStyle.paddingBottom) +
106
+ getStyleValue(computedStyle.paddingTop);
107
+ const border = getStyleValue(computedStyle.borderBottomWidth) +
108
+ getStyleValue(computedStyle.borderTopWidth);
87
109
  // The height of the inner content
88
110
  const innerHeight = inputShallow.scrollHeight - padding;
89
111
  // Measure height of a textarea with a single row
@@ -102,77 +124,50 @@ const TextareaAutosize = (0, react_1.forwardRef)((_a, ref) => {
102
124
  const outerHeightStyle = outerHeight + (boxSizing === "border-box" ? padding + border : 0);
103
125
  const overflow = Math.abs(outerHeight - innerHeight) <= 1;
104
126
  return { outerHeightStyle, overflow };
105
- }, [maxRows, minRows, other === null || other === void 0 ? void 0 : other.placeholder]);
127
+ }, [maxRows, minRows, other.placeholder]);
106
128
  const syncHeight = react_1.default.useCallback(() => {
107
129
  const newState = getUpdatedState();
108
130
  if (isEmpty(newState)) {
109
131
  return;
110
132
  }
111
- setState((prevState) => {
112
- return updateState(prevState, newState);
113
- });
133
+ setState((prevState) => updateState(prevState, newState, renders));
114
134
  }, [getUpdatedState]);
115
- const updateState = (prevState, newState) => {
116
- const { outerHeightStyle, overflow } = newState;
117
- // Need a large enough difference to update the height.
118
- // This prevents infinite rendering loop.
119
- if (renders.current < 20 &&
120
- ((outerHeightStyle > 0 &&
121
- Math.abs((prevState.outerHeightStyle || 0) - outerHeightStyle) > 1) ||
122
- prevState.overflow !== overflow)) {
123
- renders.current += 1;
124
- return {
125
- overflow,
126
- outerHeightStyle,
127
- };
128
- }
129
- if (process.env.NODE_ENV !== "production") {
130
- if (renders.current === 20) {
131
- console.error([
132
- "Textarea: Too many re-renders. The layout is unstable.",
133
- "TextareaAutosize limits the number of renders to prevent an infinite loop.",
134
- ].join("\n"));
135
+ (0, util_1.useClientLayoutEffect)(() => {
136
+ const syncHeightWithFlushSync = () => {
137
+ const newState = getUpdatedState();
138
+ if (isEmpty(newState)) {
139
+ return;
135
140
  }
136
- }
137
- return prevState;
138
- };
139
- const withFlushSync = () => {
140
- const newState = getUpdatedState();
141
- if (isEmpty(newState)) {
142
- return;
143
- }
144
- // In React 18, state updates in a ResizeObserver's callback are happening after the paint which causes flickering
145
- // when doing some visual updates in it. Using flushSync ensures that the dom will be painted after the states updates happen
146
- // Related issue - https://github.com/facebook/react/issues/24331
147
- react_dom_1.default.flushSync(() => {
148
- setState((prevState) => {
149
- return updateState(prevState, newState);
141
+ // In React 18, state updates in a ResizeObserver's callback are happening after
142
+ // the paint, this leads to an infinite rendering.
143
+ //
144
+ // Using flushSync ensures that the states is updated before the next pain.
145
+ // Related issue - https://github.com/facebook/react/issues/24331
146
+ react_dom_1.default.flushSync(() => {
147
+ setState((prevState) => updateState(prevState, newState, renders));
150
148
  });
151
- });
152
- };
153
- react_1.default.useEffect(() => {
154
- const handleResize = (0, util_1.debounce)(() => {
149
+ };
150
+ const handleResize = () => {
155
151
  renders.current = 0;
156
- if (inputRef.current) {
157
- withFlushSync();
158
- }
159
- });
160
- let resizeObserver;
152
+ syncHeightWithFlushSync();
153
+ };
154
+ const debounceHandleResize = (0, util_1.debounce)(handleResize);
161
155
  const input = inputRef.current;
162
156
  const containerWindow = ownerWindow(input);
163
- containerWindow.addEventListener("resize", handleResize);
157
+ containerWindow.addEventListener("resize", debounceHandleResize);
158
+ let resizeObserver;
164
159
  if (typeof ResizeObserver !== "undefined") {
165
160
  resizeObserver = new ResizeObserver(handleResize);
166
161
  resizeObserver.observe(input);
167
162
  }
168
163
  return () => {
169
- handleResize.clear();
170
- containerWindow.removeEventListener("resize", handleResize);
164
+ debounceHandleResize.clear();
165
+ containerWindow.removeEventListener("resize", debounceHandleResize);
171
166
  if (resizeObserver) {
172
167
  resizeObserver.disconnect();
173
168
  }
174
169
  };
175
- });
170
+ }, [getUpdatedState]);
176
171
  (0, util_1.useClientLayoutEffect)(() => {
177
172
  syncHeight();
178
173
  });
@@ -191,7 +186,10 @@ const TextareaAutosize = (0, react_1.forwardRef)((_a, ref) => {
191
186
  return (react_1.default.createElement(react_1.default.Fragment, null,
192
187
  react_1.default.createElement("textarea", Object.assign({ value: value, onChange: handleChange, ref: handleRef,
193
188
  // Apply the rows prop to get a "correct" first SSR paint
194
- rows: minRows, style: Object.assign(Object.assign({ height: state.outerHeightStyle }, (state.overflow ? { overflow: "hidden" } : {})), style) }, other, { className: className })),
189
+ rows: minRows, style: Object.assign({ height: state.outerHeightStyle,
190
+ // Need a large enough difference to allow scrolling.
191
+ // This prevents infinite rendering loop.
192
+ overflow: state.overflow && !autoScrollbar ? "hidden" : undefined }, style) }, other, { className: className })),
195
193
  react_1.default.createElement("textarea", { "aria-hidden": true, className: className, readOnly: true, ref: shadowRef, tabIndex: -1, style: Object.assign({
196
194
  // Visibility needed to hide the extra text area on iPads
197
195
  visibility: "hidden",
@@ -206,6 +204,6 @@ function isEmpty(obj) {
206
204
  return (obj === undefined ||
207
205
  obj === null ||
208
206
  Object.keys(obj).length === 0 ||
209
- ((obj === null || obj === void 0 ? void 0 : obj.outerHeightStyle) === 0 && !(obj === null || obj === void 0 ? void 0 : obj.overflow)));
207
+ (obj.outerHeightStyle === 0 && !obj.overflow));
210
208
  }
211
209
  exports.default = TextareaAutosize;
@@ -36,7 +36,13 @@ export interface TextareaProps extends FormFieldProps, React.TextareaHTMLAttribu
36
36
  /**
37
37
  * Enables resizing of field
38
38
  */
39
- resize?: boolean;
39
+ resize?: boolean | "vertical" | "horizontal";
40
+ /**
41
+ * Textarea will stop growing and get a scrollbar when there's no more room to grow.
42
+ * Requires `display:flex` on the parent.
43
+ * Experimental feature that may be removed or get breaking changes in a minor version.
44
+ */
45
+ UNSAFE_autoScrollbar?: boolean;
40
46
  /**
41
47
  * i18n-translations for counter-text
42
48
  */
@@ -31,7 +31,7 @@ import { useFormField } from "./useFormField";
31
31
  export const Textarea = forwardRef((props, ref) => {
32
32
  var _a, _b, _c;
33
33
  const { inputProps, errorId, showErrorMsg, hasError, size, inputDescriptionId, } = useFormField(props, "textarea");
34
- const { label, className, description, maxLength, hideLabel = false, resize, i18n, readOnly } = props, rest = __rest(props, ["label", "className", "description", "maxLength", "hideLabel", "resize", "i18n", "readOnly"]);
34
+ const { label, className, description, maxLength, hideLabel = false, resize, UNSAFE_autoScrollbar, i18n, readOnly } = props, rest = __rest(props, ["label", "className", "description", "maxLength", "hideLabel", "resize", "UNSAFE_autoScrollbar", "i18n", "readOnly"]);
35
35
  const maxLengthId = useId();
36
36
  const hasMaxLength = maxLength !== undefined && maxLength > 0;
37
37
  const [controlledValue, setControlledValue] = useState((_a = props === null || props === void 0 ? void 0 : props.defaultValue) !== null && _a !== void 0 ? _a : "");
@@ -50,7 +50,8 @@ export const Textarea = forwardRef((props, ref) => {
50
50
  "navds-form-field--readonly": readOnly,
51
51
  "navds-textarea--readonly": readOnly,
52
52
  "navds-textarea--error": hasError,
53
- "navds-textarea--resize": resize,
53
+ "navds-textarea--autoscrollbar": UNSAFE_autoScrollbar,
54
+ [`navds-textarea--resize-${resize === true ? "both" : resize}`]: resize,
54
55
  }) },
55
56
  React.createElement(Label, { htmlFor: inputProps.id, size: size, className: cl("navds-form-field__label", {
56
57
  "navds-sr-only": hideLabel,
@@ -63,7 +64,7 @@ export const Textarea = forwardRef((props, ref) => {
63
64
  React.createElement("div", { className: "navds-textarea__wrapper" },
64
65
  React.createElement(TextareaAutosize, Object.assign({}, omit(rest, ["error", "errorId", "size"]), inputProps, { onChange: (e) => props.onChange
65
66
  ? props.onChange(e)
66
- : setControlledValue(e.target.value), minRows: getMinRows(), ref: ref, readOnly: readOnly, className: cl("navds-textarea__input", "navds-body-short", `navds-body-short--${size !== null && size !== void 0 ? size : "medium"}`) }, (describedBy ? { "aria-describedby": describedBy } : {}))),
67
+ : setControlledValue(e.target.value), minRows: getMinRows(), autoScrollbar: UNSAFE_autoScrollbar, ref: ref, readOnly: readOnly, className: cl("navds-textarea__input", "navds-body-short", `navds-body-short--${size !== null && size !== void 0 ? size : "medium"}`) }, (describedBy ? { "aria-describedby": describedBy } : {}))),
67
68
  hasMaxLength && !readOnly && !inputProps.disabled && (React.createElement(React.Fragment, null,
68
69
  React.createElement("span", { id: maxLengthId, className: "navds-sr-only" }, `Tekstområde med plass til ${maxLength} tegn.`),
69
70
  React.createElement(Counter, { maxLength: maxLength, currentLength: (_c = (_b = props.value) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : controlledValue === null || controlledValue === void 0 ? void 0 : controlledValue.length, size: size, i18n: i18n })))),
@@ -1 +1 @@
1
- {"version":3,"file":"Textarea.js","sourceRoot":"","sources":["../../src/form/Textarea.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Textarea.js","sourceRoot":"","sources":["../../src/form/Textarea.tsx"],"names":[],"mappings":"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"}
@@ -1,14 +1,30 @@
1
1
  import cl from "clsx";
2
- import React from "react";
2
+ import React, { useEffect, useState } from "react";
3
3
  import { BodyShort } from "../typography";
4
+ import debounce from "../util/debounce";
4
5
  const TextareaCounter = ({ maxLength, currentLength, size, i18n }) => {
5
- var _a, _b;
6
6
  const difference = maxLength - currentLength;
7
- return (React.createElement(BodyShort, { className: cl("navds-textarea__counter", {
8
- "navds-textarea__counter--error": difference < 0,
9
- }), role: difference < 20 ? "status" : undefined, size: size }, difference < 0
7
+ const [debouncedDiff, setDebouncedDiff] = useState(difference);
8
+ useEffect(() => {
9
+ const debounceFunc = debounce(() => {
10
+ setDebouncedDiff(difference);
11
+ }, 2000);
12
+ debounceFunc();
13
+ return () => {
14
+ debounceFunc.clear();
15
+ };
16
+ }, [difference]);
17
+ return (React.createElement(React.Fragment, null,
18
+ difference < 20 && (React.createElement("span", { role: "status", className: "navds-textarea__sr-counter navds-sr-only" }, getCounterText(debouncedDiff, i18n))),
19
+ React.createElement(BodyShort, { className: cl("navds-textarea__counter", {
20
+ "navds-textarea__counter--error": difference < 0,
21
+ }), size: size }, getCounterText(difference, i18n))));
22
+ };
23
+ const getCounterText = (difference, i18n) => {
24
+ var _a, _b;
25
+ return difference < 0
10
26
  ? `${Math.abs(difference)} ${(_a = i18n === null || i18n === void 0 ? void 0 : i18n.counterTooMuch) !== null && _a !== void 0 ? _a : "tegn for mye"}`
11
- : `${difference} ${(_b = i18n === null || i18n === void 0 ? void 0 : i18n.counterLeft) !== null && _b !== void 0 ? _b : "tegn igjen"}`));
27
+ : `${difference} ${(_b = i18n === null || i18n === void 0 ? void 0 : i18n.counterLeft) !== null && _b !== void 0 ? _b : "tegn igjen"}`;
12
28
  };
13
29
  export default TextareaCounter;
14
30
  //# sourceMappingURL=TextareaCounter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TextareaCounter.js","sourceRoot":"","sources":["../../src/form/TextareaCounter.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,MAAM,CAAC;AACtB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAU1C,MAAM,eAAe,GAAG,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAS,EAAE,EAAE;;IAC1E,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,CAAC;IAE7C,OAAO,CACL,oBAAC,SAAS,IACR,SAAS,EAAE,EAAE,CAAC,yBAAyB,EAAE;YACvC,gCAAgC,EAAE,UAAU,GAAG,CAAC;SACjD,CAAC,EACF,IAAI,EAAE,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAC5C,IAAI,EAAE,IAAI,IAET,UAAU,GAAG,CAAC;QACb,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,cAAc,mCAAI,cAAc,EAAE;QACrE,CAAC,CAAC,GAAG,UAAU,IAAI,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,mCAAI,YAAY,EAAE,CAC9C,CACb,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"TextareaCounter.js","sourceRoot":"","sources":["../../src/form/TextareaCounter.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,MAAM,CAAC;AACtB,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,QAAQ,MAAM,kBAAkB,CAAC;AAUxC,MAAM,eAAe,GAAG,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAS,EAAE,EAAE;IAC1E,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,CAAC;IAE7C,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IAE/D,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,EAAE;YACjC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAC/B,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,YAAY,EAAE,CAAC;QAEf,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,OAAO,CACL;QACG,UAAU,GAAG,EAAE,IAAI,CAClB,8BACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,0CAA0C,IAEnD,cAAc,CAAC,aAAa,EAAE,IAAI,CAAC,CAC/B,CACR;QAED,oBAAC,SAAS,IACR,SAAS,EAAE,EAAE,CAAC,yBAAyB,EAAE;gBACvC,gCAAgC,EAAE,UAAU,GAAG,CAAC;aACjD,CAAC,EACF,IAAI,EAAE,IAAI,IAET,cAAc,CAAC,UAAU,EAAE,IAAI,CAAC,CACvB,CACX,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,UAAkB,EAAE,IAA2B,EAAE,EAAE;;IACzE,OAAA,UAAU,GAAG,CAAC;QACZ,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,cAAc,mCAAI,cAAc,EAAE;QACrE,CAAC,CAAC,GAAG,UAAU,IAAI,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,mCAAI,YAAY,EAAE,CAAA;CAAA,CAAC;AAE3D,eAAe,eAAe,CAAC"}
@@ -9,6 +9,10 @@ interface TextareaAutosizeProps extends Omit<React.TextareaHTMLAttributes<HTMLTe
9
9
  * @default 1
10
10
  */
11
11
  minRows?: number;
12
+ /**
13
+ * If true, textarea will never get `overflow:hidden`
14
+ */
15
+ autoScrollbar?: boolean;
12
16
  }
13
17
  declare const TextareaAutosize: React.ForwardRefExoticComponent<TextareaAutosizeProps & React.RefAttributes<HTMLTextAreaElement>>;
14
18
  export default TextareaAutosize;
@@ -9,53 +9,75 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- /* https://github.com/mui/material-ui/blob/master/packages/mui-base/src/TextareaAutosize/TextareaAutosize.js */
12
+ /* https://github.com/mui/material-ui/blob/master/packages/mui-base/src/TextareaAutosize/TextareaAutosize.tsx */
13
13
  import React, { forwardRef, useEffect, useMemo, useRef, useState } from "react";
14
14
  import ReactDOM from "react-dom";
15
15
  import { debounce, mergeRefs, useClientLayoutEffect } from "../util";
16
+ const updateState = (prevState, newState, renders) => {
17
+ const { outerHeightStyle, overflow } = newState;
18
+ // Need a large enough difference to update the height.
19
+ // This prevents infinite rendering loop.
20
+ if (renders.current < 20 &&
21
+ ((outerHeightStyle > 0 &&
22
+ Math.abs((prevState.outerHeightStyle || 0) - outerHeightStyle) > 1) ||
23
+ prevState.overflow !== overflow)) {
24
+ renders.current += 1;
25
+ return {
26
+ overflow,
27
+ outerHeightStyle,
28
+ };
29
+ }
30
+ if (process.env.NODE_ENV !== "production") {
31
+ if (renders.current === 20) {
32
+ console.error([
33
+ "Textarea: Too many re-renders. The layout is unstable.",
34
+ "TextareaAutosize limits the number of renders to prevent an infinite loop.",
35
+ ].join("\n"));
36
+ }
37
+ }
38
+ return prevState;
39
+ };
16
40
  /**
17
- * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerDocument.ts
18
- * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerWindow.ts
41
+ * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerDocument/ownerDocument.ts
42
+ * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerWindow/ownerWindow.ts
19
43
  */
20
44
  const ownerWindow = (node) => {
21
45
  const doc = (node && node.ownerDocument) || document;
22
46
  return doc.defaultView || window;
23
47
  };
24
- function getStyleValue(computedStyle, property) {
25
- return parseInt(computedStyle[property], 10) || 0;
48
+ function getStyleValue(value) {
49
+ return parseInt(value, 10) || 0;
26
50
  }
27
51
  const TextareaAutosize = forwardRef((_a, ref) => {
28
- var { className, onChange, maxRows, minRows = 1, style, value } = _a, other = __rest(_a, ["className", "onChange", "maxRows", "minRows", "style", "value"]);
52
+ var { className, onChange, maxRows, minRows = 1, autoScrollbar, style, value } = _a, other = __rest(_a, ["className", "onChange", "maxRows", "minRows", "autoScrollbar", "style", "value"]);
29
53
  const { current: isControlled } = useRef(value != null);
30
54
  const inputRef = useRef(null);
31
55
  const handleRef = useMemo(() => mergeRefs([inputRef, ref]), [ref]);
32
56
  const shadowRef = useRef(null);
33
57
  const renders = useRef(0);
34
- const [state, setState] = useState({});
58
+ const [state, setState] = useState({ outerHeightStyle: 0 });
35
59
  const getUpdatedState = React.useCallback(() => {
36
- if (!inputRef.current || !shadowRef.current)
37
- return;
38
60
  const input = inputRef.current;
39
61
  const containerWindow = ownerWindow(input);
40
62
  const computedStyle = containerWindow.getComputedStyle(input);
41
63
  // If input's width is shrunk and it's not visible, don't sync height.
42
64
  if (computedStyle.width === "0px") {
43
- return;
65
+ return { outerHeightStyle: 0 };
44
66
  }
45
67
  const inputShallow = shadowRef.current;
46
68
  inputShallow.style.width = computedStyle.width;
47
- inputShallow.value = input.value || (other === null || other === void 0 ? void 0 : other.placeholder) || "x";
69
+ inputShallow.value = input.value || other.placeholder || "x";
48
70
  if (inputShallow.value.slice(-1) === "\n") {
49
71
  // Certain fonts which overflow the line height will cause the textarea
50
72
  // to report a different scrollHeight depending on whether the last line
51
73
  // is empty. Make it non-empty to avoid this issue.
52
74
  inputShallow.value += " ";
53
75
  }
54
- const boxSizing = computedStyle["box-sizing"];
55
- const padding = getStyleValue(computedStyle, "padding-bottom") +
56
- getStyleValue(computedStyle, "padding-top");
57
- const border = getStyleValue(computedStyle, "border-bottom-width") +
58
- getStyleValue(computedStyle, "border-top-width");
76
+ const boxSizing = computedStyle.boxSizing;
77
+ const padding = getStyleValue(computedStyle.paddingBottom) +
78
+ getStyleValue(computedStyle.paddingTop);
79
+ const border = getStyleValue(computedStyle.borderBottomWidth) +
80
+ getStyleValue(computedStyle.borderTopWidth);
59
81
  // The height of the inner content
60
82
  const innerHeight = inputShallow.scrollHeight - padding;
61
83
  // Measure height of a textarea with a single row
@@ -74,77 +96,50 @@ const TextareaAutosize = forwardRef((_a, ref) => {
74
96
  const outerHeightStyle = outerHeight + (boxSizing === "border-box" ? padding + border : 0);
75
97
  const overflow = Math.abs(outerHeight - innerHeight) <= 1;
76
98
  return { outerHeightStyle, overflow };
77
- }, [maxRows, minRows, other === null || other === void 0 ? void 0 : other.placeholder]);
99
+ }, [maxRows, minRows, other.placeholder]);
78
100
  const syncHeight = React.useCallback(() => {
79
101
  const newState = getUpdatedState();
80
102
  if (isEmpty(newState)) {
81
103
  return;
82
104
  }
83
- setState((prevState) => {
84
- return updateState(prevState, newState);
85
- });
105
+ setState((prevState) => updateState(prevState, newState, renders));
86
106
  }, [getUpdatedState]);
87
- const updateState = (prevState, newState) => {
88
- const { outerHeightStyle, overflow } = newState;
89
- // Need a large enough difference to update the height.
90
- // This prevents infinite rendering loop.
91
- if (renders.current < 20 &&
92
- ((outerHeightStyle > 0 &&
93
- Math.abs((prevState.outerHeightStyle || 0) - outerHeightStyle) > 1) ||
94
- prevState.overflow !== overflow)) {
95
- renders.current += 1;
96
- return {
97
- overflow,
98
- outerHeightStyle,
99
- };
100
- }
101
- if (process.env.NODE_ENV !== "production") {
102
- if (renders.current === 20) {
103
- console.error([
104
- "Textarea: Too many re-renders. The layout is unstable.",
105
- "TextareaAutosize limits the number of renders to prevent an infinite loop.",
106
- ].join("\n"));
107
+ useClientLayoutEffect(() => {
108
+ const syncHeightWithFlushSync = () => {
109
+ const newState = getUpdatedState();
110
+ if (isEmpty(newState)) {
111
+ return;
107
112
  }
108
- }
109
- return prevState;
110
- };
111
- const withFlushSync = () => {
112
- const newState = getUpdatedState();
113
- if (isEmpty(newState)) {
114
- return;
115
- }
116
- // In React 18, state updates in a ResizeObserver's callback are happening after the paint which causes flickering
117
- // when doing some visual updates in it. Using flushSync ensures that the dom will be painted after the states updates happen
118
- // Related issue - https://github.com/facebook/react/issues/24331
119
- ReactDOM.flushSync(() => {
120
- setState((prevState) => {
121
- return updateState(prevState, newState);
113
+ // In React 18, state updates in a ResizeObserver's callback are happening after
114
+ // the paint, this leads to an infinite rendering.
115
+ //
116
+ // Using flushSync ensures that the states is updated before the next pain.
117
+ // Related issue - https://github.com/facebook/react/issues/24331
118
+ ReactDOM.flushSync(() => {
119
+ setState((prevState) => updateState(prevState, newState, renders));
122
120
  });
123
- });
124
- };
125
- React.useEffect(() => {
126
- const handleResize = debounce(() => {
121
+ };
122
+ const handleResize = () => {
127
123
  renders.current = 0;
128
- if (inputRef.current) {
129
- withFlushSync();
130
- }
131
- });
132
- let resizeObserver;
124
+ syncHeightWithFlushSync();
125
+ };
126
+ const debounceHandleResize = debounce(handleResize);
133
127
  const input = inputRef.current;
134
128
  const containerWindow = ownerWindow(input);
135
- containerWindow.addEventListener("resize", handleResize);
129
+ containerWindow.addEventListener("resize", debounceHandleResize);
130
+ let resizeObserver;
136
131
  if (typeof ResizeObserver !== "undefined") {
137
132
  resizeObserver = new ResizeObserver(handleResize);
138
133
  resizeObserver.observe(input);
139
134
  }
140
135
  return () => {
141
- handleResize.clear();
142
- containerWindow.removeEventListener("resize", handleResize);
136
+ debounceHandleResize.clear();
137
+ containerWindow.removeEventListener("resize", debounceHandleResize);
143
138
  if (resizeObserver) {
144
139
  resizeObserver.disconnect();
145
140
  }
146
141
  };
147
- });
142
+ }, [getUpdatedState]);
148
143
  useClientLayoutEffect(() => {
149
144
  syncHeight();
150
145
  });
@@ -163,7 +158,10 @@ const TextareaAutosize = forwardRef((_a, ref) => {
163
158
  return (React.createElement(React.Fragment, null,
164
159
  React.createElement("textarea", Object.assign({ value: value, onChange: handleChange, ref: handleRef,
165
160
  // Apply the rows prop to get a "correct" first SSR paint
166
- rows: minRows, style: Object.assign(Object.assign({ height: state.outerHeightStyle }, (state.overflow ? { overflow: "hidden" } : {})), style) }, other, { className: className })),
161
+ rows: minRows, style: Object.assign({ height: state.outerHeightStyle,
162
+ // Need a large enough difference to allow scrolling.
163
+ // This prevents infinite rendering loop.
164
+ overflow: state.overflow && !autoScrollbar ? "hidden" : undefined }, style) }, other, { className: className })),
167
165
  React.createElement("textarea", { "aria-hidden": true, className: className, readOnly: true, ref: shadowRef, tabIndex: -1, style: Object.assign({
168
166
  // Visibility needed to hide the extra text area on iPads
169
167
  visibility: "hidden",
@@ -178,7 +176,7 @@ function isEmpty(obj) {
178
176
  return (obj === undefined ||
179
177
  obj === null ||
180
178
  Object.keys(obj).length === 0 ||
181
- ((obj === null || obj === void 0 ? void 0 : obj.outerHeightStyle) === 0 && !(obj === null || obj === void 0 ? void 0 : obj.overflow)));
179
+ (obj.outerHeightStyle === 0 && !obj.overflow));
182
180
  }
183
181
  export default TextareaAutosize;
184
182
  //# sourceMappingURL=TextareaAutoSize.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TextareaAutoSize.js","sourceRoot":"","sources":["../../src/util/TextareaAutoSize.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TextareaAutoSize.js","sourceRoot":"","sources":["../../src/util/TextareaAutoSize.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navikt/ds-react",
3
- "version": "5.10.4",
3
+ "version": "5.11.0",
4
4
  "description": "Aksel react-components for NAV designsystem",
5
5
  "author": "Aksel | NAV designsystem team",
6
6
  "license": "MIT",
@@ -38,8 +38,8 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@floating-ui/react": "0.25.4",
41
- "@navikt/aksel-icons": "^5.10.4",
42
- "@navikt/ds-tokens": "^5.10.4",
41
+ "@navikt/aksel-icons": "^5.11.0",
42
+ "@navikt/ds-tokens": "^5.11.0",
43
43
  "@radix-ui/react-tabs": "1.0.0",
44
44
  "@radix-ui/react-toggle-group": "1.0.0",
45
45
  "clsx": "^1.2.1",
@@ -45,7 +45,13 @@ export interface TextareaProps
45
45
  /**
46
46
  * Enables resizing of field
47
47
  */
48
- resize?: boolean;
48
+ resize?: boolean | "vertical" | "horizontal";
49
+ /**
50
+ * Textarea will stop growing and get a scrollbar when there's no more room to grow.
51
+ * Requires `display:flex` on the parent.
52
+ * Experimental feature that may be removed or get breaking changes in a minor version.
53
+ */
54
+ UNSAFE_autoScrollbar?: boolean;
49
55
  /**
50
56
  * i18n-translations for counter-text
51
57
  */
@@ -86,6 +92,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
86
92
  maxLength,
87
93
  hideLabel = false,
88
94
  resize,
95
+ UNSAFE_autoScrollbar,
89
96
  i18n,
90
97
  readOnly,
91
98
  ...rest
@@ -121,7 +128,9 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
121
128
  "navds-form-field--readonly": readOnly,
122
129
  "navds-textarea--readonly": readOnly,
123
130
  "navds-textarea--error": hasError,
124
- "navds-textarea--resize": resize,
131
+ "navds-textarea--autoscrollbar": UNSAFE_autoScrollbar,
132
+ [`navds-textarea--resize-${resize === true ? "both" : resize}`]:
133
+ resize,
125
134
  }
126
135
  )}
127
136
  >
@@ -157,6 +166,7 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
157
166
  : setControlledValue(e.target.value)
158
167
  }
159
168
  minRows={getMinRows()}
169
+ autoScrollbar={UNSAFE_autoScrollbar}
160
170
  ref={ref}
161
171
  readOnly={readOnly}
162
172
  className={cl(
@@ -1,6 +1,7 @@
1
1
  import cl from "clsx";
2
- import React from "react";
2
+ import React, { useEffect, useState } from "react";
3
3
  import { BodyShort } from "../typography";
4
+ import debounce from "../util/debounce";
4
5
  import type { TextareaProps } from "./Textarea";
5
6
 
6
7
  interface Props {
@@ -13,19 +14,45 @@ interface Props {
13
14
  const TextareaCounter = ({ maxLength, currentLength, size, i18n }: Props) => {
14
15
  const difference = maxLength - currentLength;
15
16
 
17
+ const [debouncedDiff, setDebouncedDiff] = useState(difference);
18
+
19
+ useEffect(() => {
20
+ const debounceFunc = debounce(() => {
21
+ setDebouncedDiff(difference);
22
+ }, 2000);
23
+ debounceFunc();
24
+
25
+ return () => {
26
+ debounceFunc.clear();
27
+ };
28
+ }, [difference]);
29
+
16
30
  return (
17
- <BodyShort
18
- className={cl("navds-textarea__counter", {
19
- "navds-textarea__counter--error": difference < 0,
20
- })}
21
- role={difference < 20 ? "status" : undefined}
22
- size={size}
23
- >
24
- {difference < 0
25
- ? `${Math.abs(difference)} ${i18n?.counterTooMuch ?? "tegn for mye"}`
26
- : `${difference} ${i18n?.counterLeft ?? "tegn igjen"}`}
27
- </BodyShort>
31
+ <>
32
+ {difference < 20 && (
33
+ <span
34
+ role="status"
35
+ className="navds-textarea__sr-counter navds-sr-only"
36
+ >
37
+ {getCounterText(debouncedDiff, i18n)}
38
+ </span>
39
+ )}
40
+
41
+ <BodyShort
42
+ className={cl("navds-textarea__counter", {
43
+ "navds-textarea__counter--error": difference < 0,
44
+ })}
45
+ size={size}
46
+ >
47
+ {getCounterText(difference, i18n)}
48
+ </BodyShort>
49
+ </>
28
50
  );
29
51
  };
30
52
 
53
+ const getCounterText = (difference: number, i18n: TextareaProps["i18n"]) =>
54
+ difference < 0
55
+ ? `${Math.abs(difference)} ${i18n?.counterTooMuch ?? "tegn for mye"}`
56
+ : `${difference} ${i18n?.counterLeft ?? "tegn igjen"}`;
57
+
31
58
  export default TextareaCounter;
@@ -1,5 +1,6 @@
1
1
  import { Meta, StoryObj } from "@storybook/react";
2
2
  import React from "react";
3
+ import { Button } from "../../button";
3
4
  import { Textarea } from "../index";
4
5
 
5
6
  const meta: Meta<typeof Textarea> = {
@@ -16,10 +17,13 @@ export const Default: StoryObj<typeof Textarea> = {
16
17
  args: {
17
18
  maxLength: 0,
18
19
  label: "Ipsum enim quis culpa",
19
- resize: false,
20
20
  },
21
21
 
22
22
  argTypes: {
23
+ resize: {
24
+ control: { type: "radio" },
25
+ options: [true, "vertical", "horizontal"],
26
+ },
23
27
  size: {
24
28
  control: { type: "radio" },
25
29
  options: ["medium", "small"],
@@ -99,7 +103,7 @@ export const HideLabel = () => {
99
103
  };
100
104
 
101
105
  export const MaxLength = () => {
102
- return <Textarea maxLength={200} label="Ipsum enim quis culpa" />;
106
+ return <Textarea maxLength={50} label="Ipsum enim quis culpa" />;
103
107
  };
104
108
 
105
109
  export const MinRows = () => {
@@ -138,3 +142,35 @@ export const Readonly = () => {
138
142
  </div>
139
143
  );
140
144
  };
145
+
146
+ export const AutoScrollbar = (props) => (
147
+ <div
148
+ style={{
149
+ border: "1px solid lightGreen",
150
+ height: "90vh",
151
+ display: "flex",
152
+ flexDirection: "column",
153
+ }}
154
+ >
155
+ <div style={{ border: "1px dashed gray" }}>
156
+ <h1>Header</h1>
157
+ </div>
158
+ <Textarea
159
+ resize
160
+ label="Textarea with autoScrollbar"
161
+ description="Description"
162
+ maxLength={30}
163
+ UNSAFE_autoScrollbar
164
+ {...props}
165
+ />
166
+ <div style={{ border: "1px dashed gray" }}>
167
+ <Button>Send</Button>
168
+ </div>
169
+ </div>
170
+ );
171
+ AutoScrollbar.argTypes = {
172
+ error: { type: "string" },
173
+ hideLabel: { type: "boolean" },
174
+ maxRows: { type: "number" },
175
+ minRows: { type: "number" },
176
+ };
@@ -1,19 +1,57 @@
1
- /* https://github.com/mui/material-ui/blob/master/packages/mui-base/src/TextareaAutosize/TextareaAutosize.js */
1
+ /* https://github.com/mui/material-ui/blob/master/packages/mui-base/src/TextareaAutosize/TextareaAutosize.tsx */
2
2
  import React, { forwardRef, useEffect, useMemo, useRef, useState } from "react";
3
3
  import ReactDOM from "react-dom";
4
4
  import { debounce, mergeRefs, useClientLayoutEffect } from "../util";
5
5
 
6
+ type State = {
7
+ outerHeightStyle: number;
8
+ overflow?: boolean | undefined;
9
+ };
10
+
11
+ const updateState = (
12
+ prevState: State,
13
+ newState: State,
14
+ renders: React.MutableRefObject<number>
15
+ ) => {
16
+ const { outerHeightStyle, overflow } = newState;
17
+ // Need a large enough difference to update the height.
18
+ // This prevents infinite rendering loop.
19
+ if (
20
+ renders.current < 20 &&
21
+ ((outerHeightStyle > 0 &&
22
+ Math.abs((prevState.outerHeightStyle || 0) - outerHeightStyle) > 1) ||
23
+ prevState.overflow !== overflow)
24
+ ) {
25
+ renders.current += 1;
26
+ return {
27
+ overflow,
28
+ outerHeightStyle,
29
+ };
30
+ }
31
+ if (process.env.NODE_ENV !== "production") {
32
+ if (renders.current === 20) {
33
+ console.error(
34
+ [
35
+ "Textarea: Too many re-renders. The layout is unstable.",
36
+ "TextareaAutosize limits the number of renders to prevent an infinite loop.",
37
+ ].join("\n")
38
+ );
39
+ }
40
+ }
41
+ return prevState;
42
+ };
43
+
6
44
  /**
7
- * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerDocument.ts
8
- * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerWindow.ts
45
+ * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerDocument/ownerDocument.ts
46
+ * https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/ownerWindow/ownerWindow.ts
9
47
  */
10
- const ownerWindow = (node: Node | null): Window => {
48
+ const ownerWindow = (node: Node | undefined): Window => {
11
49
  const doc = (node && node.ownerDocument) || document;
12
50
  return doc.defaultView || window;
13
51
  };
14
52
 
15
- function getStyleValue(computedStyle, property) {
16
- return parseInt(computedStyle[property], 10) || 0;
53
+ function getStyleValue(value: string) {
54
+ return parseInt(value, 10) || 0;
17
55
  }
18
56
 
19
57
  interface TextareaAutosizeProps
@@ -30,34 +68,46 @@ interface TextareaAutosizeProps
30
68
  * @default 1
31
69
  */
32
70
  minRows?: number;
71
+ /**
72
+ * If true, textarea will never get `overflow:hidden`
73
+ */
74
+ autoScrollbar?: boolean;
33
75
  }
34
76
 
35
77
  const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
36
78
  (
37
- { className, onChange, maxRows, minRows = 1, style, value, ...other },
79
+ {
80
+ className,
81
+ onChange,
82
+ maxRows,
83
+ minRows = 1,
84
+ autoScrollbar,
85
+ style,
86
+ value,
87
+ ...other
88
+ }: TextareaAutosizeProps,
38
89
  ref
39
90
  ) => {
40
91
  const { current: isControlled } = useRef(value != null);
41
- const inputRef = useRef<HTMLTextAreaElement | null>(null);
92
+ const inputRef = useRef<HTMLTextAreaElement>(null);
42
93
  const handleRef = useMemo(() => mergeRefs([inputRef, ref]), [ref]);
43
- const shadowRef = useRef<HTMLTextAreaElement | null>(null);
94
+ const shadowRef = useRef<HTMLTextAreaElement>(null);
44
95
  const renders = useRef(0);
45
- const [state, setState] = useState<any>({});
96
+ const [state, setState] = useState<State>({ outerHeightStyle: 0 });
46
97
 
47
98
  const getUpdatedState = React.useCallback(() => {
48
- if (!inputRef.current || !shadowRef.current) return;
49
- const input = inputRef.current;
99
+ const input = inputRef.current!;
50
100
  const containerWindow = ownerWindow(input);
51
101
  const computedStyle = containerWindow.getComputedStyle(input);
52
102
 
53
103
  // If input's width is shrunk and it's not visible, don't sync height.
54
104
  if (computedStyle.width === "0px") {
55
- return;
105
+ return { outerHeightStyle: 0 };
56
106
  }
57
107
 
58
- const inputShallow = shadowRef.current;
108
+ const inputShallow = shadowRef.current!;
59
109
  inputShallow.style.width = computedStyle.width;
60
- inputShallow.value = input.value || other?.placeholder || "x";
110
+ inputShallow.value = input.value || other.placeholder || "x";
61
111
  if (inputShallow.value.slice(-1) === "\n") {
62
112
  // Certain fonts which overflow the line height will cause the textarea
63
113
  // to report a different scrollHeight depending on whether the last line
@@ -65,13 +115,13 @@ const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
65
115
  inputShallow.value += " ";
66
116
  }
67
117
 
68
- const boxSizing = computedStyle["box-sizing"];
118
+ const boxSizing = computedStyle.boxSizing;
69
119
  const padding =
70
- getStyleValue(computedStyle, "padding-bottom") +
71
- getStyleValue(computedStyle, "padding-top");
120
+ getStyleValue(computedStyle.paddingBottom) +
121
+ getStyleValue(computedStyle.paddingTop);
72
122
  const border =
73
- getStyleValue(computedStyle, "border-bottom-width") +
74
- getStyleValue(computedStyle, "border-top-width");
123
+ getStyleValue(computedStyle.borderBottomWidth) +
124
+ getStyleValue(computedStyle.borderTopWidth);
75
125
 
76
126
  // The height of the inner content
77
127
  const innerHeight = inputShallow.scrollHeight - padding;
@@ -97,7 +147,7 @@ const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
97
147
  const overflow = Math.abs(outerHeight - innerHeight) <= 1;
98
148
 
99
149
  return { outerHeightStyle, overflow };
100
- }, [maxRows, minRows, other?.placeholder]);
150
+ }, [maxRows, minRows, other.placeholder]);
101
151
 
102
152
  const syncHeight = React.useCallback(() => {
103
153
  const newState = getUpdatedState();
@@ -106,85 +156,52 @@ const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
106
156
  return;
107
157
  }
108
158
 
109
- setState((prevState) => {
110
- return updateState(prevState, newState);
111
- });
159
+ setState((prevState) => updateState(prevState, newState, renders));
112
160
  }, [getUpdatedState]);
113
161
 
114
- const updateState = (prevState, newState) => {
115
- const { outerHeightStyle, overflow } = newState;
116
- // Need a large enough difference to update the height.
117
- // This prevents infinite rendering loop.
118
- if (
119
- renders.current < 20 &&
120
- ((outerHeightStyle > 0 &&
121
- Math.abs((prevState.outerHeightStyle || 0) - outerHeightStyle) > 1) ||
122
- prevState.overflow !== overflow)
123
- ) {
124
- renders.current += 1;
125
- return {
126
- overflow,
127
- outerHeightStyle,
128
- };
129
- }
130
- if (process.env.NODE_ENV !== "production") {
131
- if (renders.current === 20) {
132
- console.error(
133
- [
134
- "Textarea: Too many re-renders. The layout is unstable.",
135
- "TextareaAutosize limits the number of renders to prevent an infinite loop.",
136
- ].join("\n")
137
- );
138
- }
139
- }
140
- return prevState;
141
- };
142
-
143
- const withFlushSync = () => {
144
- const newState = getUpdatedState();
162
+ useClientLayoutEffect(() => {
163
+ const syncHeightWithFlushSync = () => {
164
+ const newState = getUpdatedState();
145
165
 
146
- if (isEmpty(newState)) {
147
- return;
148
- }
166
+ if (isEmpty(newState)) {
167
+ return;
168
+ }
149
169
 
150
- // In React 18, state updates in a ResizeObserver's callback are happening after the paint which causes flickering
151
- // when doing some visual updates in it. Using flushSync ensures that the dom will be painted after the states updates happen
152
- // Related issue - https://github.com/facebook/react/issues/24331
153
- ReactDOM.flushSync(() => {
154
- setState((prevState) => {
155
- return updateState(prevState, newState);
170
+ // In React 18, state updates in a ResizeObserver's callback are happening after
171
+ // the paint, this leads to an infinite rendering.
172
+ //
173
+ // Using flushSync ensures that the states is updated before the next pain.
174
+ // Related issue - https://github.com/facebook/react/issues/24331
175
+ ReactDOM.flushSync(() => {
176
+ setState((prevState) => updateState(prevState, newState, renders));
156
177
  });
157
- });
158
- };
178
+ };
159
179
 
160
- React.useEffect(() => {
161
- const handleResize = debounce(() => {
180
+ const handleResize = () => {
162
181
  renders.current = 0;
182
+ syncHeightWithFlushSync();
183
+ };
163
184
 
164
- if (inputRef.current) {
165
- withFlushSync();
166
- }
167
- });
168
- let resizeObserver: ResizeObserver;
169
-
185
+ const debounceHandleResize = debounce(handleResize);
170
186
  const input = inputRef.current!;
171
187
  const containerWindow = ownerWindow(input);
172
188
 
173
- containerWindow.addEventListener("resize", handleResize);
189
+ containerWindow.addEventListener("resize", debounceHandleResize);
174
190
 
191
+ let resizeObserver: ResizeObserver;
175
192
  if (typeof ResizeObserver !== "undefined") {
176
193
  resizeObserver = new ResizeObserver(handleResize);
177
194
  resizeObserver.observe(input);
178
195
  }
179
196
 
180
197
  return () => {
181
- handleResize.clear();
182
- containerWindow.removeEventListener("resize", handleResize);
198
+ debounceHandleResize.clear();
199
+ containerWindow.removeEventListener("resize", debounceHandleResize);
183
200
  if (resizeObserver) {
184
201
  resizeObserver.disconnect();
185
202
  }
186
203
  };
187
- });
204
+ }, [getUpdatedState]);
188
205
 
189
206
  useClientLayoutEffect(() => {
190
207
  syncHeight();
@@ -194,7 +211,7 @@ const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
194
211
  renders.current = 0;
195
212
  }, [value]);
196
213
 
197
- const handleChange = (event) => {
214
+ const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
198
215
  renders.current = 0;
199
216
 
200
217
  if (!isControlled) {
@@ -218,7 +235,7 @@ const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
218
235
  height: state.outerHeightStyle,
219
236
  // Need a large enough difference to allow scrolling.
220
237
  // This prevents infinite rendering loop.
221
- ...(state.overflow ? { overflow: "hidden" } : {}),
238
+ overflow: state.overflow && !autoScrollbar ? "hidden" : undefined,
222
239
  ...style,
223
240
  }}
224
241
  {...other}
@@ -250,12 +267,12 @@ const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
250
267
  }
251
268
  );
252
269
 
253
- function isEmpty(obj: any) {
270
+ function isEmpty(obj: State) {
254
271
  return (
255
272
  obj === undefined ||
256
273
  obj === null ||
257
274
  Object.keys(obj).length === 0 ||
258
- (obj?.outerHeightStyle === 0 && !obj?.overflow)
275
+ (obj.outerHeightStyle === 0 && !obj.overflow)
259
276
  );
260
277
  }
261
278