@telus-uds/components-base 3.22.0 → 3.24.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 +29 -1
- package/lib/cjs/Button/Button.js +2 -0
- package/lib/cjs/Button/ButtonBase.js +10 -5
- package/lib/cjs/Button/ButtonDropdown.js +2 -0
- package/lib/cjs/Button/ButtonGroup.js +45 -38
- package/lib/cjs/Button/propTypes.js +6 -0
- package/lib/cjs/Card/CardBase.js +97 -17
- package/lib/cjs/Card/PressableCardBase.js +12 -8
- package/lib/cjs/Carousel/Carousel.js +52 -19
- package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +23 -3
- package/lib/cjs/HorizontalScroll/HorizontalScroll.js +5 -2
- package/lib/cjs/Icon/Icon.js +11 -11
- package/lib/cjs/Icon/IconText.js +0 -1
- package/lib/cjs/Listbox/GroupControl.js +44 -44
- package/lib/cjs/Listbox/Listbox.js +63 -20
- package/lib/cjs/Listbox/ListboxGroup.js +141 -9
- package/lib/cjs/Listbox/ListboxOverlay.js +13 -5
- package/lib/cjs/Listbox/PressableItem.js +8 -4
- package/lib/cjs/Listbox/SecondLevelHeader.js +201 -0
- package/lib/cjs/Listbox/dictionary.js +14 -0
- package/lib/cjs/Shortcuts/Shortcuts.js +169 -0
- package/lib/cjs/Shortcuts/ShortcutsItem.js +280 -0
- package/lib/cjs/Shortcuts/index.js +16 -0
- package/lib/cjs/TextInput/TextInputBase.js +5 -1
- package/lib/cjs/Tooltip/Tooltip.native.js +2 -0
- package/lib/cjs/Validator/Validator.js +171 -135
- package/lib/cjs/index.js +15 -0
- package/lib/esm/Button/Button.js +2 -0
- package/lib/esm/Button/ButtonBase.js +10 -5
- package/lib/esm/Button/ButtonDropdown.js +2 -0
- package/lib/esm/Button/ButtonGroup.js +44 -39
- package/lib/esm/Button/propTypes.js +6 -0
- package/lib/esm/Card/CardBase.js +97 -17
- package/lib/esm/Card/PressableCardBase.js +10 -8
- package/lib/esm/Carousel/Carousel.js +52 -19
- package/lib/esm/Carousel/CarouselItem/CarouselItem.js +23 -3
- package/lib/esm/HorizontalScroll/HorizontalScroll.js +6 -3
- package/lib/esm/Icon/Icon.js +11 -11
- package/lib/esm/Icon/IconText.js +0 -1
- package/lib/esm/Listbox/GroupControl.js +44 -44
- package/lib/esm/Listbox/Listbox.js +64 -21
- package/lib/esm/Listbox/ListboxGroup.js +143 -11
- package/lib/esm/Listbox/ListboxOverlay.js +13 -5
- package/lib/esm/Listbox/PressableItem.js +8 -4
- package/lib/esm/Listbox/SecondLevelHeader.js +194 -0
- package/lib/esm/Listbox/dictionary.js +8 -0
- package/lib/esm/Shortcuts/Shortcuts.js +160 -0
- package/lib/esm/Shortcuts/ShortcutsItem.js +273 -0
- package/lib/esm/Shortcuts/index.js +3 -0
- package/lib/esm/TextInput/TextInputBase.js +5 -1
- package/lib/esm/Tooltip/Tooltip.native.js +2 -0
- package/lib/esm/Validator/Validator.js +171 -135
- package/lib/esm/index.js +1 -0
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Button/Button.jsx +2 -1
- package/src/Button/ButtonBase.jsx +18 -12
- package/src/Button/ButtonDropdown.jsx +2 -0
- package/src/Button/ButtonGroup.jsx +62 -45
- package/src/Button/propTypes.js +6 -0
- package/src/Card/CardBase.jsx +113 -14
- package/src/Card/PressableCardBase.jsx +17 -5
- package/src/Carousel/Carousel.jsx +58 -5
- package/src/Carousel/CarouselItem/CarouselItem.jsx +31 -3
- package/src/HorizontalScroll/HorizontalScroll.jsx +6 -3
- package/src/Icon/Icon.jsx +14 -14
- package/src/Icon/IconText.jsx +0 -1
- package/src/Listbox/GroupControl.jsx +72 -70
- package/src/Listbox/Listbox.jsx +67 -11
- package/src/Listbox/ListboxGroup.jsx +160 -27
- package/src/Listbox/ListboxOverlay.jsx +23 -5
- package/src/Listbox/PressableItem.jsx +8 -4
- package/src/Listbox/SecondLevelHeader.jsx +182 -0
- package/src/Listbox/dictionary.js +8 -0
- package/src/Shortcuts/Shortcuts.jsx +174 -0
- package/src/Shortcuts/ShortcutsItem.jsx +297 -0
- package/src/Shortcuts/index.js +4 -0
- package/src/TextInput/TextInputBase.jsx +5 -1
- package/src/Tooltip/Tooltip.native.jsx +2 -1
- package/src/Validator/Validator.jsx +180 -159
- package/src/index.js +1 -0
- package/types/Listbox.d.ts +24 -0
- package/types/Shortcuts.d.ts +136 -0
- package/types/index.d.ts +12 -0
|
@@ -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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
}, (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
//
|
|
108
|
-
|
|
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
|
-
|
|
120
|
-
|
|
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
|
-
|
|
122
|
+
} else if (index === validatorsLength - 1) {
|
|
123
|
+
element?.blur();
|
|
123
124
|
}
|
|
124
|
-
}, [codeReferences,
|
|
125
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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[
|
|
149
|
-
validation: strValidation ||
|
|
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[
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
184
|
-
|
|
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 &&
|
|
189
|
-
element.value = mask;
|
|
226
|
+
if (mask && code) {
|
|
227
|
+
element.value = mask.substring(0, 1);
|
|
190
228
|
} else {
|
|
191
|
-
element.value =
|
|
229
|
+
element.value = code;
|
|
192
230
|
}
|
|
193
231
|
}
|
|
194
|
-
handleSingleCodes(prefix + i, text[i] ?? '', text[i] ? 'success' : '');
|
|
195
|
-
return null;
|
|
196
232
|
});
|
|
197
|
-
}, [
|
|
233
|
+
}, [codes, codeReferences, mask]);
|
|
234
|
+
|
|
235
|
+
// Setup event listeners - only runs once on mount
|
|
198
236
|
_react.default.useEffect(() => {
|
|
199
|
-
|
|
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
|
-
|
|
221
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
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: {
|
package/lib/cjs/index.js
CHANGED
|
@@ -59,6 +59,8 @@ var _exportNames = {
|
|
|
59
59
|
Responsive: true,
|
|
60
60
|
Search: true,
|
|
61
61
|
Select: true,
|
|
62
|
+
Shortcuts: true,
|
|
63
|
+
ShortcutsItem: true,
|
|
62
64
|
SideNav: true,
|
|
63
65
|
Skeleton: true,
|
|
64
66
|
SkipLink: true,
|
|
@@ -432,6 +434,18 @@ Object.defineProperty(exports, "Select", {
|
|
|
432
434
|
return _Select.default;
|
|
433
435
|
}
|
|
434
436
|
});
|
|
437
|
+
Object.defineProperty(exports, "Shortcuts", {
|
|
438
|
+
enumerable: true,
|
|
439
|
+
get: function () {
|
|
440
|
+
return _Shortcuts.default;
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
Object.defineProperty(exports, "ShortcutsItem", {
|
|
444
|
+
enumerable: true,
|
|
445
|
+
get: function () {
|
|
446
|
+
return _Shortcuts.ShortcutsItem;
|
|
447
|
+
}
|
|
448
|
+
});
|
|
435
449
|
Object.defineProperty(exports, "SideNav", {
|
|
436
450
|
enumerable: true,
|
|
437
451
|
get: function () {
|
|
@@ -744,6 +758,7 @@ var _RadioCard = _interopRequireWildcard(require("./RadioCard"));
|
|
|
744
758
|
var _Responsive = _interopRequireDefault(require("./Responsive"));
|
|
745
759
|
var _Search = _interopRequireDefault(require("./Search"));
|
|
746
760
|
var _Select = _interopRequireDefault(require("./Select"));
|
|
761
|
+
var _Shortcuts = _interopRequireWildcard(require("./Shortcuts"));
|
|
747
762
|
var _SideNav = _interopRequireDefault(require("./SideNav"));
|
|
748
763
|
var _Skeleton = _interopRequireDefault(require("./Skeleton"));
|
|
749
764
|
var _SkipLink = _interopRequireDefault(require("./SkipLink"));
|
package/lib/esm/Button/Button.js
CHANGED
|
@@ -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,
|
|
@@ -1,26 +1,18 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useCallback, useMemo } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import ABBPropTypes from 'airbnb-prop-types';
|
|
4
4
|
import Platform from "react-native-web/dist/exports/Platform";
|
|
5
|
+
import View from "react-native-web/dist/exports/View";
|
|
6
|
+
import StyleSheet from "react-native-web/dist/exports/StyleSheet";
|
|
5
7
|
import ButtonBase from './ButtonBase';
|
|
6
|
-
import { StackWrap } from '../StackView';
|
|
7
8
|
import Fieldset from '../Fieldset';
|
|
8
9
|
import { useViewport } from '../ViewportProvider';
|
|
9
10
|
import { useThemeTokens, useThemeTokensCallback } from '../ThemeProvider';
|
|
10
|
-
import { a11yProps, containUniqueFields, focusHandlerProps, pressProps, getTokensPropType, selectSystemProps,
|
|
11
|
+
import { useSpacingScale, a11yProps, containUniqueFields, focusHandlerProps, pressProps, getTokensPropType, selectSystemProps, useMultipleInputValues, variantProp, viewProps } from '../utils';
|
|
11
12
|
import { getPressHandlersWithArgs } from '../utils/pressability';
|
|
12
13
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
14
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
|
|
14
15
|
const [selectItemProps, selectedItemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, pressProps, viewProps]);
|
|
15
|
-
const getStackWrapTokens = variant => {
|
|
16
|
-
return Platform.select({
|
|
17
|
-
web: {
|
|
18
|
-
justifyContent: variant?.width === 'equal' ? 'space-evenly' : 'flex-start',
|
|
19
|
-
width: variant?.width === 'equal' ? '100%' : 'auto'
|
|
20
|
-
},
|
|
21
|
-
default: {}
|
|
22
|
-
});
|
|
23
|
-
};
|
|
24
16
|
const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
25
17
|
let {
|
|
26
18
|
variant,
|
|
@@ -53,12 +45,6 @@ const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
53
45
|
const themeTokens = useThemeTokens('ButtonGroup', tokens, variant, {
|
|
54
46
|
viewport
|
|
55
47
|
});
|
|
56
|
-
const themeStackTokens = selectTokens('StackView', themeTokens);
|
|
57
|
-
const variantStackTokens = getStackWrapTokens(variant);
|
|
58
|
-
const stackTokens = {
|
|
59
|
-
...themeStackTokens,
|
|
60
|
-
...variantStackTokens
|
|
61
|
-
};
|
|
62
48
|
const {
|
|
63
49
|
direction,
|
|
64
50
|
space,
|
|
@@ -68,15 +54,34 @@ const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
68
54
|
padding,
|
|
69
55
|
gap
|
|
70
56
|
} = themeTokens;
|
|
57
|
+
const isMobileNonContained = Platform.OS !== 'web' && (!variant || variant?.style !== 'contained');
|
|
71
58
|
const themeButtonTokensCallback = useThemeTokensCallback('ButtonGroupItem', tokens, variant);
|
|
72
|
-
const
|
|
59
|
+
const gapValue = useSpacingScale(gap || space);
|
|
60
|
+
const getButtonTokens = useCallback(state => {
|
|
73
61
|
const themeButtonTokens = themeButtonTokensCallback(state);
|
|
62
|
+
const shouldUseTransparentBackground = isMobileNonContained && !state.selected && !state.pressed && !state.hover && !state.focus;
|
|
74
63
|
return {
|
|
75
64
|
...themeButtonTokens,
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
...(variant?.width === 'equal' && staticStyles.equalWidth),
|
|
66
|
+
...(shouldUseTransparentBackground && {
|
|
67
|
+
backgroundColor: 'transparent'
|
|
68
|
+
}),
|
|
69
|
+
alignSelf: themeButtonTokens.width ? 'flex-start' : 'center'
|
|
78
70
|
};
|
|
79
|
-
};
|
|
71
|
+
}, [themeButtonTokensCallback, isMobileNonContained, variant?.width]);
|
|
72
|
+
const fieldsetStyles = useMemo(() => ({
|
|
73
|
+
...staticStyles.fieldsetBase,
|
|
74
|
+
borderRadius,
|
|
75
|
+
backgroundColor: isMobileNonContained ? 'transparent' : backgroundColor || 'transparent',
|
|
76
|
+
padding,
|
|
77
|
+
width: variant?.width === 'equal' ? '100%' : 'auto'
|
|
78
|
+
}), [borderRadius, backgroundColor, padding, variant?.width, isMobileNonContained]);
|
|
79
|
+
const viewStyles = useMemo(() => ({
|
|
80
|
+
...staticStyles.viewBase,
|
|
81
|
+
flexDirection: direction === 'column' ? 'column' : 'row',
|
|
82
|
+
gap: gapValue || 0,
|
|
83
|
+
justifyContent: variant?.width === 'equal' ? 'space-evenly' : 'flex-start'
|
|
84
|
+
}), [direction, gapValue, variant?.width]);
|
|
80
85
|
const {
|
|
81
86
|
currentValues,
|
|
82
87
|
toggleOneValue
|
|
@@ -113,25 +118,11 @@ const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
113
118
|
inactive: inactive,
|
|
114
119
|
validation: validation,
|
|
115
120
|
accessibilityRole: accessibilityRole,
|
|
116
|
-
style:
|
|
117
|
-
borderRadius,
|
|
118
|
-
backgroundColor,
|
|
119
|
-
padding,
|
|
120
|
-
...(Platform.OS === 'web' ? {
|
|
121
|
-
gap,
|
|
122
|
-
width: variant?.width === 'equal' ? '100%' : 'auto'
|
|
123
|
-
} : {
|
|
124
|
-
alignSelf: 'flex-start'
|
|
125
|
-
})
|
|
126
|
-
},
|
|
121
|
+
style: fieldsetStyles,
|
|
127
122
|
...selectProps(rest),
|
|
128
|
-
children: /*#__PURE__*/_jsx(
|
|
123
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
129
124
|
accessibilityRole: innerRole,
|
|
130
|
-
|
|
131
|
-
direction: direction,
|
|
132
|
-
tokens: stackTokens,
|
|
133
|
-
gap: gap,
|
|
134
|
-
ref: ref,
|
|
125
|
+
style: viewStyles,
|
|
135
126
|
children: items.map((_ref2, index) => {
|
|
136
127
|
let {
|
|
137
128
|
label,
|
|
@@ -291,4 +282,18 @@ ButtonGroup.propTypes = {
|
|
|
291
282
|
*/
|
|
292
283
|
copy: PropTypes.oneOf(['en', 'fr'])
|
|
293
284
|
};
|
|
285
|
+
const staticStyles = StyleSheet.create({
|
|
286
|
+
fieldsetBase: {
|
|
287
|
+
alignSelf: 'flex-start',
|
|
288
|
+
display: 'inline'
|
|
289
|
+
},
|
|
290
|
+
viewBase: {
|
|
291
|
+
flexWrap: 'wrap',
|
|
292
|
+
alignItems: 'center'
|
|
293
|
+
},
|
|
294
|
+
equalWidth: {
|
|
295
|
+
width: '100%',
|
|
296
|
+
flex: 1
|
|
297
|
+
}
|
|
298
|
+
});
|
|
294
299
|
export default ButtonGroup;
|
|
@@ -6,6 +6,12 @@ import { iconComponentPropTypes } from '../Icon';
|
|
|
6
6
|
export const textAndA11yText = ABBPropTypes.childrenOf(PropTypes.oneOfType([ABBPropTypes.elementType(A11yText), PropTypes.string]));
|
|
7
7
|
const buttonPropTypes = {
|
|
8
8
|
tokens: getTokensPropType('Button'),
|
|
9
|
+
/**
|
|
10
|
+
* If true, the button will honor the align-items value from its parent flex container.
|
|
11
|
+
* If false, the button will always align to 'flex-start' in a flex container.
|
|
12
|
+
* Currently, `heightFull`'s default behaviour will expand to the full height of its parent's flex container. In an upcoming major release, the default behaviour will be changed to expand based on the content. To maintain the expected behaviour, you must explicitly set `heightFull: true`
|
|
13
|
+
*/
|
|
14
|
+
heightFull: PropTypes.bool,
|
|
9
15
|
/**
|
|
10
16
|
* If true, prevents the button from being pressed, changes the cursor (on web) and accessibility
|
|
11
17
|
* attributes to communicate this to the user, and applies `inactive: true` appearances from the theme
|