@telus-uds/components-base 3.22.0 → 3.23.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/lib/cjs/Button/Button.js +2 -0
  3. package/lib/cjs/Button/ButtonBase.js +10 -5
  4. package/lib/cjs/Button/ButtonDropdown.js +2 -0
  5. package/lib/cjs/Button/ButtonGroup.js +45 -38
  6. package/lib/cjs/Button/propTypes.js +6 -0
  7. package/lib/cjs/Carousel/Carousel.js +52 -19
  8. package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +23 -3
  9. package/lib/cjs/Icon/Icon.js +8 -11
  10. package/lib/cjs/Icon/IconText.js +0 -1
  11. package/lib/cjs/Listbox/GroupControl.js +33 -39
  12. package/lib/cjs/Listbox/Listbox.js +22 -13
  13. package/lib/cjs/Listbox/ListboxGroup.js +2 -1
  14. package/lib/cjs/Listbox/ListboxOverlay.js +5 -2
  15. package/lib/cjs/Listbox/PressableItem.js +8 -4
  16. package/lib/cjs/TextInput/TextInputBase.js +5 -1
  17. package/lib/cjs/Validator/Validator.js +171 -135
  18. package/lib/esm/Button/Button.js +2 -0
  19. package/lib/esm/Button/ButtonBase.js +10 -5
  20. package/lib/esm/Button/ButtonDropdown.js +2 -0
  21. package/lib/esm/Button/ButtonGroup.js +44 -39
  22. package/lib/esm/Button/propTypes.js +6 -0
  23. package/lib/esm/Carousel/Carousel.js +52 -19
  24. package/lib/esm/Carousel/CarouselItem/CarouselItem.js +23 -3
  25. package/lib/esm/Icon/Icon.js +8 -11
  26. package/lib/esm/Icon/IconText.js +0 -1
  27. package/lib/esm/Listbox/GroupControl.js +33 -39
  28. package/lib/esm/Listbox/Listbox.js +23 -14
  29. package/lib/esm/Listbox/ListboxGroup.js +2 -1
  30. package/lib/esm/Listbox/ListboxOverlay.js +5 -2
  31. package/lib/esm/Listbox/PressableItem.js +8 -4
  32. package/lib/esm/TextInput/TextInputBase.js +5 -1
  33. package/lib/esm/Validator/Validator.js +171 -135
  34. package/lib/package.json +2 -2
  35. package/package.json +2 -2
  36. package/src/Button/Button.jsx +2 -1
  37. package/src/Button/ButtonBase.jsx +18 -12
  38. package/src/Button/ButtonDropdown.jsx +2 -0
  39. package/src/Button/ButtonGroup.jsx +62 -45
  40. package/src/Button/propTypes.js +6 -0
  41. package/src/Carousel/Carousel.jsx +58 -5
  42. package/src/Carousel/CarouselItem/CarouselItem.jsx +31 -3
  43. package/src/Icon/Icon.jsx +11 -14
  44. package/src/Icon/IconText.jsx +0 -1
  45. package/src/Listbox/GroupControl.jsx +41 -47
  46. package/src/Listbox/Listbox.jsx +26 -9
  47. package/src/Listbox/ListboxGroup.jsx +2 -1
  48. package/src/Listbox/ListboxOverlay.jsx +7 -2
  49. package/src/Listbox/PressableItem.jsx +8 -4
  50. package/src/TextInput/TextInputBase.jsx +5 -1
  51. package/src/Validator/Validator.jsx +180 -159
@@ -7,8 +7,8 @@ exports.default = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _propTypes = _interopRequireDefault(require("prop-types"));
9
9
  var _View = _interopRequireDefault(require("react-native-web/dist/cjs/exports/View"));
10
- var _StyleSheet = _interopRequireDefault(require("react-native-web/dist/cjs/exports/StyleSheet"));
11
10
  var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
11
+ var _StyleSheet = _interopRequireDefault(require("react-native-web/dist/cjs/exports/StyleSheet"));
12
12
  var _ThemeProvider = require("../ThemeProvider");
13
13
  var _utils = require("../utils");
14
14
  var _ExpandCollapse = _interopRequireDefault(require("../ExpandCollapse"));
@@ -21,11 +21,16 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
21
21
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
22
22
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
23
23
  const styles = _StyleSheet.default.create({
24
- list: {
24
+ container: {
25
25
  padding: 0,
26
26
  margin: 0
27
27
  }
28
28
  });
29
+ const selectContainerStyles = tokens => ({
30
+ minHeight: tokens.minHeight,
31
+ minWidth: tokens.minWidth,
32
+ backgroundColor: tokens.containerBackgroundColor
33
+ });
29
34
  const getInitialOpen = (items, selectedId) => items.filter(item => item.items && item.items.some(nestedItem => (nestedItem.id ?? nestedItem.label) === selectedId)).map(item => item.id ?? item.label);
30
35
  const Listbox = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
31
36
  let {
@@ -39,14 +44,12 @@ const Listbox = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
39
44
  itemRouterProps,
40
45
  onClose,
41
46
  variant,
42
- tokens
47
+ tokens,
48
+ testID
43
49
  } = _ref;
44
50
  const initialOpen = getInitialOpen(items, defaultSelectedId);
45
51
  const [selectedId, setSelectedId] = _react.default.useState(defaultSelectedId);
46
- const {
47
- minHeight,
48
- minWidth
49
- } = (0, _ThemeProvider.useThemeTokens)('Listbox', variant, tokens);
52
+ const listboxTokens = (0, _ThemeProvider.useThemeTokens)('Listbox', tokens, variant);
50
53
 
51
54
  // We need to keep track of each item's ref in order to be able to
52
55
  // focus on a specific item via keyboard navigation
@@ -105,11 +108,9 @@ const Listbox = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
105
108
  maxOpen: 1,
106
109
  ref: ref,
107
110
  children: expandProps => /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
108
- style: [styles.list, {
109
- minHeight,
110
- minWidth
111
- }],
112
- role: "listbox",
111
+ style: [styles.container, selectContainerStyles(listboxTokens)],
112
+ accessibilityRole: "combobox",
113
+ testID: testID,
113
114
  children: items.map((item, index) => {
114
115
  const {
115
116
  id,
@@ -171,7 +172,15 @@ Listbox.propTypes = {
171
172
  /**
172
173
  * onClose event
173
174
  */
174
- onClose: _propTypes.default.func
175
+ onClose: _propTypes.default.func,
176
+ /**
177
+ * Test ID for testing
178
+ */
179
+ testID: _propTypes.default.string,
180
+ /**
181
+ * Listbox variant
182
+ */
183
+ variant: _utils.variantProp.propType
175
184
  };
176
185
  Listbox.Overlay = _ListboxOverlay.default;
177
186
  var _default = exports.default = Listbox;
@@ -85,7 +85,8 @@ const ListboxGroup = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
85
85
  borderColor: 'transparent',
86
86
  borderRadius: 0,
87
87
  borderWidth: 0,
88
- marginBottom: 0
88
+ marginBottom: 0,
89
+ contentPanelBackgroundColor: 'transparent'
89
90
  },
90
91
  controlRef: ref,
91
92
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
@@ -37,12 +37,14 @@ const DropdownOverlay = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
37
37
  maxWidth,
38
38
  minWidth,
39
39
  onLayout,
40
- tokens
40
+ tokens,
41
+ testID
41
42
  } = _ref;
42
43
  const systemTokens = (0, _ThemeProvider.useThemeTokens)('Listbox', {}, {});
43
44
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
44
45
  ref: ref,
45
46
  onLayout: onLayout,
47
+ testID: testID,
46
48
  style: [overlaidPosition, {
47
49
  maxWidth,
48
50
  minWidth
@@ -81,6 +83,7 @@ DropdownOverlay.propTypes = {
81
83
  maxWidth: _propTypes.default.number,
82
84
  minWidth: _propTypes.default.number,
83
85
  onLayout: _propTypes.default.func,
84
- tokens: _propTypes.default.object
86
+ tokens: _propTypes.default.object,
87
+ testID: _propTypes.default.string
85
88
  };
86
89
  var _default = exports.default = _Platform.default.OS === 'web' ? withPortal(DropdownOverlay) : DropdownOverlay;
@@ -48,10 +48,14 @@ const getItemStyles = _ref => {
48
48
  color: itemColor,
49
49
  outline: itemOutline,
50
50
  textDecoration: itemTextDecoration,
51
- borderLeft: `${itemBorderLeftWidth}px solid ${itemBorderLeftColor}`,
52
- borderRight: `${itemBorderRightWidth}px solid ${itemBorderRightColor}`,
53
- borderTop: `${itemBorderTopWidth}px solid ${itemBorderTopColor}`,
54
- borderBottom: `${itemBorderBottomWidth}px solid ${itemBorderBottomColor}`,
51
+ borderLeftWidth: itemBorderLeftWidth,
52
+ borderLeftColor: itemBorderLeftColor,
53
+ borderRightWidth: itemBorderRightWidth,
54
+ borderRightColor: itemBorderRightColor,
55
+ borderTopWidth: itemBorderTopWidth,
56
+ borderTopColor: itemBorderTopColor,
57
+ borderBottomWidth: itemBorderBottomWidth,
58
+ borderBottomColor: itemBorderBottomColor,
55
59
  borderRadius: itemBorderRadius,
56
60
  justifyContent: 'center'
57
61
  };
@@ -286,6 +286,10 @@ const TextInputBase = /*#__PURE__*/_react.default.forwardRef((_ref8, ref) => {
286
286
  // Add a space every 4 digits starting from the 5th position
287
287
  filteredText = formattedValue.replace(regex, '$1 ').trim();
288
288
  }
289
+ // Apply maxLength if provided
290
+ if (rest.maxLength && filteredText && filteredText.length > rest.maxLength) {
291
+ filteredText = filteredText.substring(0, rest.maxLength);
292
+ }
289
293
  setValue(filteredText, event);
290
294
  if (typeof onChangeText === 'function') onChangeText(filteredText, event);
291
295
  };
@@ -358,7 +362,7 @@ const TextInputBase = /*#__PURE__*/_react.default.forwardRef((_ref8, ref) => {
358
362
  onMouseOut: handleMouseOut,
359
363
  onChange: handleChangeText,
360
364
  defaultValue: initialValue,
361
- maxLength: type === 'card' ? 19 : undefined,
365
+ maxLength: type === 'card' ? 19 : rest.maxLength,
362
366
  value: isControlled ? currentValue : undefined,
363
367
  onKeyPress
364
368
  };
@@ -44,11 +44,10 @@ const Validator = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
44
44
  supportsProps
45
45
  } = selectProps(rest);
46
46
  const strValidation = supportsProps.validation;
47
- const [, setIndividualCodes] = _react.default.useState({});
48
- const [text, setText] = _react.default.useState(value);
49
47
  const validatorsLength = 6;
50
48
  const prefix = 'code';
51
49
  const sufixValidation = 'Validation';
50
+ const [codes, setCodes] = _react.default.useState(() => Array(validatorsLength).fill(''));
52
51
  const [isHover, setIsHover] = _react.default.useState(false);
53
52
  const handleMouseOver = () => {
54
53
  setIsHover(true);
@@ -59,84 +58,87 @@ const Validator = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
59
58
  const themeTokens = (0, _ThemeProvider.useThemeTokens)('TextInput', tokens, variant, {
60
59
  hover: isHover
61
60
  });
62
- const [codeReferences, singleCodes] = _react.default.useMemo(() => {
63
- const codes = [];
64
- const valueCodes = {};
65
- Array.from({
61
+
62
+ // Create refs for input elements
63
+ const codeReferences = _react.default.useMemo(() => {
64
+ return Array.from({
66
65
  length: validatorsLength
67
- }, (_, i) => {
68
- codes[prefix + i] = /*#__PURE__*/_react.default.createRef();
69
- valueCodes[prefix + i] = '';
70
- valueCodes[prefix + i + sufixValidation] = '';
71
- return null;
66
+ }, () => /*#__PURE__*/_react.default.createRef());
67
+ }, [validatorsLength]);
68
+
69
+ // Keep onChange and mask in refs to avoid re-creating event listeners
70
+ const onChangeRef = _react.default.useRef(onChange);
71
+ const maskRef = _react.default.useRef(mask);
72
+ _react.default.useEffect(() => {
73
+ onChangeRef.current = onChange;
74
+ maskRef.current = mask;
75
+ }, [onChange, mask]);
76
+
77
+ // Update a single code digit
78
+ const updateCode = _react.default.useCallback((index, digit) => {
79
+ setCodes(prevCodes => {
80
+ const newCodes = [...prevCodes];
81
+ newCodes[index] = digit;
82
+ if (onChangeRef.current) {
83
+ const codeString = newCodes.join('');
84
+ const singleCodesObj = {};
85
+ newCodes.forEach((code, i) => {
86
+ singleCodesObj[prefix + i] = code;
87
+ singleCodesObj[prefix + i + sufixValidation] = code ? 'success' : '';
88
+ });
89
+ onChangeRef.current(codeString, singleCodesObj);
90
+ }
91
+ return newCodes;
72
92
  });
73
- return [codes, valueCodes];
74
- }, [validatorsLength, prefix, sufixValidation]);
75
- const handleSingleCodes = _react.default.useCallback((codeId, val, validation) => {
76
- singleCodes[codeId] = val;
77
- singleCodes[codeId + sufixValidation] = validation;
78
- setIndividualCodes(prev => ({
79
- ...prev,
80
- [codeId]: val
81
- }));
82
- }, [singleCodes, sufixValidation]);
83
- const changeDataMasking = _react.default.useCallback(boxElement => {
84
- let charMasking = '';
85
- const element = boxElement;
86
- if (mask && mask.length === 1) {
87
- charMasking = mask;
88
- } else if (mask && mask.length > 1) {
89
- charMasking = mask.substring(0, 1);
90
- }
91
- if (charMasking && element) {
92
- element.value = charMasking;
93
- }
94
- }, [mask]);
95
- const handleChangeCode = _react.default.useCallback(() => {
96
- const code = Array.from({
97
- length: validatorsLength
98
- }, (_, i) => singleCodes[prefix + i] || '').join('');
99
- if (typeof onChange === 'function') {
100
- onChange(code, singleCodes);
101
- }
102
- }, [validatorsLength, singleCodes, prefix, onChange]);
103
- const handleChangeCodeValues = _react.default.useCallback((event, codeId, nextIndex) => {
104
- const codeElement = codeReferences[codeId]?.current;
93
+ }, [prefix, sufixValidation]);
94
+
95
+ // Handle input change
96
+ const handleInputChange = _react.default.useCallback((index, event) => {
105
97
  const val = event.nativeEvent?.value || event.target?.value;
106
98
 
107
- // Only allow numeric characters and limit to single digit
108
- const numericOnly = val.replace(/\D/g, '').substring(0, 1);
109
- if (codeElement && codeElement.value) {
110
- codeElement.value = numericOnly;
111
- }
112
- handleSingleCodes(codeId, numericOnly, numericOnly ? 'success' : '');
113
- handleChangeCode();
114
- if (nextIndex === validatorsLength) {
115
- codeElement?.blur();
116
- changeDataMasking(codeElement);
99
+ // This prevents the infinite loop where setting element.value triggers another input event
100
+ if (maskRef.current && val === maskRef.current.substring(0, 1)) {
117
101
  return;
118
102
  }
119
- if (numericOnly.length > 0) {
120
- const nextElement = codeReferences[prefix + nextIndex]?.current;
103
+ const numericOnly = val.replace(/\D/g, '').substring(0, 1);
104
+
105
+ // Update state
106
+ updateCode(index, numericOnly);
107
+
108
+ // Update DOM element
109
+ const element = codeReferences[index]?.current;
110
+ if (element) {
111
+ if (maskRef.current && numericOnly) {
112
+ element.value = maskRef.current.substring(0, 1);
113
+ } else {
114
+ element.value = numericOnly;
115
+ }
116
+ }
117
+
118
+ // Move to next field if digit entered
119
+ if (numericOnly && index < validatorsLength - 1) {
120
+ const nextElement = codeReferences[index + 1]?.current;
121
121
  nextElement?.focus();
122
- changeDataMasking(codeElement);
122
+ } else if (index === validatorsLength - 1) {
123
+ element?.blur();
123
124
  }
124
- }, [codeReferences, handleSingleCodes, handleChangeCode, validatorsLength, changeDataMasking, prefix]);
125
- const handleKeyPress = (event, currentIndex, previousIndex) => {
125
+ }, [codeReferences, updateCode, validatorsLength]);
126
+
127
+ // Handle backspace
128
+ const handleKeyPress = _react.default.useCallback((index, event) => {
126
129
  if (!(event.keyCode === 8 || event.code === 'Backspace')) {
127
130
  return;
128
131
  }
129
- if (currentIndex > 0) {
130
- const currentElement = codeReferences[prefix + currentIndex]?.current;
131
- const previousElement = codeReferences[prefix + previousIndex]?.current;
132
- if (currentElement && currentElement.value) {
133
- currentElement.value = '';
134
- }
132
+ const currentElement = codeReferences[index]?.current;
133
+ if (currentElement) {
134
+ currentElement.value = '';
135
+ }
136
+ updateCode(index, '');
137
+ if (index > 0) {
138
+ const previousElement = codeReferences[index - 1]?.current;
135
139
  previousElement?.focus();
136
140
  }
137
- handleSingleCodes(prefix + currentIndex, '', '');
138
- handleChangeCode();
139
- };
141
+ }, [codeReferences, updateCode]);
140
142
  const getCodeComponents = () => {
141
143
  return Array.from({
142
144
  length: validatorsLength
@@ -145,11 +147,14 @@ const Validator = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
145
147
  const codeInputProps = {
146
148
  nativeID: codeId,
147
149
  keyboardType: 'numeric',
148
- ref: codeReferences[codeId] ?? null,
149
- validation: strValidation || singleCodes[codeId + sufixValidation],
150
+ ref: codeReferences[i] ?? null,
151
+ validation: strValidation || (codes[i] ? 'success' : ''),
150
152
  tokens: selectCodeTextInputTokens(themeTokens),
153
+ // Only use secureTextEntry in React Native, web handles mask differently
154
+ secureTextEntry: !!(_Platform.default.OS !== 'web' && mask),
155
+ selectTextOnFocus: _Platform.default.OS !== 'web',
151
156
  onFocus: () => {
152
- const element = codeReferences[codeId]?.current;
157
+ const element = codeReferences[i]?.current;
153
158
  if (_Platform.default.OS === 'web' && element?.select) {
154
159
  return element.select() ?? null;
155
160
  }
@@ -158,11 +163,35 @@ const Validator = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
158
163
  }
159
164
  return null;
160
165
  },
161
- onKeyPress: event => handleKeyPress(event, i, i - 1),
166
+ onKeyPress: event => handleKeyPress(i, event),
162
167
  onMouseOver: handleMouseOver,
163
168
  onMouseOut: handleMouseOut,
164
169
  inactive
165
170
  };
171
+
172
+ // For React Native, use onChangeText and maxLength
173
+ if (_Platform.default.OS !== 'web') {
174
+ codeInputProps.maxLength = 1;
175
+ codeInputProps.value = codes[i];
176
+ codeInputProps.onChange = () => {};
177
+ codeInputProps.onChangeText = text => {
178
+ if (text) {
179
+ updateCode(i, text);
180
+ if (i < validatorsLength - 1) {
181
+ setTimeout(() => {
182
+ codeReferences[i + 1]?.current?.focus();
183
+ }, 50);
184
+ }
185
+ } else {
186
+ updateCode(i, '');
187
+ if (i > 0) {
188
+ setTimeout(() => {
189
+ codeReferences[i - 1]?.current?.focus();
190
+ }, 50);
191
+ }
192
+ }
193
+ };
194
+ }
166
195
  if (!codeInputProps.validation) {
167
196
  delete codeInputProps.validation;
168
197
  }
@@ -174,89 +203,96 @@ const Validator = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
174
203
  }, codeId);
175
204
  });
176
205
  };
206
+
207
+ // Sync external value prop to internal state
177
208
  _react.default.useEffect(() => {
178
- if (Number(value).toString() !== 'NaN') {
179
- setText(value);
209
+ if (value && Number(value).toString() !== 'NaN') {
210
+ const digits = value.split('').slice(0, validatorsLength);
211
+ const newCodes = Array(validatorsLength).fill('');
212
+ digits.forEach((digit, i) => {
213
+ if (/\d/.test(digit)) {
214
+ newCodes[i] = digit;
215
+ }
216
+ });
217
+ setCodes(newCodes);
180
218
  }
181
- }, [value]);
219
+ }, [value, validatorsLength]);
220
+
221
+ // Sync codes state to DOM elements
182
222
  _react.default.useEffect(() => {
183
- Array.from({
184
- length: validatorsLength
185
- }, (_, i) => {
186
- const element = codeReferences[prefix + i]?.current;
223
+ codes.forEach((code, i) => {
224
+ const element = codeReferences[i]?.current;
187
225
  if (element && element.value !== undefined) {
188
- if (mask && text[i]) {
189
- element.value = mask;
226
+ if (mask && code) {
227
+ element.value = mask.substring(0, 1);
190
228
  } else {
191
- element.value = text[i] ?? '';
229
+ element.value = code;
192
230
  }
193
231
  }
194
- handleSingleCodes(prefix + i, text[i] ?? '', text[i] ? 'success' : '');
195
- return null;
196
232
  });
197
- }, [text, mask, validatorsLength, prefix, codeReferences, handleSingleCodes]);
233
+ }, [codes, codeReferences, mask]);
234
+
235
+ // Setup event listeners - only runs once on mount
198
236
  _react.default.useEffect(() => {
199
- const handlePasteCode = event => {
237
+ if (_Platform.default.OS !== 'web') {
238
+ return undefined;
239
+ }
240
+ const handlePaste = event => {
200
241
  event.preventDefault();
201
-
202
- // Clear current state first
203
- setText('');
204
-
205
- // Clear all individual input fields and their state
206
- Array.from({
207
- length: validatorsLength
208
- }, (_, i) => {
209
- const element = codeReferences[prefix + i]?.current;
210
- if (element && element.value !== undefined) {
211
- element.value = '';
212
- }
213
- handleSingleCodes(prefix + i, '', '');
214
- return null;
215
- });
216
242
  const clipBoardText = event.clipboardData.getData('text');
217
-
218
- // Validate that input contains only digits and truncate to 6 characters
219
243
  const numericOnly = clipBoardText.replace(/\D/g, '').substring(0, validatorsLength);
220
- if (numericOnly.length > 0) {
221
- setText(numericOnly);
244
+ const newCodes = Array(validatorsLength).fill('');
245
+ numericOnly.split('').forEach((digit, i) => {
246
+ newCodes[i] = digit;
247
+ });
248
+ setCodes(newCodes);
249
+ if (onChangeRef.current) {
250
+ const singleCodesObj = {};
251
+ newCodes.forEach((code, i) => {
252
+ singleCodesObj[prefix + i] = code;
253
+ singleCodesObj[prefix + i + sufixValidation] = code ? 'success' : '';
254
+ });
255
+ onChangeRef.current(numericOnly, singleCodesObj);
222
256
  }
223
257
  };
224
258
  const handleCopy = event => {
225
- const clipBoardText = Array.from({
226
- length: validatorsLength
227
- }, (_, i) => singleCodes[prefix + i] || '').join('');
228
- event.clipboardData.setData('text/plain', clipBoardText);
229
- event.preventDefault();
259
+ setCodes(currentCodes => {
260
+ const clipBoardText = currentCodes.join('');
261
+ event.clipboardData.setData('text/plain', clipBoardText);
262
+ event.preventDefault();
263
+ return currentCodes;
264
+ });
230
265
  };
231
- if (_Platform.default.OS === 'web') {
232
- Array.from({
233
- length: validatorsLength
234
- }, (_, i) => {
235
- const element = codeReferences[prefix + i]?.current;
236
- if (element && typeof element.addEventListener === 'function') {
237
- element.addEventListener('paste', handlePasteCode);
238
- element.addEventListener('copy', handleCopy);
239
- element.addEventListener('input', event => handleChangeCodeValues(event, prefix + i, i + 1));
266
+
267
+ // Add event listeners to each input
268
+ codeReferences.forEach((inputRef, i) => {
269
+ const element = inputRef?.current;
270
+ if (element && typeof element.addEventListener === 'function') {
271
+ element.addEventListener('paste', handlePaste);
272
+ element.addEventListener('copy', handleCopy);
273
+ element.addEventListener('input', event => handleInputChange(i, event));
274
+ }
275
+ });
276
+
277
+ // Cleanup
278
+ return () => {
279
+ codeReferences.forEach(inputRef => {
280
+ const element = inputRef?.current;
281
+ if (element && typeof element.removeEventListener === 'function') {
282
+ element.removeEventListener('paste', handlePaste);
283
+ element.removeEventListener('copy', handleCopy);
284
+ element.removeEventListener('input', event => handleInputChange(event.target.dataset.index, event));
240
285
  }
241
- return null;
242
286
  });
243
- }
244
- return () => {
245
- if (_Platform.default.OS === 'web') {
246
- Array.from({
247
- length: validatorsLength
248
- }, (_, i) => {
249
- const element = codeReferences[prefix + i]?.current;
250
- if (element && typeof element.removeEventListener === 'function') {
251
- element.removeEventListener('paste', handlePasteCode);
252
- element.removeEventListener('copy', handleCopy);
253
- element.removeEventListener('input', event => handleChangeCodeValues(event, prefix + i, i + 1));
254
- }
255
- return null;
256
- });
257
- }
258
287
  };
259
- }, [validatorsLength, prefix, codeReferences, handleChangeCodeValues, handleSingleCodes, singleCodes]);
288
+ /*
289
+ * codeReferences and handleInputChange are intentionally omitted from dependencies
290
+ * because we want event listeners to be registered ONLY ONCE when the component mounts.
291
+ * codeReferences is stable (created with useMemo) and handleInputChange is stable (useCallback).
292
+ * Including them would cause unnecessary re-registration of listeners on each render.
293
+ */
294
+ // eslint-disable-next-line react-hooks/exhaustive-deps
295
+ }, []);
260
296
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_InputSupports.default, {
261
297
  ...supportsProps,
262
298
  feedbackProps: {
@@ -10,6 +10,7 @@ const Button = /*#__PURE__*/React.forwardRef((_ref, ref) => {
10
10
  accessibilityRole = 'button',
11
11
  tokens,
12
12
  variant,
13
+ heightFull = true,
13
14
  ...props
14
15
  } = _ref;
15
16
  const viewport = useViewport();
@@ -27,6 +28,7 @@ const Button = /*#__PURE__*/React.forwardRef((_ref, ref) => {
27
28
  return /*#__PURE__*/_jsx(ButtonBase, {
28
29
  ...props,
29
30
  tokens: getTokens,
31
+ heightFull: heightFull,
30
32
  accessibilityRole: accessibilityRole,
31
33
  ref: ref,
32
34
  viewport: viewport
@@ -33,23 +33,27 @@ const selectFlexAndWidthStyles = _ref2 => {
33
33
  }
34
34
  return styles;
35
35
  };
36
- const selectOuterContainerStyles = _ref3 => {
36
+ const selectOuterContainerStyles = (_ref3, heightFull) => {
37
37
  let {
38
38
  opacity,
39
39
  outerBorderColor,
40
40
  outerBorderWidth,
41
41
  outerBorderGap,
42
42
  borderRadius,
43
- outerBackgroundColor
43
+ outerBackgroundColor,
44
+ alignSelf
44
45
  } = _ref3;
45
46
  return {
47
+ backgroundColor: outerBackgroundColor,
48
+ opacity,
46
49
  ...Platform.select({
47
50
  native: {
51
+ alignSelf: alignSelf !== undefined ? alignSelf : 'flex-start'
52
+ },
53
+ web: heightFull ? {} : {
48
54
  alignSelf: 'flex-start'
49
55
  }
50
56
  }),
51
- backgroundColor: outerBackgroundColor,
52
- opacity,
53
57
  ...applyOuterBorder({
54
58
  outerBorderGap,
55
59
  outerBorderWidth,
@@ -248,6 +252,7 @@ const ButtonBase = /*#__PURE__*/React.forwardRef((_ref12, ref) => {
248
252
  icon,
249
253
  iconPosition = icon ? 'left' : undefined,
250
254
  iconProps,
255
+ heightFull = true,
251
256
  ...rawRest
252
257
  } = _ref12;
253
258
  const {
@@ -307,7 +312,7 @@ const ButtonBase = /*#__PURE__*/React.forwardRef((_ref12, ref) => {
307
312
  }
308
313
  const themeTokens = resolveButtonTokens(pressableState);
309
314
  const flexAndWidthStyles = themeTokens.width === '100%' && themeTokens.flex === 1 ? selectFlexAndWidthStyles(themeTokens) : {};
310
- return [staticStyles.row, selectWebOnlyStyles(inactive, themeTokens, systemProps), selectOuterContainerStyles(themeTokens), ...(Object.keys(flexAndWidthStyles).length > 0 ? [flexAndWidthStyles] : []), selectOuterSizeStyles(themeTokens)];
315
+ return [staticStyles.row, selectWebOnlyStyles(inactive, themeTokens, systemProps), selectOuterContainerStyles(themeTokens, heightFull), ...(Object.keys(flexAndWidthStyles).length > 0 ? [flexAndWidthStyles] : []), selectOuterSizeStyles(themeTokens)];
311
316
  };
312
317
  const dataSetProp = flexAndWidthStylesIds || rawRest.dataSet ? {
313
318
  dataSet: {
@@ -103,6 +103,7 @@ const ButtonDropdown = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
103
103
  accessibilityRole = 'radio',
104
104
  description,
105
105
  singleOption,
106
+ heightFull = true,
106
107
  ...props
107
108
  } = _ref2;
108
109
  const isFullWidth = variant?.width === FULL_WIDTH_STYLE;
@@ -145,6 +146,7 @@ const ButtonDropdown = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
145
146
  ...pressHandlers,
146
147
  onPress: handlePress,
147
148
  tokens: getButtonTokens,
149
+ heightFull: heightFull,
148
150
  inactive: singleOption || inactive,
149
151
  icon: () => null,
150
152
  accessibilityRole: accessibilityRole,