@telus-uds/components-base 3.8.0 → 3.9.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.
@@ -24,7 +24,7 @@ const Validator = React.forwardRef(
24
24
 
25
25
  const { supportsProps } = selectProps(rest)
26
26
  const strValidation = supportsProps.validation
27
- const [individualCodes, setIndividualCodes] = React.useState({})
27
+ const [, setIndividualCodes] = React.useState({})
28
28
  const [text, setText] = React.useState(value)
29
29
  const validatorsLength = 6
30
30
  const prefix = 'code'
@@ -44,75 +44,110 @@ const Validator = React.forwardRef(
44
44
  const [codeReferences, singleCodes] = React.useMemo(() => {
45
45
  const codes = []
46
46
  const valueCodes = {}
47
- for (let i = 0; validatorsLength && i < validatorsLength; i += 1) {
47
+ Array.from({ length: validatorsLength }, (_, i) => {
48
48
  codes[prefix + i] = React.createRef()
49
49
  valueCodes[prefix + i] = ''
50
50
  valueCodes[prefix + i + sufixValidation] = ''
51
- }
52
- return [codes, valueCodes]
53
- }, [])
54
-
55
- const handleSingleCodes = (codeId, val, validation) => {
56
- singleCodes[codeId] = val
57
- singleCodes[codeId + sufixValidation] = validation
58
- /* eslint-disable no-unused-expressions */
59
- setIndividualCodes({
60
- ...individualCodes,
61
- [codeId]: val
51
+ return null
62
52
  })
63
- }
64
-
65
- const changeDataMasking = (boxElement) => {
66
- let charMasking = ''
67
- const element = boxElement
68
- if (mask && mask.length === 1) charMasking = mask
69
- else if (mask && mask.length > 1) charMasking = mask.substring(0, 1)
53
+ return [codes, valueCodes]
54
+ }, [validatorsLength, prefix, sufixValidation])
70
55
 
71
- if (charMasking) element.value = charMasking
72
- }
56
+ const handleSingleCodes = React.useCallback(
57
+ (codeId, val, validation) => {
58
+ singleCodes[codeId] = val
59
+ singleCodes[codeId + sufixValidation] = validation
60
+ setIndividualCodes((prev) => ({
61
+ ...prev,
62
+ [codeId]: val
63
+ }))
64
+ },
65
+ [singleCodes, sufixValidation]
66
+ )
73
67
 
74
- const handleChangeCode = () => {
75
- let code = ''
76
- for (let i = 0; i < validatorsLength; i += 1) code += singleCodes[prefix + i]
77
- if (typeof onChange === 'function') onChange(code, singleCodes)
78
- }
68
+ const changeDataMasking = React.useCallback(
69
+ (boxElement) => {
70
+ let charMasking = ''
71
+ const element = boxElement
72
+ if (mask && mask.length === 1) {
73
+ charMasking = mask
74
+ } else if (mask && mask.length > 1) {
75
+ charMasking = mask.substring(0, 1)
76
+ }
79
77
 
80
- const handleChangeCodeValues = (event, codeId, nextIndex) => {
81
- const codeElement = codeReferences[codeId]?.current
82
- const val = event.nativeEvent?.value || event.target?.value
78
+ if (charMasking && element) {
79
+ element.value = charMasking
80
+ }
81
+ },
82
+ [mask]
83
+ )
83
84
 
84
- if (Number(val).toString() === 'NaN') {
85
- codeElement.value = singleCodes[codeId] ?? ''
86
- return
85
+ const handleChangeCode = React.useCallback(() => {
86
+ const code = Array.from(
87
+ { length: validatorsLength },
88
+ (_, i) => singleCodes[prefix + i] || ''
89
+ ).join('')
90
+ if (typeof onChange === 'function') {
91
+ onChange(code, singleCodes)
87
92
  }
93
+ }, [validatorsLength, singleCodes, prefix, onChange])
88
94
 
89
- handleSingleCodes(codeId, codeElement?.value ?? singleCodes[codeId], 'success')
90
- handleChangeCode()
91
- if (nextIndex === validatorsLength) {
92
- codeElement.blur()
93
- changeDataMasking(codeElement)
94
- return
95
- }
96
- if (codeElement?.value?.length > 0) {
97
- codeReferences[prefix + nextIndex].current.focus()
98
- changeDataMasking(codeElement)
99
- }
100
- }
95
+ const handleChangeCodeValues = React.useCallback(
96
+ (event, codeId, nextIndex) => {
97
+ const codeElement = codeReferences[codeId]?.current
98
+ const val = event.nativeEvent?.value || event.target?.value
99
+
100
+ // Only allow numeric characters and limit to single digit
101
+ const numericOnly = val.replace(/\D/g, '').substring(0, 1)
102
+
103
+ if (codeElement && codeElement.value) {
104
+ codeElement.value = numericOnly
105
+ }
106
+
107
+ handleSingleCodes(codeId, numericOnly, numericOnly ? 'success' : '')
108
+ handleChangeCode()
109
+
110
+ if (nextIndex === validatorsLength) {
111
+ codeElement?.blur()
112
+ changeDataMasking(codeElement)
113
+ return
114
+ }
115
+
116
+ if (numericOnly.length > 0) {
117
+ const nextElement = codeReferences[prefix + nextIndex]?.current
118
+ nextElement?.focus()
119
+ changeDataMasking(codeElement)
120
+ }
121
+ },
122
+ [
123
+ codeReferences,
124
+ handleSingleCodes,
125
+ handleChangeCode,
126
+ validatorsLength,
127
+ changeDataMasking,
128
+ prefix
129
+ ]
130
+ )
101
131
 
102
132
  const handleKeyPress = (event, currentIndex, previousIndex) => {
103
- if (!(event.keyCode === 8 || event.code === 'Backspace')) return
133
+ if (!(event.keyCode === 8 || event.code === 'Backspace')) {
134
+ return
135
+ }
104
136
  if (currentIndex > 0) {
105
- codeReferences[prefix + currentIndex].current.value = ''
106
- codeReferences[prefix + previousIndex].current.focus()
137
+ const currentElement = codeReferences[prefix + currentIndex]?.current
138
+ const previousElement = codeReferences[prefix + previousIndex]?.current
139
+
140
+ if (currentElement && currentElement.value) {
141
+ currentElement.value = ''
142
+ }
143
+ previousElement?.focus()
107
144
  }
108
145
  handleSingleCodes(prefix + currentIndex, '', '')
109
146
  handleChangeCode()
110
147
  }
111
148
 
112
149
  const getCodeComponents = () => {
113
- const components = []
114
-
115
- for (let i = 0; validatorsLength && i < validatorsLength; i += 1) {
150
+ return Array.from({ length: validatorsLength }, (_, i) => {
116
151
  const codeId = prefix + i
117
152
  const codeInputProps = {
118
153
  nativeID: codeId,
@@ -120,75 +155,127 @@ const Validator = React.forwardRef(
120
155
  ref: codeReferences[codeId] ?? null,
121
156
  validation: strValidation || singleCodes[codeId + sufixValidation],
122
157
  tokens: selectCodeTextInputTokens(themeTokens),
123
- onFocus: () => codeReferences[codeId]?.current?.select() ?? null,
158
+ onFocus: () => {
159
+ const element = codeReferences[codeId]?.current
160
+ if (Platform.OS === 'web' && element?.select) {
161
+ return element.select() ?? null
162
+ }
163
+ if (element?.focus) {
164
+ return element.focus() ?? null
165
+ }
166
+ return null
167
+ },
124
168
  onKeyPress: (event) => handleKeyPress(event, i, i - 1),
125
169
  onMouseOver: handleMouseOver,
126
170
  onMouseOut: handleMouseOut,
127
171
  inactive
128
172
  }
129
- codeInputProps.validation || delete codeInputProps.validation
173
+ if (!codeInputProps.validation) {
174
+ delete codeInputProps.validation
175
+ }
130
176
 
131
- components.push(
177
+ return (
132
178
  <View key={codeId} style={staticStyles.codeInputWidth}>
133
179
  <TextInput {...codeInputProps} />
134
180
  </View>
135
181
  )
136
- }
137
- return components
182
+ })
138
183
  }
139
184
 
140
185
  React.useEffect(() => {
141
- /* eslint-disable no-unused-expressions */
142
- if (Number(value).toString() !== 'NaN') setText(value)
186
+ if (Number(value).toString() !== 'NaN') {
187
+ setText(value)
188
+ }
143
189
  }, [value])
144
190
 
145
- /* eslint-disable react-hooks/exhaustive-deps */
146
191
  React.useEffect(() => {
147
- for (let i = 0; i < validatorsLength; i += 1) {
148
- if (mask && text[i]) codeReferences[prefix + i].current.value = mask
149
- else codeReferences[prefix + i].current.value = text[i] ?? ''
192
+ Array.from({ length: validatorsLength }, (_, i) => {
193
+ const element = codeReferences[prefix + i]?.current
194
+ if (element && element.value !== undefined) {
195
+ if (mask && text[i]) {
196
+ element.value = mask
197
+ } else {
198
+ element.value = text[i] ?? ''
199
+ }
200
+ }
150
201
  handleSingleCodes(prefix + i, text[i] ?? '', text[i] ? 'success' : '')
151
- }
152
- }, [text])
202
+ return null
203
+ })
204
+ }, [text, mask, validatorsLength, prefix, codeReferences, handleSingleCodes])
153
205
 
154
- /* eslint-disable react-hooks/exhaustive-deps */
155
206
  React.useEffect(() => {
156
207
  const handlePasteCode = (event) => {
208
+ event.preventDefault()
209
+
210
+ // Clear current state first
157
211
  setText('')
212
+
213
+ // Clear all individual input fields and their state
214
+ Array.from({ length: validatorsLength }, (_, i) => {
215
+ const element = codeReferences[prefix + i]?.current
216
+ if (element && element.value !== undefined) {
217
+ element.value = ''
218
+ }
219
+ handleSingleCodes(prefix + i, '', '')
220
+ return null
221
+ })
222
+
158
223
  const clipBoardText = event.clipboardData.getData('text')
159
- if (Number(clipBoardText).toString() !== 'NaN') setText(clipBoardText)
224
+
225
+ // Validate that input contains only digits and truncate to 6 characters
226
+ const numericOnly = clipBoardText.replace(/\D/g, '').substring(0, validatorsLength)
227
+
228
+ if (numericOnly.length > 0) {
229
+ setText(numericOnly)
230
+ }
160
231
  }
161
232
 
162
233
  const handleCopy = (event) => {
163
- let clipBoardText = ''
164
- for (let i = 0; i < validatorsLength; i += 1)
165
- singleCodes[prefix + i] && (clipBoardText += singleCodes[prefix + i])
234
+ const clipBoardText = Array.from(
235
+ { length: validatorsLength },
236
+ (_, i) => singleCodes[prefix + i] || ''
237
+ ).join('')
166
238
  event.clipboardData.setData('text/plain', clipBoardText)
167
239
  event.preventDefault()
168
240
  }
169
241
 
170
242
  if (Platform.OS === 'web') {
171
- for (let i = 0; i < validatorsLength; i += 1) {
172
- codeReferences[prefix + i].current.addEventListener('paste', handlePasteCode)
173
- codeReferences[prefix + i].current.addEventListener('copy', handleCopy)
174
- codeReferences[prefix + i].current.addEventListener('input', (event) =>
175
- handleChangeCodeValues(event, prefix + i, i + 1)
176
- )
177
- }
178
- }
179
-
180
- return () => {
181
- if (Platform.oldValue === 'web') {
182
- for (let i = 0; i < validatorsLength; i += 1) {
183
- codeReferences[prefix + i]?.current?.removeEventListener('paste', handlePasteCode)
184
- codeReferences[prefix + i]?.current?.removeEventListener('copy', handleCopy)
185
- codeReferences[prefix + i]?.current?.removeEventListener('input', (event) =>
243
+ Array.from({ length: validatorsLength }, (_, i) => {
244
+ const element = codeReferences[prefix + i]?.current
245
+ if (element && typeof element.addEventListener === 'function') {
246
+ element.addEventListener('paste', handlePasteCode)
247
+ element.addEventListener('copy', handleCopy)
248
+ element.addEventListener('input', (event) =>
186
249
  handleChangeCodeValues(event, prefix + i, i + 1)
187
250
  )
188
251
  }
252
+ return null
253
+ })
254
+ }
255
+
256
+ return () => {
257
+ if (Platform.OS === 'web') {
258
+ Array.from({ length: validatorsLength }, (_, i) => {
259
+ const element = codeReferences[prefix + i]?.current
260
+ if (element && typeof element.removeEventListener === 'function') {
261
+ element.removeEventListener('paste', handlePasteCode)
262
+ element.removeEventListener('copy', handleCopy)
263
+ element.removeEventListener('input', (event) =>
264
+ handleChangeCodeValues(event, prefix + i, i + 1)
265
+ )
266
+ }
267
+ return null
268
+ })
189
269
  }
190
270
  }
191
- }, [])
271
+ }, [
272
+ validatorsLength,
273
+ prefix,
274
+ codeReferences,
275
+ handleChangeCodeValues,
276
+ handleSingleCodes,
277
+ singleCodes
278
+ ])
192
279
 
193
280
  return (
194
281
  <InputSupports