carbon-react 120.6.0 → 121.0.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/esm/__internal__/character-count/character-count.component.d.ts +4 -2
- package/esm/__internal__/character-count/character-count.component.js +16 -6
- package/esm/__internal__/character-count/character-count.style.d.ts +4 -1
- package/esm/__internal__/character-count/character-count.style.js +26 -2
- package/esm/components/date/date.component.d.ts +1 -1
- package/esm/components/grouped-character/grouped-character.component.js +0 -1
- package/esm/components/multi-action-button/multi-action-button.style.js +0 -5
- package/esm/components/number/number.component.js +0 -1
- package/esm/components/textarea/textarea.component.d.ts +0 -2
- package/esm/components/textarea/textarea.component.js +6 -11
- package/esm/components/textbox/textbox.component.d.ts +0 -2
- package/esm/components/textbox/textbox.component.js +7 -12
- package/esm/components/textbox/textbox.style.js +4 -0
- package/esm/hooks/__internal__/useCharacterCount/useCharacterCount.d.ts +1 -6
- package/esm/hooks/__internal__/useCharacterCount/useCharacterCount.js +24 -8
- package/esm/hooks/__internal__/useDebounce/index.d.ts +1 -0
- package/esm/hooks/__internal__/useDebounce/index.js +1 -0
- package/esm/hooks/__internal__/useDebounce/useDebounce.d.ts +4 -0
- package/esm/hooks/__internal__/useDebounce/useDebounce.js +19 -0
- package/esm/locales/en-gb.js +3 -3
- package/esm/locales/locale.d.ts +1 -1
- package/esm/locales/pl-pl.js +2 -2
- package/lib/__internal__/character-count/character-count.component.d.ts +4 -2
- package/lib/__internal__/character-count/character-count.component.js +16 -6
- package/lib/__internal__/character-count/character-count.style.d.ts +4 -1
- package/lib/__internal__/character-count/character-count.style.js +31 -5
- package/lib/components/date/date.component.d.ts +1 -1
- package/lib/components/grouped-character/grouped-character.component.js +0 -1
- package/lib/components/multi-action-button/multi-action-button.style.js +0 -5
- package/lib/components/number/number.component.js +0 -1
- package/lib/components/textarea/textarea.component.d.ts +0 -2
- package/lib/components/textarea/textarea.component.js +6 -11
- package/lib/components/textbox/textbox.component.d.ts +0 -2
- package/lib/components/textbox/textbox.component.js +7 -12
- package/lib/components/textbox/textbox.style.js +4 -0
- package/lib/hooks/__internal__/useCharacterCount/useCharacterCount.d.ts +1 -6
- package/lib/hooks/__internal__/useCharacterCount/useCharacterCount.js +23 -7
- package/lib/hooks/__internal__/useDebounce/index.d.ts +1 -0
- package/lib/hooks/__internal__/useDebounce/index.js +13 -0
- package/lib/hooks/__internal__/useDebounce/package.json +6 -0
- package/lib/hooks/__internal__/useDebounce/useDebounce.d.ts +4 -0
- package/lib/hooks/__internal__/useDebounce/useDebounce.js +26 -0
- package/lib/locales/en-gb.js +3 -3
- package/lib/locales/locale.d.ts +1 -1
- package/lib/locales/pl-pl.js +2 -2
- package/package.json +1 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
interface CharacterCountProps {
|
|
3
3
|
value: number;
|
|
4
|
+
debouncedValue?: number;
|
|
4
5
|
limit: number;
|
|
6
|
+
isDebouncedOverLimit?: boolean;
|
|
5
7
|
isOverLimit: boolean;
|
|
6
|
-
|
|
8
|
+
visuallyHiddenHintId?: string;
|
|
7
9
|
}
|
|
8
|
-
declare const CharacterCount: ({ value, limit, isOverLimit,
|
|
10
|
+
declare const CharacterCount: ({ value, debouncedValue, limit, isDebouncedOverLimit, isOverLimit, visuallyHiddenHintId, }: CharacterCountProps) => React.JSX.Element;
|
|
9
11
|
export default CharacterCount;
|
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
|
-
import StyledCharacterCount from "./character-count.style";
|
|
3
|
+
import { StyledCharacterCountWrapper, StyledCharacterCount, VisuallyHiddenCharacterCount, VisuallyHiddenHint } from "./character-count.style";
|
|
4
4
|
import useLocale from "../../hooks/__internal__/useLocale";
|
|
5
5
|
const CharacterCount = ({
|
|
6
6
|
value,
|
|
7
|
+
debouncedValue = value,
|
|
7
8
|
limit,
|
|
9
|
+
isDebouncedOverLimit,
|
|
8
10
|
isOverLimit,
|
|
9
|
-
|
|
11
|
+
visuallyHiddenHintId
|
|
10
12
|
}) => {
|
|
11
13
|
const limitMinusValue = +limit - +value;
|
|
12
14
|
const valueMinusLimit = +value - +limit;
|
|
15
|
+
const debouncedLimitMinusValue = +limit - +debouncedValue;
|
|
16
|
+
const debouncedValueMinusLimit = debouncedValue - +limit;
|
|
13
17
|
const l = useLocale();
|
|
14
18
|
const getFormatNumber = (rawValue, locale) => new Intl.NumberFormat(locale).format(rawValue);
|
|
15
|
-
return /*#__PURE__*/React.createElement(
|
|
16
|
-
"
|
|
19
|
+
return /*#__PURE__*/React.createElement(StyledCharacterCountWrapper, null, /*#__PURE__*/React.createElement(VisuallyHiddenHint, {
|
|
20
|
+
"data-element": "visually-hidden-hint",
|
|
21
|
+
id: visuallyHiddenHintId
|
|
22
|
+
}, l.characterCount.visuallyHiddenHint(getFormatNumber(limit, l.locale()))), /*#__PURE__*/React.createElement(StyledCharacterCount, {
|
|
23
|
+
"aria-hidden": "true",
|
|
17
24
|
isOverLimit: isOverLimit,
|
|
18
|
-
"data-element":
|
|
19
|
-
}, !isOverLimit ? l.characterCount.charactersLeft(limitMinusValue, getFormatNumber(limitMinusValue, l.locale())) : l.characterCount.tooManyCharacters(valueMinusLimit, getFormatNumber(valueMinusLimit, l.locale())))
|
|
25
|
+
"data-element": "character-count"
|
|
26
|
+
}, !isOverLimit ? l.characterCount.charactersLeft(limitMinusValue, getFormatNumber(limitMinusValue, l.locale())) : l.characterCount.tooManyCharacters(valueMinusLimit, getFormatNumber(valueMinusLimit, l.locale()))), /*#__PURE__*/React.createElement(VisuallyHiddenCharacterCount, {
|
|
27
|
+
"data-element": "visually-hidden-character-count",
|
|
28
|
+
"aria-live": "polite"
|
|
29
|
+
}, !isDebouncedOverLimit ? l.characterCount.charactersLeft(debouncedLimitMinusValue, getFormatNumber(debouncedLimitMinusValue, l.locale())) : l.characterCount.tooManyCharacters(debouncedValueMinusLimit, getFormatNumber(debouncedValueMinusLimit, l.locale()))));
|
|
20
30
|
};
|
|
21
31
|
export default CharacterCount;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
declare const StyledCharacterCountWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
1
2
|
declare const StyledCharacterCount: import("styled-components").StyledComponent<"div", any, {
|
|
2
3
|
isOverLimit: boolean;
|
|
3
4
|
}, never>;
|
|
4
|
-
|
|
5
|
+
declare const VisuallyHiddenCharacterCount: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
6
|
+
declare const VisuallyHiddenHint: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
7
|
+
export { StyledCharacterCountWrapper, StyledCharacterCount, VisuallyHiddenCharacterCount, VisuallyHiddenHint, };
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import styled, { css } from "styled-components";
|
|
2
2
|
import baseTheme from "../../style/themes/base";
|
|
3
|
+
import visuallyHidden from "../../style/utils/visually-hidden";
|
|
4
|
+
const StyledCharacterCountWrapper = styled.div``;
|
|
3
5
|
const StyledCharacterCount = styled.div`
|
|
4
|
-
|
|
6
|
+
::after {
|
|
7
|
+
content: " ";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
text-align: left;
|
|
5
11
|
font-size: 12px;
|
|
6
12
|
margin-top: 4px;
|
|
7
13
|
margin-bottom: 4px;
|
|
@@ -15,7 +21,25 @@ const StyledCharacterCount = styled.div`
|
|
|
15
21
|
font-weight: 700;
|
|
16
22
|
`}
|
|
17
23
|
`;
|
|
24
|
+
const VisuallyHiddenCharacterCount = styled.div`
|
|
25
|
+
::after {
|
|
26
|
+
content: " ";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
${visuallyHidden}
|
|
30
|
+
`;
|
|
31
|
+
const VisuallyHiddenHint = styled.div`
|
|
32
|
+
::before {
|
|
33
|
+
content: " ";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
::after {
|
|
37
|
+
content: " ";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
${visuallyHidden}
|
|
41
|
+
`;
|
|
18
42
|
StyledCharacterCount.defaultProps = {
|
|
19
43
|
theme: baseTheme
|
|
20
44
|
};
|
|
21
|
-
export
|
|
45
|
+
export { StyledCharacterCountWrapper, StyledCharacterCount, VisuallyHiddenCharacterCount, VisuallyHiddenHint };
|
|
@@ -12,7 +12,7 @@ export interface DateChangeEvent {
|
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
export interface DateInputProps extends Omit<TextboxProps, "value" | "formattedValue" | "rawValue" | "onChange" | "onBlur" | "onMouseDown" | "onChangeDeferred" | "deferTimeout" | "children" | "leftChildren" | "placeholder" | "iconOnClick" | "iconOnMouseDown" | "
|
|
15
|
+
export interface DateInputProps extends Omit<TextboxProps, "value" | "formattedValue" | "rawValue" | "onChange" | "onBlur" | "onMouseDown" | "onChangeDeferred" | "deferTimeout" | "children" | "leftChildren" | "placeholder" | "iconOnClick" | "iconOnMouseDown" | "characterLimit" | "warnOverLimit" | "iconTabIndex" | "inputIcon"> {
|
|
16
16
|
/** Boolean to allow the input to have an empty value */
|
|
17
17
|
allowEmptyValue?: boolean;
|
|
18
18
|
/** Boolean to toggle where DatePicker is rendered in relation to the Date Input */
|
|
@@ -203,7 +203,6 @@ GroupedCharacter.propTypes = {
|
|
|
203
203
|
"dir": PropTypes.string,
|
|
204
204
|
"disabled": PropTypes.bool,
|
|
205
205
|
"draggable": PropTypes.oneOfType([PropTypes.oneOf(["false", "true"]), PropTypes.bool]),
|
|
206
|
-
"enforceCharacterLimit": PropTypes.bool,
|
|
207
206
|
"enterKeyHint": PropTypes.oneOf(["done", "enter", "go", "next", "previous", "search", "send"]),
|
|
208
207
|
"error": PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
|
|
209
208
|
"fieldHelp": PropTypes.node,
|
|
@@ -135,7 +135,6 @@ Number.propTypes = {
|
|
|
135
135
|
"dir": PropTypes.string,
|
|
136
136
|
"disabled": PropTypes.bool,
|
|
137
137
|
"draggable": PropTypes.oneOfType([PropTypes.oneOf(["false", "true"]), PropTypes.bool]),
|
|
138
|
-
"enforceCharacterLimit": PropTypes.bool,
|
|
139
138
|
"enterKeyHint": PropTypes.oneOf(["done", "enter", "go", "next", "previous", "search", "send"]),
|
|
140
139
|
"error": PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
|
|
141
140
|
"fieldHelp": PropTypes.node,
|
|
@@ -26,8 +26,6 @@ export interface TextareaProps extends ValidationProps, MarginProps, Omit<Common
|
|
|
26
26
|
cols?: number;
|
|
27
27
|
/** If true, the component will be disabled */
|
|
28
28
|
disabled?: boolean;
|
|
29
|
-
/** Stop the user typing over the characterLimit */
|
|
30
|
-
enforceCharacterLimit?: boolean;
|
|
31
29
|
/** Indicate that error has occurred
|
|
32
30
|
Pass string to display icon, tooltip and red border
|
|
33
31
|
Pass true boolean to only display red border */
|
|
@@ -28,7 +28,6 @@ const Textarea = /*#__PURE__*/React.forwardRef(({
|
|
|
28
28
|
size,
|
|
29
29
|
children,
|
|
30
30
|
characterLimit,
|
|
31
|
-
enforceCharacterLimit = true,
|
|
32
31
|
onChange,
|
|
33
32
|
disabled = false,
|
|
34
33
|
labelInline,
|
|
@@ -119,7 +118,7 @@ const Textarea = /*#__PURE__*/React.forwardRef(({
|
|
|
119
118
|
label,
|
|
120
119
|
fieldHelp
|
|
121
120
|
});
|
|
122
|
-
const [
|
|
121
|
+
const [characterCount, visuallyHiddenHintId] = useCharacterCount(value, characterLimit);
|
|
123
122
|
useEffect(() => {
|
|
124
123
|
if (rows) {
|
|
125
124
|
minHeight.current = internalRef?.current?.scrollHeight || 0;
|
|
@@ -147,10 +146,8 @@ const Textarea = /*#__PURE__*/React.forwardRef(({
|
|
|
147
146
|
}, [expandable]);
|
|
148
147
|
const hasIconInside = !!(inputIcon || validationId && !validationOnLabel);
|
|
149
148
|
const hintId = useRef(guid());
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
const hintIdValue = characterLimit ? characterCountHintIdValue : inputHintIdValue;
|
|
153
|
-
const combinedAriaDescribedBy = [ariaDescribedBy, hintIdValue].filter(Boolean).join(" ");
|
|
149
|
+
const inputHintId = inputHint ? hintId.current : undefined;
|
|
150
|
+
const combinedAriaDescribedBy = [ariaDescribedBy, inputHintId, visuallyHiddenHintId].filter(Boolean).join(" ");
|
|
154
151
|
const input = /*#__PURE__*/React.createElement(InputPresentation, {
|
|
155
152
|
size: size,
|
|
156
153
|
disabled: disabled,
|
|
@@ -168,7 +165,6 @@ const Textarea = /*#__PURE__*/React.forwardRef(({
|
|
|
168
165
|
name: name,
|
|
169
166
|
value: value,
|
|
170
167
|
ref: callbackRef,
|
|
171
|
-
maxLength: maxLength,
|
|
172
168
|
onChange: onChange,
|
|
173
169
|
disabled: disabled,
|
|
174
170
|
readOnly: readOnly,
|
|
@@ -219,10 +215,10 @@ const Textarea = /*#__PURE__*/React.forwardRef(({
|
|
|
219
215
|
useValidationIcon: computeLabelPropValues(validationOnLabel),
|
|
220
216
|
adaptiveLabelBreakpoint: adaptiveLabelBreakpoint,
|
|
221
217
|
validationRedesignOptIn: validationRedesignOptIn
|
|
222
|
-
},
|
|
223
|
-
id:
|
|
218
|
+
}, inputHint ? /*#__PURE__*/React.createElement(StyledInputHint, {
|
|
219
|
+
id: inputHintId,
|
|
224
220
|
"data-element": "input-hint"
|
|
225
|
-
},
|
|
221
|
+
}, inputHint) : null, validationRedesignOptIn && labelHelp && /*#__PURE__*/React.createElement(StyledHintText, null, labelHelp), validationRedesignOptIn ? /*#__PURE__*/React.createElement(Box, {
|
|
226
222
|
position: "relative"
|
|
227
223
|
}, /*#__PURE__*/React.createElement(ValidationMessage, {
|
|
228
224
|
error: error,
|
|
@@ -317,7 +313,6 @@ Textarea.propTypes = {
|
|
|
317
313
|
"dir": PropTypes.string,
|
|
318
314
|
"disabled": PropTypes.bool,
|
|
319
315
|
"draggable": PropTypes.oneOfType([PropTypes.oneOf(["false", "true"]), PropTypes.bool]),
|
|
320
|
-
"enforceCharacterLimit": PropTypes.bool,
|
|
321
316
|
"enterKeyHint": PropTypes.oneOf(["done", "enter", "go", "next", "previous", "search", "send"]),
|
|
322
317
|
"error": PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
|
|
323
318
|
"expandable": PropTypes.bool,
|
|
@@ -94,8 +94,6 @@ export interface TextboxProps extends CommonTextboxProps {
|
|
|
94
94
|
positionedChildren?: React.ReactNode;
|
|
95
95
|
/** Character limit of the textarea */
|
|
96
96
|
characterLimit?: number;
|
|
97
|
-
/** Stop the user typing over the characterLimit */
|
|
98
|
-
enforceCharacterLimit?: boolean;
|
|
99
97
|
}
|
|
100
98
|
export declare const Textbox: React.ForwardRefExoticComponent<TextboxProps & React.RefAttributes<HTMLInputElement>>;
|
|
101
99
|
export default Textbox;
|
|
@@ -70,13 +70,13 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
|
|
|
70
70
|
"data-component": dataComponent,
|
|
71
71
|
"data-element": dataElement,
|
|
72
72
|
"data-role": dataRole,
|
|
73
|
-
enforceCharacterLimit = true,
|
|
74
73
|
characterLimit,
|
|
75
74
|
helpAriaLabel,
|
|
76
75
|
...props
|
|
77
76
|
}, ref) => {
|
|
78
77
|
const characterCountValue = typeof value === "string" ? value : "";
|
|
79
|
-
const [
|
|
78
|
+
const [uniqueId, uniqueName] = useUniqueId(id, name);
|
|
79
|
+
const [characterCount, visuallyHiddenHintId] = useCharacterCount(characterCountValue, characterLimit);
|
|
80
80
|
const {
|
|
81
81
|
validationRedesignOptIn
|
|
82
82
|
} = useContext(NewValidationContext);
|
|
@@ -84,7 +84,6 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
|
|
|
84
84
|
disableErrorBorder
|
|
85
85
|
} = useContext(NumeralDateContext);
|
|
86
86
|
const computeLabelPropValues = prop => validationRedesignOptIn ? undefined : prop;
|
|
87
|
-
const [uniqueId, uniqueName] = useUniqueId(id, name);
|
|
88
87
|
if (!deprecateInputRefWarnTriggered && inputRef) {
|
|
89
88
|
deprecateInputRefWarnTriggered = true;
|
|
90
89
|
Logger.deprecate("The `inputRef` prop in `Textbox` component is deprecated and will soon be removed. Please use `ref` instead.");
|
|
@@ -108,10 +107,8 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
|
|
|
108
107
|
fieldHelp
|
|
109
108
|
});
|
|
110
109
|
const hintId = useRef(guid());
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
const hintIdValue = characterLimit ? characterCountHintIdValue : inputHintIdValue;
|
|
114
|
-
const combinedAriaDescribedBy = [ariaDescribedBy, hintIdValue].filter(Boolean).join(" ");
|
|
110
|
+
const inputHintId = inputHint ? hintId.current : undefined;
|
|
111
|
+
const combinedAriaDescribedBy = [ariaDescribedBy, inputHintId, visuallyHiddenHintId].filter(Boolean).join(" ");
|
|
115
112
|
const hasIconInside = !!(inputIcon || validationId && !validationOnLabel);
|
|
116
113
|
const input = /*#__PURE__*/React.createElement(InputPresentation, {
|
|
117
114
|
align: align,
|
|
@@ -152,7 +149,6 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
|
|
|
152
149
|
placeholder: disabled || readOnly ? "" : placeholder,
|
|
153
150
|
readOnly: readOnly,
|
|
154
151
|
value: typeof formattedValue === "string" ? formattedValue : value,
|
|
155
|
-
maxLength: maxLength,
|
|
156
152
|
validationIconId: validationRedesignOptIn ? undefined : validationId
|
|
157
153
|
}, props)), children, /*#__PURE__*/React.createElement(InputIconToggle, {
|
|
158
154
|
align: align,
|
|
@@ -197,10 +193,10 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
|
|
|
197
193
|
"data-element": dataElement,
|
|
198
194
|
validationIconId: validationRedesignOptIn ? undefined : validationId,
|
|
199
195
|
validationRedesignOptIn: validationRedesignOptIn
|
|
200
|
-
}, filterStyledSystemMarginProps(props)),
|
|
201
|
-
id:
|
|
196
|
+
}, filterStyledSystemMarginProps(props)), inputHint ? /*#__PURE__*/React.createElement(StyledInputHint, {
|
|
197
|
+
id: inputHintId,
|
|
202
198
|
"data-element": "input-hint"
|
|
203
|
-
},
|
|
199
|
+
}, inputHint) : null, validationRedesignOptIn && labelHelp && /*#__PURE__*/React.createElement(StyledHintText, null, labelHelp), validationRedesignOptIn ? /*#__PURE__*/React.createElement(Box, {
|
|
204
200
|
position: "relative"
|
|
205
201
|
}, /*#__PURE__*/React.createElement(ValidationMessage, {
|
|
206
202
|
error: error,
|
|
@@ -295,7 +291,6 @@ Textbox.propTypes = {
|
|
|
295
291
|
"dir": PropTypes.string,
|
|
296
292
|
"disabled": PropTypes.bool,
|
|
297
293
|
"draggable": PropTypes.oneOfType([PropTypes.oneOf(["false", "true"]), PropTypes.bool]),
|
|
298
|
-
"enforceCharacterLimit": PropTypes.bool,
|
|
299
294
|
"enterKeyHint": PropTypes.oneOf(["done", "enter", "go", "next", "previous", "search", "send"]),
|
|
300
295
|
"error": PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
|
|
301
296
|
"fieldHelp": PropTypes.node,
|
|
@@ -1,8 +1,3 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
declare const useCharacterCount: (value?: string, characterLimit?: number
|
|
3
|
-
number | undefined,
|
|
4
|
-
JSX.Element | null,
|
|
5
|
-
string | undefined,
|
|
6
|
-
string | null
|
|
7
|
-
];
|
|
2
|
+
declare const useCharacterCount: (value?: string, characterLimit?: number) => [JSX.Element | null, string | undefined];
|
|
8
3
|
export default useCharacterCount;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import React, { useMemo, useRef } from "react";
|
|
1
|
+
import React, { useMemo, useRef, useEffect, useState } from "react";
|
|
2
2
|
import CharacterCount from "../../../__internal__/character-count";
|
|
3
|
-
import useLocale from "../useLocale";
|
|
4
3
|
import guid from "../../../__internal__/utils/helpers/guid";
|
|
5
|
-
|
|
4
|
+
import useDebounce from "../useDebounce";
|
|
5
|
+
const useCharacterCount = (value = "", characterLimit) => {
|
|
6
6
|
const isCharacterLimitValid = typeof characterLimit === "number" && !Number.isNaN(characterLimit);
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
8
|
+
const debounceWaitTime = 2000;
|
|
9
|
+
const updateDebouncedValue = useDebounce(newValue => {
|
|
10
|
+
setDebouncedValue(newValue);
|
|
11
|
+
}, debounceWaitTime);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (characterLimit) {
|
|
14
|
+
updateDebouncedValue(value);
|
|
15
|
+
}
|
|
16
|
+
}, [value, characterLimit, updateDebouncedValue]);
|
|
9
17
|
const hintId = useRef(guid());
|
|
10
18
|
const isOverLimit = useMemo(() => {
|
|
11
19
|
if (value && isCharacterLimitValid) {
|
|
@@ -13,11 +21,19 @@ const useCharacterCount = (value = "", characterLimit, enforceCharacterLimit = t
|
|
|
13
21
|
}
|
|
14
22
|
return false;
|
|
15
23
|
}, [value, characterLimit, isCharacterLimitValid]);
|
|
16
|
-
|
|
24
|
+
const isDebouncedOverLimit = useMemo(() => {
|
|
25
|
+
if (debouncedValue && isCharacterLimitValid) {
|
|
26
|
+
return debouncedValue.length > characterLimit;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}, [debouncedValue, characterLimit, isCharacterLimitValid]);
|
|
30
|
+
return [isCharacterLimitValid ? /*#__PURE__*/React.createElement(CharacterCount, {
|
|
17
31
|
isOverLimit: isOverLimit,
|
|
32
|
+
isDebouncedOverLimit: isDebouncedOverLimit,
|
|
18
33
|
value: value.length,
|
|
34
|
+
debouncedValue: debouncedValue.length,
|
|
19
35
|
limit: characterLimit,
|
|
20
|
-
|
|
21
|
-
}) : null,
|
|
36
|
+
visuallyHiddenHintId: hintId.current
|
|
37
|
+
}) : null, isCharacterLimitValid ? hintId.current : undefined];
|
|
22
38
|
};
|
|
23
39
|
export default useCharacterCount;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./useDebounce";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./useDebounce";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useMemo, useEffect, useRef } from "react";
|
|
2
|
+
import debounce from "lodash/debounce";
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
+
|
|
6
|
+
const useDebounce = (callback, delay) => {
|
|
7
|
+
const callbackRef = useRef(callback);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
callbackRef.current = callback;
|
|
10
|
+
});
|
|
11
|
+
const debouncedCallback = useMemo(() => debounce(callbackRef.current, delay), [delay]);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
return () => {
|
|
14
|
+
debouncedCallback.cancel();
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
return debouncedCallback;
|
|
18
|
+
};
|
|
19
|
+
export default useDebounce;
|
package/esm/locales/en-gb.js
CHANGED
|
@@ -25,9 +25,9 @@ const enGB = {
|
|
|
25
25
|
yes: () => "Yes"
|
|
26
26
|
},
|
|
27
27
|
characterCount: {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
tooManyCharacters: (count, formattedCount) => count === 1 ? `${formattedCount} character too many` : `${formattedCount} characters too many`,
|
|
29
|
+
charactersLeft: (count, formattedCount) => count === 1 ? `${formattedCount} character left` : `${formattedCount} characters left`,
|
|
30
|
+
visuallyHiddenHint: formattedCount => `You can enter up to ${formattedCount} characters`
|
|
31
31
|
},
|
|
32
32
|
date: {
|
|
33
33
|
dateFnsLocale: () => enGBDateLocale
|
package/esm/locales/locale.d.ts
CHANGED
|
@@ -20,9 +20,9 @@ interface Locale {
|
|
|
20
20
|
ariaLabel: () => string;
|
|
21
21
|
};
|
|
22
22
|
characterCount: {
|
|
23
|
-
hintString: () => string;
|
|
24
23
|
tooManyCharacters: (count: number, formattedCount: string) => string;
|
|
25
24
|
charactersLeft: (count: number, formattedCount: string) => string;
|
|
25
|
+
visuallyHiddenHint: (formattedCount: string) => string;
|
|
26
26
|
};
|
|
27
27
|
confirm: {
|
|
28
28
|
no: () => string;
|
package/esm/locales/pl-pl.js
CHANGED
|
@@ -62,9 +62,9 @@ const plPL = {
|
|
|
62
62
|
yes: () => "Tak"
|
|
63
63
|
},
|
|
64
64
|
characterCount: {
|
|
65
|
-
hintString: () => "Pole zawiera licznik znaków",
|
|
66
65
|
tooManyCharacters: (count, formattedCount) => `Masz o ${formattedCount} ${PolishPlural("znak", "znaki", "znaków", count)} za dużo`,
|
|
67
|
-
charactersLeft: (count, formattedCount) => `Masz ${formattedCount} ${PolishPlural("pozostały", "pozostałe", "pozostałych", count)} ${PolishPlural("znak", "znaki", "znaków", count)}
|
|
66
|
+
charactersLeft: (count, formattedCount) => `Masz ${formattedCount} ${PolishPlural("pozostały", "pozostałe", "pozostałych", count)} ${PolishPlural("znak", "znaki", "znaków", count)}`,
|
|
67
|
+
visuallyHiddenHint: formattedCount => `Można wprowadzić do ${formattedCount} znaków`
|
|
68
68
|
},
|
|
69
69
|
date: {
|
|
70
70
|
dateFnsLocale: () => plDateLocale
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
interface CharacterCountProps {
|
|
3
3
|
value: number;
|
|
4
|
+
debouncedValue?: number;
|
|
4
5
|
limit: number;
|
|
6
|
+
isDebouncedOverLimit?: boolean;
|
|
5
7
|
isOverLimit: boolean;
|
|
6
|
-
|
|
8
|
+
visuallyHiddenHintId?: string;
|
|
7
9
|
}
|
|
8
|
-
declare const CharacterCount: ({ value, limit, isOverLimit,
|
|
10
|
+
declare const CharacterCount: ({ value, debouncedValue, limit, isDebouncedOverLimit, isOverLimit, visuallyHiddenHintId, }: CharacterCountProps) => React.JSX.Element;
|
|
9
11
|
export default CharacterCount;
|
|
@@ -6,24 +6,34 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _react = _interopRequireDefault(require("react"));
|
|
8
8
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
9
|
-
var _characterCount =
|
|
9
|
+
var _characterCount = require("./character-count.style");
|
|
10
10
|
var _useLocale = _interopRequireDefault(require("../../hooks/__internal__/useLocale"));
|
|
11
11
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
12
|
const CharacterCount = ({
|
|
13
13
|
value,
|
|
14
|
+
debouncedValue = value,
|
|
14
15
|
limit,
|
|
16
|
+
isDebouncedOverLimit,
|
|
15
17
|
isOverLimit,
|
|
16
|
-
|
|
18
|
+
visuallyHiddenHintId
|
|
17
19
|
}) => {
|
|
18
20
|
const limitMinusValue = +limit - +value;
|
|
19
21
|
const valueMinusLimit = +value - +limit;
|
|
22
|
+
const debouncedLimitMinusValue = +limit - +debouncedValue;
|
|
23
|
+
const debouncedValueMinusLimit = debouncedValue - +limit;
|
|
20
24
|
const l = (0, _useLocale.default)();
|
|
21
25
|
const getFormatNumber = (rawValue, locale) => new Intl.NumberFormat(locale).format(rawValue);
|
|
22
|
-
return /*#__PURE__*/_react.default.createElement(_characterCount.default, {
|
|
23
|
-
"
|
|
26
|
+
return /*#__PURE__*/_react.default.createElement(_characterCount.StyledCharacterCountWrapper, null, /*#__PURE__*/_react.default.createElement(_characterCount.VisuallyHiddenHint, {
|
|
27
|
+
"data-element": "visually-hidden-hint",
|
|
28
|
+
id: visuallyHiddenHintId
|
|
29
|
+
}, l.characterCount.visuallyHiddenHint(getFormatNumber(limit, l.locale()))), /*#__PURE__*/_react.default.createElement(_characterCount.StyledCharacterCount, {
|
|
30
|
+
"aria-hidden": "true",
|
|
24
31
|
isOverLimit: isOverLimit,
|
|
25
|
-
"data-element":
|
|
26
|
-
}, !isOverLimit ? l.characterCount.charactersLeft(limitMinusValue, getFormatNumber(limitMinusValue, l.locale())) : l.characterCount.tooManyCharacters(valueMinusLimit, getFormatNumber(valueMinusLimit, l.locale())))
|
|
32
|
+
"data-element": "character-count"
|
|
33
|
+
}, !isOverLimit ? l.characterCount.charactersLeft(limitMinusValue, getFormatNumber(limitMinusValue, l.locale())) : l.characterCount.tooManyCharacters(valueMinusLimit, getFormatNumber(valueMinusLimit, l.locale()))), /*#__PURE__*/_react.default.createElement(_characterCount.VisuallyHiddenCharacterCount, {
|
|
34
|
+
"data-element": "visually-hidden-character-count",
|
|
35
|
+
"aria-live": "polite"
|
|
36
|
+
}, !isDebouncedOverLimit ? l.characterCount.charactersLeft(debouncedLimitMinusValue, getFormatNumber(debouncedLimitMinusValue, l.locale())) : l.characterCount.tooManyCharacters(debouncedValueMinusLimit, getFormatNumber(debouncedValueMinusLimit, l.locale()))));
|
|
27
37
|
};
|
|
28
38
|
var _default = CharacterCount;
|
|
29
39
|
exports.default = _default;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
declare const StyledCharacterCountWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
1
2
|
declare const StyledCharacterCount: import("styled-components").StyledComponent<"div", any, {
|
|
2
3
|
isOverLimit: boolean;
|
|
3
4
|
}, never>;
|
|
4
|
-
|
|
5
|
+
declare const VisuallyHiddenCharacterCount: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
6
|
+
declare const VisuallyHiddenHint: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
7
|
+
export { StyledCharacterCountWrapper, StyledCharacterCount, VisuallyHiddenCharacterCount, VisuallyHiddenHint, };
|
|
@@ -3,14 +3,21 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.VisuallyHiddenHint = exports.VisuallyHiddenCharacterCount = exports.StyledCharacterCountWrapper = exports.StyledCharacterCount = void 0;
|
|
7
7
|
var _styledComponents = _interopRequireWildcard(require("styled-components"));
|
|
8
8
|
var _base = _interopRequireDefault(require("../../style/themes/base"));
|
|
9
|
+
var _visuallyHidden = _interopRequireDefault(require("../../style/utils/visually-hidden"));
|
|
9
10
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
11
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
11
12
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
13
|
+
const StyledCharacterCountWrapper = _styledComponents.default.div``;
|
|
14
|
+
exports.StyledCharacterCountWrapper = StyledCharacterCountWrapper;
|
|
12
15
|
const StyledCharacterCount = _styledComponents.default.div`
|
|
13
|
-
|
|
16
|
+
::after {
|
|
17
|
+
content: " ";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
text-align: left;
|
|
14
21
|
font-size: 12px;
|
|
15
22
|
margin-top: 4px;
|
|
16
23
|
margin-bottom: 4px;
|
|
@@ -24,8 +31,27 @@ const StyledCharacterCount = _styledComponents.default.div`
|
|
|
24
31
|
font-weight: 700;
|
|
25
32
|
`}
|
|
26
33
|
`;
|
|
34
|
+
exports.StyledCharacterCount = StyledCharacterCount;
|
|
35
|
+
const VisuallyHiddenCharacterCount = _styledComponents.default.div`
|
|
36
|
+
::after {
|
|
37
|
+
content: " ";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
${_visuallyHidden.default}
|
|
41
|
+
`;
|
|
42
|
+
exports.VisuallyHiddenCharacterCount = VisuallyHiddenCharacterCount;
|
|
43
|
+
const VisuallyHiddenHint = _styledComponents.default.div`
|
|
44
|
+
::before {
|
|
45
|
+
content: " ";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
::after {
|
|
49
|
+
content: " ";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
${_visuallyHidden.default}
|
|
53
|
+
`;
|
|
54
|
+
exports.VisuallyHiddenHint = VisuallyHiddenHint;
|
|
27
55
|
StyledCharacterCount.defaultProps = {
|
|
28
56
|
theme: _base.default
|
|
29
|
-
};
|
|
30
|
-
var _default = StyledCharacterCount;
|
|
31
|
-
exports.default = _default;
|
|
57
|
+
};
|
|
@@ -12,7 +12,7 @@ export interface DateChangeEvent {
|
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
export interface DateInputProps extends Omit<TextboxProps, "value" | "formattedValue" | "rawValue" | "onChange" | "onBlur" | "onMouseDown" | "onChangeDeferred" | "deferTimeout" | "children" | "leftChildren" | "placeholder" | "iconOnClick" | "iconOnMouseDown" | "
|
|
15
|
+
export interface DateInputProps extends Omit<TextboxProps, "value" | "formattedValue" | "rawValue" | "onChange" | "onBlur" | "onMouseDown" | "onChangeDeferred" | "deferTimeout" | "children" | "leftChildren" | "placeholder" | "iconOnClick" | "iconOnMouseDown" | "characterLimit" | "warnOverLimit" | "iconTabIndex" | "inputIcon"> {
|
|
16
16
|
/** Boolean to allow the input to have an empty value */
|
|
17
17
|
allowEmptyValue?: boolean;
|
|
18
18
|
/** Boolean to toggle where DatePicker is rendered in relation to the Date Input */
|
|
@@ -213,7 +213,6 @@ GroupedCharacter.propTypes = {
|
|
|
213
213
|
"dir": _propTypes.default.string,
|
|
214
214
|
"disabled": _propTypes.default.bool,
|
|
215
215
|
"draggable": _propTypes.default.oneOfType([_propTypes.default.oneOf(["false", "true"]), _propTypes.default.bool]),
|
|
216
|
-
"enforceCharacterLimit": _propTypes.default.bool,
|
|
217
216
|
"enterKeyHint": _propTypes.default.oneOf(["done", "enter", "go", "next", "previous", "search", "send"]),
|
|
218
217
|
"error": _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.bool]),
|
|
219
218
|
"fieldHelp": _propTypes.default.node,
|
|
@@ -92,11 +92,6 @@ const StyledButtonChildrenContainer = _styledComponents.default.div`
|
|
|
92
92
|
box-shadow: var(--boxShadow100);
|
|
93
93
|
border-radius: var(--borderRadius100);
|
|
94
94
|
|
|
95
|
-
${_icon.default} {
|
|
96
|
-
margin-left: 0;
|
|
97
|
-
left: 8px;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
95
|
${_splitButtonChildren.borderRadiusStyling}
|
|
101
96
|
|
|
102
97
|
${_button.default} {
|
|
@@ -145,7 +145,6 @@ Number.propTypes = {
|
|
|
145
145
|
"dir": _propTypes.default.string,
|
|
146
146
|
"disabled": _propTypes.default.bool,
|
|
147
147
|
"draggable": _propTypes.default.oneOfType([_propTypes.default.oneOf(["false", "true"]), _propTypes.default.bool]),
|
|
148
|
-
"enforceCharacterLimit": _propTypes.default.bool,
|
|
149
148
|
"enterKeyHint": _propTypes.default.oneOf(["done", "enter", "go", "next", "previous", "search", "send"]),
|
|
150
149
|
"error": _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.bool]),
|
|
151
150
|
"fieldHelp": _propTypes.default.node,
|
|
@@ -26,8 +26,6 @@ export interface TextareaProps extends ValidationProps, MarginProps, Omit<Common
|
|
|
26
26
|
cols?: number;
|
|
27
27
|
/** If true, the component will be disabled */
|
|
28
28
|
disabled?: boolean;
|
|
29
|
-
/** Stop the user typing over the characterLimit */
|
|
30
|
-
enforceCharacterLimit?: boolean;
|
|
31
29
|
/** Indicate that error has occurred
|
|
32
30
|
Pass string to display icon, tooltip and red border
|
|
33
31
|
Pass true boolean to only display red border */
|
|
@@ -37,7 +37,6 @@ const Textarea = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
37
37
|
size,
|
|
38
38
|
children,
|
|
39
39
|
characterLimit,
|
|
40
|
-
enforceCharacterLimit = true,
|
|
41
40
|
onChange,
|
|
42
41
|
disabled = false,
|
|
43
42
|
labelInline,
|
|
@@ -128,7 +127,7 @@ const Textarea = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
128
127
|
label,
|
|
129
128
|
fieldHelp
|
|
130
129
|
});
|
|
131
|
-
const [
|
|
130
|
+
const [characterCount, visuallyHiddenHintId] = (0, _useCharacterCount.default)(value, characterLimit);
|
|
132
131
|
(0, _react.useEffect)(() => {
|
|
133
132
|
if (rows) {
|
|
134
133
|
minHeight.current = internalRef?.current?.scrollHeight || 0;
|
|
@@ -156,10 +155,8 @@ const Textarea = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
156
155
|
}, [expandable]);
|
|
157
156
|
const hasIconInside = !!(inputIcon || validationId && !validationOnLabel);
|
|
158
157
|
const hintId = (0, _react.useRef)((0, _guid.default)());
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
const hintIdValue = characterLimit ? characterCountHintIdValue : inputHintIdValue;
|
|
162
|
-
const combinedAriaDescribedBy = [ariaDescribedBy, hintIdValue].filter(Boolean).join(" ");
|
|
158
|
+
const inputHintId = inputHint ? hintId.current : undefined;
|
|
159
|
+
const combinedAriaDescribedBy = [ariaDescribedBy, inputHintId, visuallyHiddenHintId].filter(Boolean).join(" ");
|
|
163
160
|
const input = /*#__PURE__*/_react.default.createElement(_input.InputPresentation, {
|
|
164
161
|
size: size,
|
|
165
162
|
disabled: disabled,
|
|
@@ -177,7 +174,6 @@ const Textarea = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
177
174
|
name: name,
|
|
178
175
|
value: value,
|
|
179
176
|
ref: callbackRef,
|
|
180
|
-
maxLength: maxLength,
|
|
181
177
|
onChange: onChange,
|
|
182
178
|
disabled: disabled,
|
|
183
179
|
readOnly: readOnly,
|
|
@@ -228,10 +224,10 @@ const Textarea = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
228
224
|
useValidationIcon: computeLabelPropValues(validationOnLabel),
|
|
229
225
|
adaptiveLabelBreakpoint: adaptiveLabelBreakpoint,
|
|
230
226
|
validationRedesignOptIn: validationRedesignOptIn
|
|
231
|
-
},
|
|
232
|
-
id:
|
|
227
|
+
}, inputHint ? /*#__PURE__*/_react.default.createElement(_textbox.StyledInputHint, {
|
|
228
|
+
id: inputHintId,
|
|
233
229
|
"data-element": "input-hint"
|
|
234
|
-
},
|
|
230
|
+
}, inputHint) : null, validationRedesignOptIn && labelHelp && /*#__PURE__*/_react.default.createElement(_textbox.StyledHintText, null, labelHelp), validationRedesignOptIn ? /*#__PURE__*/_react.default.createElement(_box.default, {
|
|
235
231
|
position: "relative"
|
|
236
232
|
}, /*#__PURE__*/_react.default.createElement(_validationMessage.default, {
|
|
237
233
|
error: error,
|
|
@@ -327,7 +323,6 @@ Textarea.propTypes = {
|
|
|
327
323
|
"dir": _propTypes.default.string,
|
|
328
324
|
"disabled": _propTypes.default.bool,
|
|
329
325
|
"draggable": _propTypes.default.oneOfType([_propTypes.default.oneOf(["false", "true"]), _propTypes.default.bool]),
|
|
330
|
-
"enforceCharacterLimit": _propTypes.default.bool,
|
|
331
326
|
"enterKeyHint": _propTypes.default.oneOf(["done", "enter", "go", "next", "previous", "search", "send"]),
|
|
332
327
|
"error": _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.bool]),
|
|
333
328
|
"expandable": _propTypes.default.bool,
|
|
@@ -94,8 +94,6 @@ export interface TextboxProps extends CommonTextboxProps {
|
|
|
94
94
|
positionedChildren?: React.ReactNode;
|
|
95
95
|
/** Character limit of the textarea */
|
|
96
96
|
characterLimit?: number;
|
|
97
|
-
/** Stop the user typing over the characterLimit */
|
|
98
|
-
enforceCharacterLimit?: boolean;
|
|
99
97
|
}
|
|
100
98
|
export declare const Textbox: React.ForwardRefExoticComponent<TextboxProps & React.RefAttributes<HTMLInputElement>>;
|
|
101
99
|
export default Textbox;
|
|
@@ -79,13 +79,13 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
79
79
|
"data-component": dataComponent,
|
|
80
80
|
"data-element": dataElement,
|
|
81
81
|
"data-role": dataRole,
|
|
82
|
-
enforceCharacterLimit = true,
|
|
83
82
|
characterLimit,
|
|
84
83
|
helpAriaLabel,
|
|
85
84
|
...props
|
|
86
85
|
}, ref) => {
|
|
87
86
|
const characterCountValue = typeof value === "string" ? value : "";
|
|
88
|
-
const [
|
|
87
|
+
const [uniqueId, uniqueName] = (0, _useUniqueId.default)(id, name);
|
|
88
|
+
const [characterCount, visuallyHiddenHintId] = (0, _useCharacterCount.default)(characterCountValue, characterLimit);
|
|
89
89
|
const {
|
|
90
90
|
validationRedesignOptIn
|
|
91
91
|
} = (0, _react.useContext)(_carbonProvider.NewValidationContext);
|
|
@@ -93,7 +93,6 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
93
93
|
disableErrorBorder
|
|
94
94
|
} = (0, _react.useContext)(_numeralDateContext.default);
|
|
95
95
|
const computeLabelPropValues = prop => validationRedesignOptIn ? undefined : prop;
|
|
96
|
-
const [uniqueId, uniqueName] = (0, _useUniqueId.default)(id, name);
|
|
97
96
|
if (!deprecateInputRefWarnTriggered && inputRef) {
|
|
98
97
|
deprecateInputRefWarnTriggered = true;
|
|
99
98
|
_logger.default.deprecate("The `inputRef` prop in `Textbox` component is deprecated and will soon be removed. Please use `ref` instead.");
|
|
@@ -117,10 +116,8 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
117
116
|
fieldHelp
|
|
118
117
|
});
|
|
119
118
|
const hintId = (0, _react.useRef)((0, _guid.default)());
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
const hintIdValue = characterLimit ? characterCountHintIdValue : inputHintIdValue;
|
|
123
|
-
const combinedAriaDescribedBy = [ariaDescribedBy, hintIdValue].filter(Boolean).join(" ");
|
|
119
|
+
const inputHintId = inputHint ? hintId.current : undefined;
|
|
120
|
+
const combinedAriaDescribedBy = [ariaDescribedBy, inputHintId, visuallyHiddenHintId].filter(Boolean).join(" ");
|
|
124
121
|
const hasIconInside = !!(inputIcon || validationId && !validationOnLabel);
|
|
125
122
|
const input = /*#__PURE__*/_react.default.createElement(_input.InputPresentation, {
|
|
126
123
|
align: align,
|
|
@@ -161,7 +158,6 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
161
158
|
placeholder: disabled || readOnly ? "" : placeholder,
|
|
162
159
|
readOnly: readOnly,
|
|
163
160
|
value: typeof formattedValue === "string" ? formattedValue : value,
|
|
164
|
-
maxLength: maxLength,
|
|
165
161
|
validationIconId: validationRedesignOptIn ? undefined : validationId
|
|
166
162
|
}, props)), children, /*#__PURE__*/_react.default.createElement(_inputIconToggle.default, {
|
|
167
163
|
align: align,
|
|
@@ -206,10 +202,10 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
206
202
|
"data-element": dataElement,
|
|
207
203
|
validationIconId: validationRedesignOptIn ? undefined : validationId,
|
|
208
204
|
validationRedesignOptIn: validationRedesignOptIn
|
|
209
|
-
}, (0, _utils.filterStyledSystemMarginProps)(props)),
|
|
210
|
-
id:
|
|
205
|
+
}, (0, _utils.filterStyledSystemMarginProps)(props)), inputHint ? /*#__PURE__*/_react.default.createElement(_textbox.StyledInputHint, {
|
|
206
|
+
id: inputHintId,
|
|
211
207
|
"data-element": "input-hint"
|
|
212
|
-
},
|
|
208
|
+
}, inputHint) : null, validationRedesignOptIn && labelHelp && /*#__PURE__*/_react.default.createElement(_textbox.StyledHintText, null, labelHelp), validationRedesignOptIn ? /*#__PURE__*/_react.default.createElement(_box.default, {
|
|
213
209
|
position: "relative"
|
|
214
210
|
}, /*#__PURE__*/_react.default.createElement(_validationMessage.default, {
|
|
215
211
|
error: error,
|
|
@@ -305,7 +301,6 @@ Textbox.propTypes = {
|
|
|
305
301
|
"dir": _propTypes.default.string,
|
|
306
302
|
"disabled": _propTypes.default.bool,
|
|
307
303
|
"draggable": _propTypes.default.oneOfType([_propTypes.default.oneOf(["false", "true"]), _propTypes.default.bool]),
|
|
308
|
-
"enforceCharacterLimit": _propTypes.default.bool,
|
|
309
304
|
"enterKeyHint": _propTypes.default.oneOf(["done", "enter", "go", "next", "previous", "search", "send"]),
|
|
310
305
|
"error": _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.bool]),
|
|
311
306
|
"fieldHelp": _propTypes.default.node,
|
|
@@ -1,8 +1,3 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
declare const useCharacterCount: (value?: string, characterLimit?: number
|
|
3
|
-
number | undefined,
|
|
4
|
-
JSX.Element | null,
|
|
5
|
-
string | undefined,
|
|
6
|
-
string | null
|
|
7
|
-
];
|
|
2
|
+
declare const useCharacterCount: (value?: string, characterLimit?: number) => [JSX.Element | null, string | undefined];
|
|
8
3
|
export default useCharacterCount;
|
|
@@ -6,15 +6,23 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _characterCount = _interopRequireDefault(require("../../../__internal__/character-count"));
|
|
9
|
-
var _useLocale = _interopRequireDefault(require("../useLocale"));
|
|
10
9
|
var _guid = _interopRequireDefault(require("../../../__internal__/utils/helpers/guid"));
|
|
10
|
+
var _useDebounce = _interopRequireDefault(require("../useDebounce"));
|
|
11
11
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
12
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
13
13
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
14
|
-
const useCharacterCount = (value = "", characterLimit
|
|
14
|
+
const useCharacterCount = (value = "", characterLimit) => {
|
|
15
15
|
const isCharacterLimitValid = typeof characterLimit === "number" && !Number.isNaN(characterLimit);
|
|
16
|
-
const
|
|
17
|
-
const
|
|
16
|
+
const [debouncedValue, setDebouncedValue] = (0, _react.useState)(value);
|
|
17
|
+
const debounceWaitTime = 2000;
|
|
18
|
+
const updateDebouncedValue = (0, _useDebounce.default)(newValue => {
|
|
19
|
+
setDebouncedValue(newValue);
|
|
20
|
+
}, debounceWaitTime);
|
|
21
|
+
(0, _react.useEffect)(() => {
|
|
22
|
+
if (characterLimit) {
|
|
23
|
+
updateDebouncedValue(value);
|
|
24
|
+
}
|
|
25
|
+
}, [value, characterLimit, updateDebouncedValue]);
|
|
18
26
|
const hintId = (0, _react.useRef)((0, _guid.default)());
|
|
19
27
|
const isOverLimit = (0, _react.useMemo)(() => {
|
|
20
28
|
if (value && isCharacterLimitValid) {
|
|
@@ -22,12 +30,20 @@ const useCharacterCount = (value = "", characterLimit, enforceCharacterLimit = t
|
|
|
22
30
|
}
|
|
23
31
|
return false;
|
|
24
32
|
}, [value, characterLimit, isCharacterLimitValid]);
|
|
25
|
-
|
|
33
|
+
const isDebouncedOverLimit = (0, _react.useMemo)(() => {
|
|
34
|
+
if (debouncedValue && isCharacterLimitValid) {
|
|
35
|
+
return debouncedValue.length > characterLimit;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}, [debouncedValue, characterLimit, isCharacterLimitValid]);
|
|
39
|
+
return [isCharacterLimitValid ? /*#__PURE__*/_react.default.createElement(_characterCount.default, {
|
|
26
40
|
isOverLimit: isOverLimit,
|
|
41
|
+
isDebouncedOverLimit: isDebouncedOverLimit,
|
|
27
42
|
value: value.length,
|
|
43
|
+
debouncedValue: debouncedValue.length,
|
|
28
44
|
limit: characterLimit,
|
|
29
|
-
|
|
30
|
-
}) : null,
|
|
45
|
+
visuallyHiddenHintId: hintId.current
|
|
46
|
+
}) : null, isCharacterLimitValid ? hintId.current : undefined];
|
|
31
47
|
};
|
|
32
48
|
var _default = useCharacterCount;
|
|
33
49
|
exports.default = _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./useDebounce";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "default", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _useDebounce.default;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var _useDebounce = _interopRequireDefault(require("./useDebounce"));
|
|
13
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _debounce = _interopRequireDefault(require("lodash/debounce"));
|
|
9
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
|
|
12
|
+
const useDebounce = (callback, delay) => {
|
|
13
|
+
const callbackRef = (0, _react.useRef)(callback);
|
|
14
|
+
(0, _react.useEffect)(() => {
|
|
15
|
+
callbackRef.current = callback;
|
|
16
|
+
});
|
|
17
|
+
const debouncedCallback = (0, _react.useMemo)(() => (0, _debounce.default)(callbackRef.current, delay), [delay]);
|
|
18
|
+
(0, _react.useEffect)(() => {
|
|
19
|
+
return () => {
|
|
20
|
+
debouncedCallback.cancel();
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
return debouncedCallback;
|
|
24
|
+
};
|
|
25
|
+
var _default = useDebounce;
|
|
26
|
+
exports.default = _default;
|
package/lib/locales/en-gb.js
CHANGED
|
@@ -31,9 +31,9 @@ const enGB = {
|
|
|
31
31
|
yes: () => "Yes"
|
|
32
32
|
},
|
|
33
33
|
characterCount: {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
tooManyCharacters: (count, formattedCount) => count === 1 ? `${formattedCount} character too many` : `${formattedCount} characters too many`,
|
|
35
|
+
charactersLeft: (count, formattedCount) => count === 1 ? `${formattedCount} character left` : `${formattedCount} characters left`,
|
|
36
|
+
visuallyHiddenHint: formattedCount => `You can enter up to ${formattedCount} characters`
|
|
37
37
|
},
|
|
38
38
|
date: {
|
|
39
39
|
dateFnsLocale: () => _dateFnsLocales.enGB
|
package/lib/locales/locale.d.ts
CHANGED
|
@@ -20,9 +20,9 @@ interface Locale {
|
|
|
20
20
|
ariaLabel: () => string;
|
|
21
21
|
};
|
|
22
22
|
characterCount: {
|
|
23
|
-
hintString: () => string;
|
|
24
23
|
tooManyCharacters: (count: number, formattedCount: string) => string;
|
|
25
24
|
charactersLeft: (count: number, formattedCount: string) => string;
|
|
25
|
+
visuallyHiddenHint: (formattedCount: string) => string;
|
|
26
26
|
};
|
|
27
27
|
confirm: {
|
|
28
28
|
no: () => string;
|
package/lib/locales/pl-pl.js
CHANGED
|
@@ -69,9 +69,9 @@ const plPL = {
|
|
|
69
69
|
yes: () => "Tak"
|
|
70
70
|
},
|
|
71
71
|
characterCount: {
|
|
72
|
-
hintString: () => "Pole zawiera licznik znaków",
|
|
73
72
|
tooManyCharacters: (count, formattedCount) => `Masz o ${formattedCount} ${PolishPlural("znak", "znaki", "znaków", count)} za dużo`,
|
|
74
|
-
charactersLeft: (count, formattedCount) => `Masz ${formattedCount} ${PolishPlural("pozostały", "pozostałe", "pozostałych", count)} ${PolishPlural("znak", "znaki", "znaków", count)}
|
|
73
|
+
charactersLeft: (count, formattedCount) => `Masz ${formattedCount} ${PolishPlural("pozostały", "pozostałe", "pozostałych", count)} ${PolishPlural("znak", "znaki", "znaków", count)}`,
|
|
74
|
+
visuallyHiddenHint: formattedCount => `Można wprowadzić do ${formattedCount} znaków`
|
|
75
75
|
},
|
|
76
76
|
date: {
|
|
77
77
|
dateFnsLocale: () => _dateFnsLocales.pl
|