@telus-uds/components-base 1.64.0 → 1.66.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 +22 -2
- package/lib/Checkbox/CheckboxButton.js +200 -0
- package/lib/CheckboxCard/CheckboxCard.js +225 -0
- package/lib/CheckboxCard/index.js +13 -0
- package/lib/StackView/StackView.js +4 -1
- package/lib/Tabs/Tabs.js +15 -0
- package/lib/TextInput/TextInputBase.js +102 -19
- package/lib/TextInput/propTypes.js +5 -0
- package/lib/index.js +9 -0
- package/lib/utils/props/textInputProps.js +1 -0
- package/lib-module/Checkbox/CheckboxButton.js +174 -0
- package/lib-module/CheckboxCard/CheckboxCard.js +200 -0
- package/lib-module/CheckboxCard/index.js +2 -0
- package/lib-module/StackView/StackView.js +4 -1
- package/lib-module/Tabs/Tabs.js +14 -0
- package/lib-module/TextInput/TextInputBase.js +101 -19
- package/lib-module/TextInput/propTypes.js +5 -0
- package/lib-module/index.js +1 -0
- package/lib-module/utils/props/textInputProps.js +1 -0
- package/package.json +2 -2
- package/src/Checkbox/CheckboxButton.jsx +180 -0
- package/src/CheckboxCard/CheckboxCard.jsx +190 -0
- package/src/CheckboxCard/index.js +3 -0
- package/src/StackView/StackView.jsx +2 -1
- package/src/Tabs/Tabs.jsx +14 -1
- package/src/TextInput/TextInputBase.jsx +67 -12
- package/src/TextInput/propTypes.js +4 -0
- package/src/index.js +1 -0
- package/src/utils/props/textInputProps.js +1 -0
package/lib-module/Tabs/Tabs.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { forwardRef, useCallback } from 'react';
|
|
2
|
+
import Platform from "react-native-web/dist/exports/Platform";
|
|
2
3
|
import PropTypes from 'prop-types';
|
|
3
4
|
import ABBPropTypes from 'airbnb-prop-types';
|
|
4
5
|
import { useThemeTokens } from '../ThemeProvider';
|
|
@@ -26,6 +27,17 @@ const getDefaultTabItemAccessibilityRole = parentRole => {
|
|
|
26
27
|
return undefined;
|
|
27
28
|
}
|
|
28
29
|
};
|
|
30
|
+
|
|
31
|
+
const getStackViewTokens = variant => {
|
|
32
|
+
const equalWidth = variant === null || variant === void 0 ? void 0 : variant.equalWidth;
|
|
33
|
+
return Platform.select({
|
|
34
|
+
web: {
|
|
35
|
+
justifyContent: equalWidth ? 'space-evenly' : 'flex-start',
|
|
36
|
+
width: equalWidth ? '100%' : 'auto'
|
|
37
|
+
},
|
|
38
|
+
default: {}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
29
41
|
/**
|
|
30
42
|
* Tabs renders a horizontally-scrolling menu of selectable buttons which may link
|
|
31
43
|
* to a page or control what content is displayed on this page.
|
|
@@ -76,6 +88,7 @@ const Tabs = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
76
88
|
const restProps = selectProps(rest);
|
|
77
89
|
const parentAccessibilityRole = restProps.accessibilityRole ?? 'tablist';
|
|
78
90
|
const defaultTabItemAccessibiltyRole = getDefaultTabItemAccessibilityRole(parentAccessibilityRole);
|
|
91
|
+
const stackViewTokens = getStackViewTokens(variant);
|
|
79
92
|
return /*#__PURE__*/_jsx(HorizontalScroll, {
|
|
80
93
|
ref: ref,
|
|
81
94
|
ScrollButton: HorizontalScrollButton,
|
|
@@ -87,6 +100,7 @@ const Tabs = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
87
100
|
children: /*#__PURE__*/_jsx(StackView, {
|
|
88
101
|
space: space,
|
|
89
102
|
direction: "row",
|
|
103
|
+
tokens: stackViewTokens,
|
|
90
104
|
children: items.map((_ref3, index) => {
|
|
91
105
|
let {
|
|
92
106
|
href,
|
|
@@ -7,13 +7,14 @@ import View from "react-native-web/dist/exports/View";
|
|
|
7
7
|
import { applyTextStyles, useTheme, useThemeTokens, applyOuterBorder } from '../ThemeProvider';
|
|
8
8
|
import StackView from '../StackView';
|
|
9
9
|
import IconButton from '../IconButton';
|
|
10
|
+
import Icon from '../Icon';
|
|
10
11
|
import { a11yProps, getTokensPropType, selectSystemProps, textInputHandlerProps, textInputProps, useCopy, useInputValue, useSpacingScale, variantProp, viewProps } from '../utils';
|
|
11
12
|
import dictionary from './dictionary';
|
|
12
13
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
14
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
15
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, textInputHandlerProps, textInputProps, viewProps]);
|
|
15
16
|
|
|
16
|
-
const selectInputStyles = (_ref, themeOptions, inactive) => {
|
|
17
|
+
const selectInputStyles = (_ref, themeOptions, inactive, type) => {
|
|
17
18
|
let {
|
|
18
19
|
backgroundColor,
|
|
19
20
|
color,
|
|
@@ -72,7 +73,7 @@ const selectInputStyles = (_ref, themeOptions, inactive) => {
|
|
|
72
73
|
borderWidth,
|
|
73
74
|
borderColor,
|
|
74
75
|
borderRadius,
|
|
75
|
-
paddingLeft: offsetBorder(paddingLeft),
|
|
76
|
+
paddingLeft: type === 'card' ? offsetBorder(paddingLeft + 34) : offsetBorder(paddingLeft),
|
|
76
77
|
paddingRight: icon ? offsetBorder(paddingWithIcon) : offsetBorder(paddingRight),
|
|
77
78
|
paddingTop: offsetBorder(paddingTop),
|
|
78
79
|
paddingBottom: offsetBorder(paddingBottom),
|
|
@@ -126,16 +127,64 @@ const selectIconContainerStyles = (_ref4, buttonCount) => {
|
|
|
126
127
|
};
|
|
127
128
|
};
|
|
128
129
|
|
|
129
|
-
const
|
|
130
|
+
const selectLeftIconContainerStyles = _ref5 => {
|
|
130
131
|
let {
|
|
131
|
-
|
|
132
|
+
leftIconPaddingBottom
|
|
132
133
|
} = _ref5;
|
|
134
|
+
return {
|
|
135
|
+
// not tokenizing paddingLeft as it remains same across brands for now
|
|
136
|
+
paddingLeft: 10,
|
|
137
|
+
paddingBottom: leftIconPaddingBottom
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const selectButtonsContainerStyle = _ref6 => {
|
|
142
|
+
let {
|
|
143
|
+
buttonsPaddingRight
|
|
144
|
+
} = _ref6;
|
|
133
145
|
return {
|
|
134
146
|
paddingRight: buttonsPaddingRight
|
|
135
147
|
};
|
|
136
148
|
};
|
|
137
149
|
|
|
138
|
-
const
|
|
150
|
+
const getIcon = function () {
|
|
151
|
+
let cardNumber = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
152
|
+
let {
|
|
153
|
+
defaultCreditIcon,
|
|
154
|
+
amexIcon,
|
|
155
|
+
visaIcon,
|
|
156
|
+
masterCardIcon
|
|
157
|
+
} = arguments.length > 1 ? arguments[1] : undefined;
|
|
158
|
+
const cardType = {
|
|
159
|
+
1: {
|
|
160
|
+
icon: visaIcon,
|
|
161
|
+
testID: 'visa'
|
|
162
|
+
},
|
|
163
|
+
2: {
|
|
164
|
+
icon: amexIcon,
|
|
165
|
+
testID: 'amex'
|
|
166
|
+
},
|
|
167
|
+
4: {
|
|
168
|
+
icon: masterCardIcon,
|
|
169
|
+
testID: 'mastercard'
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const firstDigit = cardNumber ? cardNumber[0] : '';
|
|
173
|
+
const defaultIcon = {
|
|
174
|
+
icon: defaultCreditIcon,
|
|
175
|
+
testID: 'default'
|
|
176
|
+
};
|
|
177
|
+
const selectedIcon = cardNumber.length > 4 ? cardType[firstDigit] || defaultIcon : defaultIcon;
|
|
178
|
+
return /*#__PURE__*/_jsx(Icon, {
|
|
179
|
+
icon: selectedIcon.icon,
|
|
180
|
+
variant: {
|
|
181
|
+
size: 'large'
|
|
182
|
+
},
|
|
183
|
+
testID: selectedIcon.testID
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const TextInputBase = /*#__PURE__*/forwardRef((_ref7, ref) => {
|
|
139
188
|
let {
|
|
140
189
|
buttons = [],
|
|
141
190
|
copy = 'en',
|
|
@@ -154,8 +203,9 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
154
203
|
tokens,
|
|
155
204
|
value,
|
|
156
205
|
variant = {},
|
|
206
|
+
type,
|
|
157
207
|
...rest
|
|
158
|
-
} =
|
|
208
|
+
} = _ref7;
|
|
159
209
|
const [isFocused, setIsFocused] = useState(false);
|
|
160
210
|
const [showPassword, setShowPassword] = useState(false);
|
|
161
211
|
|
|
@@ -196,6 +246,12 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
196
246
|
onChange,
|
|
197
247
|
readOnly
|
|
198
248
|
});
|
|
249
|
+
const {
|
|
250
|
+
password,
|
|
251
|
+
numeric
|
|
252
|
+
} = variant;
|
|
253
|
+
const isNumeric = numeric || type === 'card' || type === 'number';
|
|
254
|
+
const isPassword = password || type === 'password';
|
|
199
255
|
const element = inputRef === null || inputRef === void 0 ? void 0 : inputRef.current;
|
|
200
256
|
useEffect(() => {
|
|
201
257
|
if (Platform.OS === 'web' && pattern && element) {
|
|
@@ -208,11 +264,16 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
208
264
|
const handleChangeText = event => {
|
|
209
265
|
var _event$nativeEvent, _event$target;
|
|
210
266
|
|
|
211
|
-
const {
|
|
212
|
-
numeric
|
|
213
|
-
} = variant;
|
|
214
267
|
const text = ((_event$nativeEvent = event.nativeEvent) === null || _event$nativeEvent === void 0 ? void 0 : _event$nativeEvent.text) || ((_event$target = event.target) === null || _event$target === void 0 ? void 0 : _event$target.value);
|
|
215
|
-
|
|
268
|
+
let filteredText = isNumeric ? text === null || text === void 0 ? void 0 : text.replace(/[^\d]/g, '') : text;
|
|
269
|
+
|
|
270
|
+
if (type === 'card' && filteredText) {
|
|
271
|
+
const formattedValue = filteredText.replace(/[^a-zA-Z0-9]/g, '');
|
|
272
|
+
const regex = new RegExp(`([a-zA-Z0-9]{4})(?=[a-zA-Z0-9])`, 'g'); // Add a space every 4 digits starting from the 5th position
|
|
273
|
+
|
|
274
|
+
filteredText = formattedValue.replace(regex, '$1 ').trim();
|
|
275
|
+
}
|
|
276
|
+
|
|
216
277
|
setValue(filteredText, event);
|
|
217
278
|
if (typeof onChangeText === 'function') onChangeText(filteredText, event);
|
|
218
279
|
};
|
|
@@ -241,7 +302,11 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
241
302
|
clearButtonIcon: ClearButtonIcon,
|
|
242
303
|
icon: IconComponent,
|
|
243
304
|
passwordShowButtonIcon,
|
|
244
|
-
passwordHideButtonIcon
|
|
305
|
+
passwordHideButtonIcon,
|
|
306
|
+
defaultCreditIcon,
|
|
307
|
+
amexIcon,
|
|
308
|
+
visaIcon,
|
|
309
|
+
masterCardIcon
|
|
245
310
|
} = themeTokens;
|
|
246
311
|
const buttonsGapSize = useSpacingScale(buttonsGap);
|
|
247
312
|
const getCopy = useCopy({
|
|
@@ -261,7 +326,7 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
261
326
|
}, "clear"));
|
|
262
327
|
}
|
|
263
328
|
|
|
264
|
-
if (
|
|
329
|
+
if (isPassword) {
|
|
265
330
|
textInputButtons === null || textInputButtons === void 0 ? void 0 : textInputButtons.unshift( /*#__PURE__*/_jsx(IconButton, {
|
|
266
331
|
accessibilityLabel: !showPassword ? getCopy('hidePasswordAccessibilityLabel') : getCopy('showPasswordAccessibilityLabel'),
|
|
267
332
|
icon: !showPassword ? passwordShowButtonIcon : passwordHideButtonIcon,
|
|
@@ -287,6 +352,7 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
287
352
|
onMouseOut: handleMouseOut,
|
|
288
353
|
onChange: handleChangeText,
|
|
289
354
|
defaultValue: initialValue,
|
|
355
|
+
maxLength: type === 'card' ? 19 : undefined,
|
|
290
356
|
value: isControlled ? currentValue : undefined
|
|
291
357
|
};
|
|
292
358
|
const {
|
|
@@ -294,20 +360,29 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
294
360
|
} = useTheme();
|
|
295
361
|
const nativeInputStyle = selectInputStyles({ ...themeTokens,
|
|
296
362
|
height
|
|
297
|
-
}, themeOptions, inactive);
|
|
363
|
+
}, themeOptions, inactive, type);
|
|
298
364
|
return /*#__PURE__*/_jsxs(View, {
|
|
299
365
|
style: selectOuterBorderStyles(themeTokens),
|
|
300
|
-
children: [/*#__PURE__*/_jsx(
|
|
366
|
+
children: [type === 'card' && /*#__PURE__*/_jsx(View, {
|
|
367
|
+
pointerEvents: "none",
|
|
368
|
+
style: [staticStyles.leftIconContainer, selectLeftIconContainerStyles(themeTokens)],
|
|
369
|
+
children: getIcon(currentValue, {
|
|
370
|
+
defaultCreditIcon,
|
|
371
|
+
amexIcon,
|
|
372
|
+
visaIcon,
|
|
373
|
+
masterCardIcon
|
|
374
|
+
})
|
|
375
|
+
}), /*#__PURE__*/_jsx(NativeTextInput, {
|
|
301
376
|
ref: inputRef,
|
|
302
|
-
keyboardType:
|
|
303
|
-
inputMode:
|
|
377
|
+
keyboardType: isNumeric && 'numeric',
|
|
378
|
+
inputMode: isNumeric && 'numeric',
|
|
304
379
|
style: nativeInputStyle,
|
|
305
|
-
secureTextEntry:
|
|
380
|
+
secureTextEntry: isPassword && !showPassword,
|
|
306
381
|
...inputProps
|
|
307
382
|
}), IconComponent && /*#__PURE__*/_jsx(View, {
|
|
308
383
|
pointerEvents: "none" // avoid hijacking input press events
|
|
309
384
|
,
|
|
310
|
-
style: [staticStyles.
|
|
385
|
+
style: [staticStyles.rightIconContainer, selectIconContainerStyles({ ...themeTokens,
|
|
311
386
|
buttonsGapSize
|
|
312
387
|
}, buttons === null || buttons === void 0 ? void 0 : buttons.length)],
|
|
313
388
|
children: /*#__PURE__*/_jsx(IconComponent, { ...selectIconTokens(themeTokens)
|
|
@@ -333,6 +408,7 @@ TextInputBase.propTypes = { ...selectedSystemPropTypes,
|
|
|
333
408
|
copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), PropTypes.shape({
|
|
334
409
|
clearButtonAccessibilityLabel: PropTypes.string
|
|
335
410
|
})]),
|
|
411
|
+
type: PropTypes.oneOfType([PropTypes.oneOf(['password', 'card', 'number'])]),
|
|
336
412
|
height: PropTypes.number,
|
|
337
413
|
inactive: PropTypes.bool,
|
|
338
414
|
initialValue: PropTypes.string,
|
|
@@ -359,9 +435,15 @@ const staticStyles = StyleSheet.create({
|
|
|
359
435
|
bottom: 0,
|
|
360
436
|
justifyContent: 'center'
|
|
361
437
|
},
|
|
362
|
-
|
|
438
|
+
rightIconContainer: {
|
|
363
439
|
position: 'absolute',
|
|
364
440
|
right: 0,
|
|
365
441
|
bottom: 0
|
|
442
|
+
},
|
|
443
|
+
leftIconContainer: {
|
|
444
|
+
position: 'absolute',
|
|
445
|
+
left: 0,
|
|
446
|
+
bottom: 0,
|
|
447
|
+
zIndex: 1
|
|
366
448
|
}
|
|
367
449
|
});
|
|
@@ -8,6 +8,11 @@ const textInputPropTypes = {
|
|
|
8
8
|
*/
|
|
9
9
|
value: PropTypes.string,
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Use this to set the type of the input. Defaults to `text`.
|
|
13
|
+
*/
|
|
14
|
+
type: PropTypes.string,
|
|
15
|
+
|
|
11
16
|
/**
|
|
12
17
|
* Use this to set the initial value of an uncontrolled input.
|
|
13
18
|
* Updating `initialValue` will **not** update the actual value.
|
package/lib-module/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from './Carousel';
|
|
|
8
8
|
export { default as Listbox } from './Listbox';
|
|
9
9
|
export { default as Checkbox } from './Checkbox';
|
|
10
10
|
export * from './Checkbox';
|
|
11
|
+
export { default as CheckboxCard } from './CheckboxCard';
|
|
11
12
|
export { default as Divider } from './Divider';
|
|
12
13
|
export { default as ExpandCollapse, Accordion } from './ExpandCollapse';
|
|
13
14
|
export { default as Feedback } from './Feedback';
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"@floating-ui/react-native": "^0.8.1",
|
|
12
12
|
"@gorhom/portal": "^1.0.14",
|
|
13
13
|
"@telus-uds/system-constants": "^1.3.0",
|
|
14
|
-
"@telus-uds/system-theme-tokens": "^2.
|
|
14
|
+
"@telus-uds/system-theme-tokens": "^2.43.0",
|
|
15
15
|
"airbnb-prop-types": "^2.16.0",
|
|
16
16
|
"lodash.debounce": "^4.0.8",
|
|
17
17
|
"lodash.merge": "^4.6.2",
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"standard-engine": {
|
|
74
74
|
"skip": true
|
|
75
75
|
},
|
|
76
|
-
"version": "1.
|
|
76
|
+
"version": "1.66.0"
|
|
77
77
|
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import { Platform, StyleSheet, View } from 'react-native'
|
|
4
|
+
|
|
5
|
+
import CheckboxInput from './CheckboxInput'
|
|
6
|
+
import { applyShadowToken, useThemeTokens } from '../ThemeProvider'
|
|
7
|
+
import {
|
|
8
|
+
a11yProps,
|
|
9
|
+
getTokensSetPropType,
|
|
10
|
+
selectSystemProps,
|
|
11
|
+
selectTokens,
|
|
12
|
+
viewProps
|
|
13
|
+
} from '../utils'
|
|
14
|
+
|
|
15
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
16
|
+
|
|
17
|
+
const selectInputStyles = (
|
|
18
|
+
{
|
|
19
|
+
iconBackgroundColor,
|
|
20
|
+
inputBorderColor,
|
|
21
|
+
inputBorderRadius,
|
|
22
|
+
inputBorderWidth,
|
|
23
|
+
inputBackgroundColor,
|
|
24
|
+
inputHeight,
|
|
25
|
+
inputOutlineColor,
|
|
26
|
+
inputOutlineWidth,
|
|
27
|
+
inputOutlineOffset,
|
|
28
|
+
inputShadow,
|
|
29
|
+
inputWidth
|
|
30
|
+
},
|
|
31
|
+
isChecked
|
|
32
|
+
) => ({
|
|
33
|
+
borderColor: inputBorderColor,
|
|
34
|
+
borderWidth: inputBorderWidth,
|
|
35
|
+
borderRadius: inputBorderRadius,
|
|
36
|
+
backgroundColor: isChecked ? iconBackgroundColor : inputBackgroundColor,
|
|
37
|
+
height: inputHeight,
|
|
38
|
+
width: inputWidth,
|
|
39
|
+
...applyShadowToken(inputShadow),
|
|
40
|
+
...Platform.select({
|
|
41
|
+
web: {
|
|
42
|
+
outlineStyle: 'solid',
|
|
43
|
+
outlineColor: inputOutlineColor,
|
|
44
|
+
outlineWidth: inputOutlineWidth,
|
|
45
|
+
outlineOffset: inputOutlineOffset
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const selectIconTokens = ({ icon, iconColor, iconSize }) => ({
|
|
51
|
+
icon,
|
|
52
|
+
color: iconColor,
|
|
53
|
+
size: iconSize
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const tokenKeys = [
|
|
57
|
+
'checkedBackgroundColor',
|
|
58
|
+
'checkedSize',
|
|
59
|
+
'inputBackgroundColor',
|
|
60
|
+
'inputBorderColor',
|
|
61
|
+
'inputBorderWidth',
|
|
62
|
+
'inputOutlineColor',
|
|
63
|
+
'inputOutlineWidth',
|
|
64
|
+
'inputSize',
|
|
65
|
+
'outerBorderColor',
|
|
66
|
+
'outerBorderWidth',
|
|
67
|
+
'outerBorderGap',
|
|
68
|
+
'inputSize'
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
export const selectCheckboxTokens = (themeTokens, prefix) =>
|
|
72
|
+
selectTokens(tokenKeys, themeTokens, prefix)
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The visual toggle of a checkbox input. Is not interactive on its own, should be used inside
|
|
76
|
+
* an interactive parent that passes down props when interacted with. Used in CheckboxCard
|
|
77
|
+
*/
|
|
78
|
+
const CheckboxButton = forwardRef(
|
|
79
|
+
(
|
|
80
|
+
{
|
|
81
|
+
isChecked,
|
|
82
|
+
tokens,
|
|
83
|
+
inactive,
|
|
84
|
+
defaultChecked,
|
|
85
|
+
inputId,
|
|
86
|
+
iconId,
|
|
87
|
+
isControlled,
|
|
88
|
+
handleChange,
|
|
89
|
+
name: inputName,
|
|
90
|
+
value,
|
|
91
|
+
...rest
|
|
92
|
+
},
|
|
93
|
+
ref
|
|
94
|
+
) => {
|
|
95
|
+
const { icon: IconComponent, ...stateTokens } = useThemeTokens(
|
|
96
|
+
'Checkbox',
|
|
97
|
+
{},
|
|
98
|
+
{ checked: isChecked }
|
|
99
|
+
)
|
|
100
|
+
const iconTokens = selectIconTokens(stateTokens)
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<View {...selectProps(rest)}>
|
|
104
|
+
<View
|
|
105
|
+
style={[staticStyles.defaultInputStyles, selectInputStyles(stateTokens, isChecked)]}
|
|
106
|
+
testID={inputId}
|
|
107
|
+
>
|
|
108
|
+
<CheckboxInput
|
|
109
|
+
ref={ref}
|
|
110
|
+
checked={isChecked}
|
|
111
|
+
defaultChecked={defaultChecked}
|
|
112
|
+
disabled={inactive}
|
|
113
|
+
id={inputId}
|
|
114
|
+
isControlled={isControlled}
|
|
115
|
+
onChange={handleChange}
|
|
116
|
+
name={inputName}
|
|
117
|
+
value={value}
|
|
118
|
+
/>
|
|
119
|
+
{isChecked && IconComponent && (
|
|
120
|
+
<View testID={iconId}>
|
|
121
|
+
<IconComponent {...iconTokens} />
|
|
122
|
+
</View>
|
|
123
|
+
)}
|
|
124
|
+
</View>
|
|
125
|
+
</View>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
CheckboxButton.displayName = 'CheckboxButton'
|
|
130
|
+
|
|
131
|
+
CheckboxButton.propTypes = {
|
|
132
|
+
...selectedSystemPropTypes,
|
|
133
|
+
/**
|
|
134
|
+
* Use `checked` for controlled Checkbox. For uncontrolled Checkbox, use the `defaultChecked` prop.
|
|
135
|
+
*/
|
|
136
|
+
isChecked: PropTypes.bool,
|
|
137
|
+
/**
|
|
138
|
+
* Checkbox tokens.
|
|
139
|
+
*/
|
|
140
|
+
tokens: getTokensSetPropType(tokenKeys, { allowFunction: true }),
|
|
141
|
+
/**
|
|
142
|
+
* Whether the corresponding input is disabled or active.
|
|
143
|
+
*/
|
|
144
|
+
inactive: PropTypes.bool,
|
|
145
|
+
/**
|
|
146
|
+
* Use `defaultChecked` to provide the initial value for an uncontrolled Checkbox.
|
|
147
|
+
*/
|
|
148
|
+
defaultChecked: PropTypes.bool,
|
|
149
|
+
/**
|
|
150
|
+
* Checkbox input ID.
|
|
151
|
+
*/
|
|
152
|
+
inputId: PropTypes.string,
|
|
153
|
+
/**
|
|
154
|
+
* Checkbox icon ID.
|
|
155
|
+
*/
|
|
156
|
+
iconId: PropTypes.string,
|
|
157
|
+
/**
|
|
158
|
+
* Can control the checkbox on the card.
|
|
159
|
+
*/
|
|
160
|
+
isControlled: PropTypes.bool,
|
|
161
|
+
/**
|
|
162
|
+
* Callback called when a controlled checkbox gets interacted with.
|
|
163
|
+
*/
|
|
164
|
+
handleChange: PropTypes.func,
|
|
165
|
+
/**
|
|
166
|
+
* Associate this checkbox with a group (set as the name attribute).
|
|
167
|
+
*/
|
|
168
|
+
name: PropTypes.string,
|
|
169
|
+
/**
|
|
170
|
+
* The value of the checkbox: true or false
|
|
171
|
+
*/
|
|
172
|
+
value: PropTypes.bool
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export default CheckboxButton
|
|
176
|
+
|
|
177
|
+
const staticStyles = StyleSheet.create({
|
|
178
|
+
container: { flexDirection: 'row', alignItems: 'center' },
|
|
179
|
+
defaultInputStyles: { alignItems: 'center', justifyContent: 'center' }
|
|
180
|
+
})
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import { StyleSheet, Text, View } from 'react-native'
|
|
4
|
+
|
|
5
|
+
import { applyTextStyles, useTheme, useThemeTokensCallback } from '../ThemeProvider'
|
|
6
|
+
import {
|
|
7
|
+
a11yProps,
|
|
8
|
+
focusHandlerProps,
|
|
9
|
+
getTokensPropType,
|
|
10
|
+
selectSystemProps,
|
|
11
|
+
selectTokens,
|
|
12
|
+
useInputValue,
|
|
13
|
+
useUniqueId,
|
|
14
|
+
variantProp,
|
|
15
|
+
viewProps
|
|
16
|
+
} from '../utils'
|
|
17
|
+
import { PressableCardBase, selectPressableCardTokens } from '../Card'
|
|
18
|
+
import StackView from '../StackView'
|
|
19
|
+
import CheckboxButton, { selectCheckboxTokens } from '../Checkbox/CheckboxButton'
|
|
20
|
+
|
|
21
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
22
|
+
a11yProps,
|
|
23
|
+
focusHandlerProps,
|
|
24
|
+
viewProps
|
|
25
|
+
])
|
|
26
|
+
|
|
27
|
+
const CheckboxCard = forwardRef(
|
|
28
|
+
(
|
|
29
|
+
{
|
|
30
|
+
tokens,
|
|
31
|
+
variant,
|
|
32
|
+
title,
|
|
33
|
+
children,
|
|
34
|
+
inactive,
|
|
35
|
+
defaultChecked,
|
|
36
|
+
checked,
|
|
37
|
+
name: inputName,
|
|
38
|
+
value,
|
|
39
|
+
id,
|
|
40
|
+
iconId,
|
|
41
|
+
onChange,
|
|
42
|
+
...rest
|
|
43
|
+
},
|
|
44
|
+
ref
|
|
45
|
+
) => {
|
|
46
|
+
const {
|
|
47
|
+
currentValue: isChecked,
|
|
48
|
+
setValue: setIsChecked,
|
|
49
|
+
isControlled
|
|
50
|
+
} = useInputValue({
|
|
51
|
+
value: checked,
|
|
52
|
+
initialValue: defaultChecked,
|
|
53
|
+
onChange
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const handleChange = (event) => {
|
|
57
|
+
if (!inactive) {
|
|
58
|
+
setIsChecked(!isChecked, event)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const uniqueId = useUniqueId('CheckboxCard')
|
|
62
|
+
const inputId = id ?? uniqueId
|
|
63
|
+
|
|
64
|
+
const uniqueIconId = useUniqueId('CheckboxIcon')
|
|
65
|
+
const iconCheckboxId = iconId ?? uniqueIconId
|
|
66
|
+
|
|
67
|
+
const getTokens = useThemeTokensCallback('CheckboxCard', tokens, variant)
|
|
68
|
+
const getCardTokens = (cardState) => selectPressableCardTokens(getTokens(cardState))
|
|
69
|
+
const { themeOptions } = useTheme()
|
|
70
|
+
|
|
71
|
+
const selectedProps = selectProps({
|
|
72
|
+
accessibilityRole: 'checkbox',
|
|
73
|
+
accessibilityState: { checked: isChecked, disabled: inactive },
|
|
74
|
+
...rest
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<PressableCardBase
|
|
79
|
+
ref={ref}
|
|
80
|
+
inactive={inactive}
|
|
81
|
+
checked={isChecked}
|
|
82
|
+
tokens={getCardTokens}
|
|
83
|
+
onPress={handleChange}
|
|
84
|
+
{...selectedProps}
|
|
85
|
+
>
|
|
86
|
+
{(cardState) => {
|
|
87
|
+
const { checkboxSpace, contentSpace, ...themeTokens } = getTokens(cardState)
|
|
88
|
+
const checkboxTokens = selectCheckboxTokens(themeTokens, 'checkbox')
|
|
89
|
+
const titleTokens = selectTokens('Typography', themeTokens)
|
|
90
|
+
const textStyle = applyTextStyles({ ...titleTokens, themeOptions })
|
|
91
|
+
return (
|
|
92
|
+
<StackView direction="row" space={checkboxSpace}>
|
|
93
|
+
<View style={[staticStyles.alignWithText, { height: textStyle.lineHeight }]}>
|
|
94
|
+
<CheckboxButton
|
|
95
|
+
tokens={checkboxTokens}
|
|
96
|
+
isControlled={isControlled}
|
|
97
|
+
isChecked={isChecked}
|
|
98
|
+
inactive={inactive}
|
|
99
|
+
defaultChecked={defaultChecked}
|
|
100
|
+
inputId={inputId}
|
|
101
|
+
iconId={iconCheckboxId}
|
|
102
|
+
handleChange={handleChange}
|
|
103
|
+
name={inputName}
|
|
104
|
+
value={value}
|
|
105
|
+
/>
|
|
106
|
+
</View>
|
|
107
|
+
<StackView direction="column" space={contentSpace} tokens={{ flexShrink: 1 }}>
|
|
108
|
+
{title ? <Text style={textStyle}>{title}</Text> : null}
|
|
109
|
+
{typeof children === 'function' ? children(cardState, textStyle) : children}
|
|
110
|
+
</StackView>
|
|
111
|
+
</StackView>
|
|
112
|
+
)
|
|
113
|
+
}}
|
|
114
|
+
</PressableCardBase>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
CheckboxCard.displayName = 'CheckboxCard'
|
|
119
|
+
|
|
120
|
+
CheckboxCard.propTypes = {
|
|
121
|
+
...selectedSystemPropTypes,
|
|
122
|
+
/**
|
|
123
|
+
* Content to be displayed at the top of the card alongside the checkbox
|
|
124
|
+
*/
|
|
125
|
+
title: PropTypes.string,
|
|
126
|
+
/**
|
|
127
|
+
* Additional content to be displayed below the checkbox.
|
|
128
|
+
*/
|
|
129
|
+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
|
130
|
+
/**
|
|
131
|
+
* Use `checked` for controlled checkbox. For uncontrolled checkbox, use the `defaultChecked` prop.
|
|
132
|
+
*/
|
|
133
|
+
checked: PropTypes.bool,
|
|
134
|
+
/**
|
|
135
|
+
* Use `defaultChecked` to provide the initial value for an uncontrolled checkbox.
|
|
136
|
+
*/
|
|
137
|
+
defaultChecked: PropTypes.bool,
|
|
138
|
+
/**
|
|
139
|
+
* An optional checkboxdescription.
|
|
140
|
+
*/
|
|
141
|
+
description: PropTypes.string,
|
|
142
|
+
/**
|
|
143
|
+
* Checkbox card ID.
|
|
144
|
+
*/
|
|
145
|
+
id: PropTypes.string,
|
|
146
|
+
/**
|
|
147
|
+
* Checkbox icon ID.
|
|
148
|
+
*/
|
|
149
|
+
iconId: PropTypes.string,
|
|
150
|
+
/**
|
|
151
|
+
* Whether the corresponding input is disabled or active.
|
|
152
|
+
*/
|
|
153
|
+
inactive: PropTypes.bool,
|
|
154
|
+
/**
|
|
155
|
+
* The label.
|
|
156
|
+
*/
|
|
157
|
+
label: PropTypes.string,
|
|
158
|
+
/**
|
|
159
|
+
* Associate this checkbox card with a group (set as the name attribute).
|
|
160
|
+
*/
|
|
161
|
+
name: PropTypes.string,
|
|
162
|
+
/**
|
|
163
|
+
* Whether the underlying input triggered a validation error or not.
|
|
164
|
+
*/
|
|
165
|
+
error: PropTypes.bool,
|
|
166
|
+
/**
|
|
167
|
+
* The value. Must be unique within the group.
|
|
168
|
+
*/
|
|
169
|
+
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
|
|
170
|
+
/**
|
|
171
|
+
* Callback called when a controlled checkbox card gets interacted with.
|
|
172
|
+
*/
|
|
173
|
+
onChange: PropTypes.func,
|
|
174
|
+
/**
|
|
175
|
+
* checkbox card tokens.
|
|
176
|
+
*/
|
|
177
|
+
tokens: getTokensPropType('CheckboxCard'),
|
|
178
|
+
/**
|
|
179
|
+
* checkbox variant.
|
|
180
|
+
*/
|
|
181
|
+
variant: variantProp.propType
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export default CheckboxCard
|
|
185
|
+
|
|
186
|
+
const staticStyles = StyleSheet.create({
|
|
187
|
+
alignWithText: {
|
|
188
|
+
justifyContent: 'center'
|
|
189
|
+
}
|
|
190
|
+
})
|
|
@@ -89,9 +89,10 @@ const StackView = forwardRef(
|
|
|
89
89
|
const content = getStackedContent(children, { direction, divider, space })
|
|
90
90
|
const themeTokens = useThemeTokens('StackView', tokens, variant, { viewport })
|
|
91
91
|
const flexStyles = selectFlexStyles(themeTokens)
|
|
92
|
+
const size = { width: themeTokens.width }
|
|
92
93
|
|
|
93
94
|
return (
|
|
94
|
-
<View ref={ref} {...selectedProps} style={[flexStyles, staticStyles[direction]]}>
|
|
95
|
+
<View ref={ref} {...selectedProps} style={[flexStyles, staticStyles[direction], size]}>
|
|
95
96
|
{content}
|
|
96
97
|
</View>
|
|
97
98
|
)
|