@telus-uds/components-base 1.28.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -2
- package/component-docs.json +76 -2
- package/lib/TextInput/TextInputBase.js +34 -11
- package/lib/TextInput/dictionary.js +6 -2
- package/lib/TooltipButton/TooltipButton.js +4 -5
- package/lib/Validator/Validator.js +272 -0
- package/lib/Validator/index.js +13 -0
- package/lib/index.js +9 -0
- package/lib/utils/props/handlerProps.js +6 -1
- package/lib-module/TextInput/TextInputBase.js +34 -11
- package/lib-module/TextInput/dictionary.js +6 -2
- package/lib-module/TooltipButton/TooltipButton.js +4 -5
- package/lib-module/Validator/Validator.js +246 -0
- package/lib-module/Validator/index.js +2 -0
- package/lib-module/index.js +1 -0
- package/lib-module/utils/props/handlerProps.js +6 -1
- package/package.json +2 -2
- package/src/TextInput/TextInputBase.jsx +44 -9
- package/src/TextInput/dictionary.js +6 -2
- package/src/TooltipButton/TooltipButton.jsx +3 -3
- package/src/Validator/Validator.jsx +217 -0
- package/src/Validator/index.js +3 -0
- package/src/index.js +1 -0
- package/src/utils/props/handlerProps.js +5 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import React, { forwardRef, createRef, useRef, useMemo, useEffect, useState } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import { View, StyleSheet, Platform } from 'react-native'
|
|
4
|
+
import { inputSupportsProps, selectSystemProps } from '../utils'
|
|
5
|
+
import { TextInput } from '../TextInput'
|
|
6
|
+
import StackView from '../StackView'
|
|
7
|
+
import InputSupports from '../InputSupports'
|
|
8
|
+
import { useThemeTokens } from '../ThemeProvider'
|
|
9
|
+
|
|
10
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([inputSupportsProps])
|
|
11
|
+
|
|
12
|
+
const selectCodeTextInputTokens = ({ outerBorderColor, outerBackgroundColor }) => {
|
|
13
|
+
return {
|
|
14
|
+
outerBorderColor,
|
|
15
|
+
outerBackgroundColor,
|
|
16
|
+
icon: null
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const Validator = forwardRef(
|
|
21
|
+
({ value = '', inactive, onChange, tokens = {}, variant = {}, ...rest }, ref) => {
|
|
22
|
+
const defaultRef = useRef()
|
|
23
|
+
const codeRef = ref ?? defaultRef
|
|
24
|
+
|
|
25
|
+
const { supportsProps } = selectProps(rest)
|
|
26
|
+
const strValidation = supportsProps.validation
|
|
27
|
+
const [individualCodes, setIndividualCodes] = useState({})
|
|
28
|
+
const [text, setText] = useState(value)
|
|
29
|
+
const validatorsLength = 6
|
|
30
|
+
const prefix = 'code'
|
|
31
|
+
const sufixValidation = 'Validation'
|
|
32
|
+
|
|
33
|
+
const [isHover, setIsHover] = useState(false)
|
|
34
|
+
const handleMouseOver = () => {
|
|
35
|
+
setIsHover(true)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const handleMouseOut = () => {
|
|
39
|
+
setIsHover(false)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const themeTokens = useThemeTokens('TextInput', tokens, variant, { hover: isHover })
|
|
43
|
+
|
|
44
|
+
const [codeReferences, singleCodes] = useMemo(() => {
|
|
45
|
+
const codes = []
|
|
46
|
+
const valueCodes = {}
|
|
47
|
+
for (let i = 0; validatorsLength && i < validatorsLength; i += 1) {
|
|
48
|
+
codes[prefix + i] = createRef()
|
|
49
|
+
valueCodes[prefix + i] = ''
|
|
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
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const handleChangeCode = () => {
|
|
66
|
+
let code = ''
|
|
67
|
+
for (let i = 0; i < validatorsLength; i += 1) code += singleCodes[prefix + i]
|
|
68
|
+
if (typeof onChange === 'function') onChange(code, singleCodes)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const handleChangeCodeValues = (event, codeId, nextIndex) => {
|
|
72
|
+
const codeElement = codeReferences[codeId]?.current
|
|
73
|
+
const val = event.nativeEvent?.value || event.target?.value
|
|
74
|
+
|
|
75
|
+
if (Number(val).toString() === 'NaN') {
|
|
76
|
+
codeElement.value = singleCodes[codeId] ?? ''
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
if (codeElement?.value?.length > 1) {
|
|
80
|
+
const oldValue = singleCodes[codeId]
|
|
81
|
+
const newValue = codeElement.value.replace(oldValue, '')
|
|
82
|
+
codeElement.value = newValue
|
|
83
|
+
handleSingleCodes(codeId, codeElement.value, 'success')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
handleSingleCodes(codeId, codeElement?.value ?? singleCodes[codeId], 'success')
|
|
87
|
+
handleChangeCode()
|
|
88
|
+
if (nextIndex === validatorsLength) {
|
|
89
|
+
codeElement.blur()
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
if (codeElement?.value?.length > 0) codeReferences[prefix + nextIndex].current.focus()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const handleKeyPress = (event, currentIndex, previousIndex) => {
|
|
96
|
+
if (!(event.keyCode === 8 || event.code === 'Backspace')) return
|
|
97
|
+
if (currentIndex > 0) {
|
|
98
|
+
codeReferences[prefix + currentIndex].current.value = ''
|
|
99
|
+
codeReferences[prefix + previousIndex].current.focus()
|
|
100
|
+
}
|
|
101
|
+
handleSingleCodes(prefix + currentIndex, '', '')
|
|
102
|
+
handleChangeCode()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const getCodeComponents = () => {
|
|
106
|
+
const components = []
|
|
107
|
+
|
|
108
|
+
for (let i = 0; validatorsLength && i < validatorsLength; i += 1) {
|
|
109
|
+
const codeId = prefix + i
|
|
110
|
+
const codeInputProps = {
|
|
111
|
+
nativeID: codeId,
|
|
112
|
+
ref: codeReferences[codeId] ?? null,
|
|
113
|
+
validation: strValidation || singleCodes[codeId + sufixValidation],
|
|
114
|
+
tokens: selectCodeTextInputTokens(themeTokens),
|
|
115
|
+
onFocus: () => codeReferences[codeId].current.select(),
|
|
116
|
+
onKeyPress: (event) => handleKeyPress(event, i, i - 1),
|
|
117
|
+
onMouseOver: handleMouseOver,
|
|
118
|
+
onMouseOut: handleMouseOut,
|
|
119
|
+
inactive
|
|
120
|
+
}
|
|
121
|
+
codeInputProps.validation || delete codeInputProps.validation
|
|
122
|
+
|
|
123
|
+
components.push(
|
|
124
|
+
<View key={codeId} style={staticStyles.codeInputWidth}>
|
|
125
|
+
<TextInput {...codeInputProps} />
|
|
126
|
+
</View>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
return components
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
/* eslint-disable no-unused-expressions */
|
|
134
|
+
if (Number(value).toString() !== 'NaN') setText(value)
|
|
135
|
+
}, [value])
|
|
136
|
+
|
|
137
|
+
/* eslint-disable react-hooks/exhaustive-deps */
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
for (let i = 0; i < validatorsLength; i += 1) {
|
|
140
|
+
codeReferences[prefix + i].current.value = text[i] ?? ''
|
|
141
|
+
handleSingleCodes(prefix + i, text[i] ?? '', text[i] ? 'success' : '')
|
|
142
|
+
}
|
|
143
|
+
}, [text])
|
|
144
|
+
|
|
145
|
+
/* eslint-disable react-hooks/exhaustive-deps */
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
const handlePasteCode = (event) => {
|
|
148
|
+
setText('')
|
|
149
|
+
const clipBoardText = event.clipboardData.getData('text')
|
|
150
|
+
if (Number(clipBoardText).toString() !== 'NaN') setText(clipBoardText)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const handleCopy = (event) => {
|
|
154
|
+
let clipBoardText = ''
|
|
155
|
+
for (let i = 0; i < validatorsLength; i += 1)
|
|
156
|
+
singleCodes[prefix + i] && (clipBoardText += singleCodes[prefix + i])
|
|
157
|
+
event.clipboardData.setData('text/plain', clipBoardText)
|
|
158
|
+
event.preventDefault()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (Platform.OS === 'web') {
|
|
162
|
+
for (let i = 0; i < validatorsLength; i += 1) {
|
|
163
|
+
codeReferences[prefix + i].current.addEventListener('paste', handlePasteCode)
|
|
164
|
+
codeReferences[prefix + i].current.addEventListener('copy', handleCopy)
|
|
165
|
+
codeReferences[prefix + i].current.addEventListener('input', (event) =>
|
|
166
|
+
handleChangeCodeValues(event, prefix + i, i + 1)
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return () => {
|
|
172
|
+
if (Platform.oldValue === 'web') {
|
|
173
|
+
for (let i = 0; i < validatorsLength; i += 1) {
|
|
174
|
+
codeReferences[prefix + i]?.current?.removeEventListener('paste', handlePasteCode)
|
|
175
|
+
codeReferences[prefix + i]?.current?.removeEventListener('copy', handleCopy)
|
|
176
|
+
codeReferences[prefix + i]?.current?.removeEventListener('input', (event) =>
|
|
177
|
+
handleChangeCodeValues(event, prefix + i, i + 1)
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}, [])
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<InputSupports {...supportsProps}>
|
|
186
|
+
<StackView space={2} direction="row" ref={codeRef}>
|
|
187
|
+
{getCodeComponents()}
|
|
188
|
+
</StackView>
|
|
189
|
+
</InputSupports>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
Validator.displayName = 'Validator'
|
|
194
|
+
|
|
195
|
+
Validator.propTypes = {
|
|
196
|
+
...selectedSystemPropTypes,
|
|
197
|
+
/**
|
|
198
|
+
* The value is a 6-digit code, may be only numeric characters, non numeric character aren't renderize
|
|
199
|
+
*/
|
|
200
|
+
value: PropTypes.string,
|
|
201
|
+
/**
|
|
202
|
+
* If true, the component is inactive and non editable.
|
|
203
|
+
*/
|
|
204
|
+
inactive: PropTypes.bool,
|
|
205
|
+
/**
|
|
206
|
+
* Use to react upon input's value changes. Required when the `value` prop is set. Will receive the input's value as an argument.
|
|
207
|
+
*/
|
|
208
|
+
onChange: PropTypes.func
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export default Validator
|
|
212
|
+
|
|
213
|
+
const staticStyles = StyleSheet.create({
|
|
214
|
+
codeInputWidth: {
|
|
215
|
+
width: 43
|
|
216
|
+
}
|
|
217
|
+
})
|
package/src/index.js
CHANGED
|
@@ -53,6 +53,7 @@ export { default as Typography } from './Typography'
|
|
|
53
53
|
export { default as A11yInfoProvider, useA11yInfo } from './A11yInfoProvider'
|
|
54
54
|
export { default as BaseProvider } from './BaseProvider'
|
|
55
55
|
export { useHydrationContext } from './BaseProvider/HydrationContext'
|
|
56
|
+
export { default as Validator } from './Validator'
|
|
56
57
|
export { default as ViewportProvider, useViewport, ViewportContext } from './ViewportProvider'
|
|
57
58
|
export {
|
|
58
59
|
default as ThemeProvider,
|
|
@@ -65,7 +65,11 @@ const textInputHandlerProps = {
|
|
|
65
65
|
/**
|
|
66
66
|
* onKeyDown handler (only supported on Web)
|
|
67
67
|
*/
|
|
68
|
-
|
|
68
|
+
onMouseOver: PropTypes.func,
|
|
69
|
+
/**
|
|
70
|
+
* onKeyDown handler (only supported on Web)
|
|
71
|
+
*/
|
|
72
|
+
onMouseOut: PropTypes.func
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
const selectTextInputHandlers = getPropSelector(textInputHandlerProps.types)
|