@telus-uds/components-base 1.85.1 → 1.87.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 (59) hide show
  1. package/CHANGELOG.md +34 -2
  2. package/lib/ActionCard/ActionCard.js +350 -0
  3. package/lib/ActionCard/index.js +10 -0
  4. package/lib/Card/Card.js +3 -3
  5. package/lib/Card/PressableCardBase.js +1 -1
  6. package/lib/Checkbox/Checkbox.js +4 -0
  7. package/lib/Link/TextButton.js +17 -7
  8. package/lib/List/List.js +9 -2
  9. package/lib/List/ListItemContent.js +3 -2
  10. package/lib/Modal/ModalContent.js +6 -8
  11. package/lib/Modal/WebModal.js +2 -1
  12. package/lib/Notification/Notification.js +20 -15
  13. package/lib/Radio/Radio.js +4 -0
  14. package/lib/Select/Picker.js +4 -0
  15. package/lib/Select/Select.js +1 -1
  16. package/lib/Tabs/Tabs.js +4 -1
  17. package/lib/Tabs/TabsItem.js +7 -1
  18. package/lib/index.js +8 -0
  19. package/lib/utils/animation/useVerticalExpandAnimation.js +3 -3
  20. package/lib/utils/props/tokens.js +2 -2
  21. package/lib-module/ActionCard/ActionCard.js +343 -0
  22. package/lib-module/ActionCard/index.js +2 -0
  23. package/lib-module/Card/Card.js +4 -4
  24. package/lib-module/Card/PressableCardBase.js +1 -1
  25. package/lib-module/Checkbox/Checkbox.js +4 -0
  26. package/lib-module/Link/TextButton.js +17 -7
  27. package/lib-module/List/List.js +9 -2
  28. package/lib-module/List/ListItemContent.js +3 -2
  29. package/lib-module/Modal/ModalContent.js +6 -8
  30. package/lib-module/Modal/WebModal.js +2 -1
  31. package/lib-module/Notification/Notification.js +20 -15
  32. package/lib-module/Radio/Radio.js +4 -0
  33. package/lib-module/Select/Picker.js +5 -1
  34. package/lib-module/Select/Select.js +2 -2
  35. package/lib-module/Tabs/Tabs.js +4 -1
  36. package/lib-module/Tabs/TabsItem.js +7 -1
  37. package/lib-module/index.js +1 -0
  38. package/lib-module/utils/animation/useVerticalExpandAnimation.js +3 -3
  39. package/lib-module/utils/props/tokens.js +2 -2
  40. package/package.json +2 -2
  41. package/src/ActionCard/ActionCard.jsx +306 -0
  42. package/src/ActionCard/index.js +3 -0
  43. package/src/Card/Card.jsx +6 -4
  44. package/src/Card/PressableCardBase.jsx +1 -1
  45. package/src/Checkbox/Checkbox.jsx +3 -1
  46. package/src/Link/TextButton.jsx +17 -9
  47. package/src/List/List.jsx +7 -2
  48. package/src/List/ListItemContent.jsx +2 -2
  49. package/src/Modal/ModalContent.jsx +3 -5
  50. package/src/Modal/WebModal.jsx +2 -1
  51. package/src/Notification/Notification.jsx +36 -16
  52. package/src/Radio/Radio.jsx +3 -1
  53. package/src/Select/Picker.jsx +6 -1
  54. package/src/Select/Select.jsx +4 -2
  55. package/src/Tabs/Tabs.jsx +4 -1
  56. package/src/Tabs/TabsItem.jsx +5 -1
  57. package/src/index.js +1 -0
  58. package/src/utils/animation/useVerticalExpandAnimation.js +3 -3
  59. package/src/utils/props/tokens.js +4 -2
@@ -60,13 +60,17 @@ const selectDismissButtonContainerStyles = _ref4 => {
60
60
  } = _ref4;
61
61
  return {
62
62
  paddingLeft: dismissButtonGap,
63
- placeContent: 'center'
63
+ placeContent: 'start'
64
64
  };
65
65
  };
66
- const selectContentContainerStyle = maxWidth => ({
67
- maxWidth: maxWidth || '100%'
66
+ const selectContentContainerStyle = (themeTokens, maxWidth, viewport, system) => ({
67
+ maxWidth: viewport === 'xl' && system === true ? maxWidth : 'auto',
68
+ minWidth: viewport === 'xl' && system === true ? maxWidth : 'auto',
69
+ paddingRight: themeTokens === null || themeTokens === void 0 ? void 0 : themeTokens.containerPaddingRight,
70
+ paddingLeft: themeTokens === null || themeTokens === void 0 ? void 0 : themeTokens.containerPaddingLeft
68
71
  });
69
- const getMediaQueryStyles = (themeTokens, themeOptions, viewport, mediaIdsRef, dismissible) => {
72
+ const getMediaQueryStyles = (themeTokens, themeOptions, mediaIdsRef, dismissible, viewport, system) => {
73
+ var _themeOptions$content, _themeOptions$content2, _themeOptions$content3, _themeOptions$content4, _themeOptions$content5;
70
74
  const transformedSelectContainerStyles = Object.entries(themeTokens).reduce((acc, _ref5) => {
71
75
  let [vp, viewportTokens] = _ref5;
72
76
  acc[vp] = selectContainerStyles({
@@ -80,7 +84,7 @@ const getMediaQueryStyles = (themeTokens, themeOptions, viewport, mediaIdsRef, d
80
84
  styles: containerStyles
81
85
  } = StyleSheet.create({
82
86
  container: {
83
- flexDirection: 'row',
87
+ flexDirection: system === true && viewport === 'xl' ? 'row' : 'inherit',
84
88
  ...selectContainerMediaQueryStyles
85
89
  }
86
90
  });
@@ -94,19 +98,19 @@ const getMediaQueryStyles = (themeTokens, themeOptions, viewport, mediaIdsRef, d
94
98
  justifyContent: 'space-between',
95
99
  ...createMediaQueryStyles({
96
100
  xs: {
97
- width: (themeOptions === null || themeOptions === void 0 ? void 0 : themeOptions.contentMaxWidth.xs) || '100%'
101
+ width: (themeOptions === null || themeOptions === void 0 ? void 0 : (_themeOptions$content = themeOptions.contentMaxWidth) === null || _themeOptions$content === void 0 ? void 0 : _themeOptions$content.xs) || '100%'
98
102
  },
99
103
  md: {
100
- width: (themeOptions === null || themeOptions === void 0 ? void 0 : themeOptions.contentMaxWidth.md) || '100%'
104
+ width: (themeOptions === null || themeOptions === void 0 ? void 0 : (_themeOptions$content2 = themeOptions.contentMaxWidth) === null || _themeOptions$content2 === void 0 ? void 0 : _themeOptions$content2.md) || '100%'
101
105
  },
102
106
  lg: {
103
- width: (themeOptions === null || themeOptions === void 0 ? void 0 : themeOptions.contentMaxWidth.lg) || '100%'
107
+ width: (themeOptions === null || themeOptions === void 0 ? void 0 : (_themeOptions$content3 = themeOptions.contentMaxWidth) === null || _themeOptions$content3 === void 0 ? void 0 : _themeOptions$content3.lg) || '100%'
104
108
  },
105
109
  sm: {
106
- width: (themeOptions === null || themeOptions === void 0 ? void 0 : themeOptions.contentMaxWidth.sm) || '100%'
110
+ width: (themeOptions === null || themeOptions === void 0 ? void 0 : (_themeOptions$content4 = themeOptions.contentMaxWidth) === null || _themeOptions$content4 === void 0 ? void 0 : _themeOptions$content4.sm) || '100%'
107
111
  },
108
112
  xl: {
109
- width: (themeOptions === null || themeOptions === void 0 ? void 0 : themeOptions.contentMaxWidth.xl) || '100%'
113
+ width: (themeOptions === null || themeOptions === void 0 ? void 0 : (_themeOptions$content5 = themeOptions.contentMaxWidth) === null || _themeOptions$content5 === void 0 ? void 0 : _themeOptions$content5.xl) || '100%'
110
114
  }
111
115
  })
112
116
  }
@@ -169,19 +173,20 @@ const getMediaQueryStyles = (themeTokens, themeOptions, viewport, mediaIdsRef, d
169
173
  selectDismissIconPropsStyles
170
174
  };
171
175
  };
172
- const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible) => ({
176
+ const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, viewport, system) => ({
173
177
  containerStyles: {
174
178
  container: {
175
- flexDirection: 'row',
179
+ flexDirection: system === true && viewport === 'xl' ? 'row' : 'inherit',
176
180
  ...selectContainerStyles(themeTokens)
177
181
  }
178
182
  },
179
183
  contentContainerStyles: {
180
184
  contentContainer: {
185
+ flex: 1,
181
186
  flexDirection: 'row',
182
187
  flexShrink: 1,
183
188
  justifyContent: 'space-between',
184
- ...selectContentContainerStyle(maxWidth)
189
+ ...selectContentContainerStyle(themeTokens, maxWidth, viewport, system)
185
190
  }
186
191
  },
187
192
  staticContentContainerStyles: {
@@ -317,9 +322,9 @@ const Notification = /*#__PURE__*/React.forwardRef((_ref6, ref) => {
317
322
  selectDismissIconPropsIds: {}
318
323
  });
319
324
  if (enableMediaQueryStyleSheet) {
320
- notificationComponentRef.current = getMediaQueryStyles(themeTokens, themeOptions, viewport, mediaIdsRef, dismissible);
325
+ notificationComponentRef.current = getMediaQueryStyles(themeTokens, themeOptions, mediaIdsRef, dismissible, viewport, system);
321
326
  } else {
322
- notificationComponentRef.current = getDefaultStyles(themeTokens, themeOptions, maxWidth, dismissible);
327
+ notificationComponentRef.current = getDefaultStyles(themeTokens, themeOptions, maxWidth, dismissible, viewport, system);
323
328
  }
324
329
  if (isDismissed) {
325
330
  return null;
@@ -164,6 +164,7 @@ const Radio = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
164
164
  onKeyDown: handleKeyDown,
165
165
  onPress: handleChange,
166
166
  ...selectedProps,
167
+ style: staticStyles.removeOutline,
167
168
  children: _ref5 => {
168
169
  let {
169
170
  focused: focus,
@@ -275,5 +276,8 @@ const staticStyles = StyleSheet.create({
275
276
  alignWithLabel: {
276
277
  alignSelf: 'flex-start',
277
278
  justifyContent: 'center'
279
+ },
280
+ removeOutline: {
281
+ outlineStyle: 'none'
278
282
  }
279
283
  });
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { componentPropType } from '../utils';
3
+ import { componentPropType, selectSystemProps, htmlAttrs } from '../utils';
4
4
  import { jsx as _jsx } from "react/jsx-runtime";
5
5
  import { jsxs as _jsxs } from "react/jsx-runtime";
6
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs]);
6
7
  const Picker = /*#__PURE__*/React.forwardRef((_ref, ref) => {
7
8
  let {
8
9
  value,
@@ -19,6 +20,7 @@ const Picker = /*#__PURE__*/React.forwardRef((_ref, ref) => {
19
20
  testID,
20
21
  ...rest
21
22
  } = _ref;
23
+ const selectedProps = selectProps(rest);
22
24
  const {
23
25
  accessibilityLabel,
24
26
  accessibilityDescribedBy,
@@ -39,6 +41,7 @@ const Picker = /*#__PURE__*/React.forwardRef((_ref, ref) => {
39
41
  "aria-describedby": accessibilityDescribedBy,
40
42
  "aria-invalid": accessibilityInvalid,
41
43
  "data-testid": testID,
44
+ ...selectedProps,
42
45
  children: [placeholder !== undefined && /*#__PURE__*/_jsx("option", {
43
46
  value: placeholder.value,
44
47
  disabled: true,
@@ -50,6 +53,7 @@ const Picker = /*#__PURE__*/React.forwardRef((_ref, ref) => {
50
53
  Picker.displayName = 'Picker';
51
54
  export default Picker;
52
55
  Picker.propTypes = {
56
+ ...selectedSystemPropTypes,
53
57
  value: PropTypes.string,
54
58
  onChange: PropTypes.func,
55
59
  onFocus: PropTypes.func,
@@ -4,13 +4,13 @@ import Platform from "react-native-web/dist/exports/Platform";
4
4
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
5
5
  import PropTypes from 'prop-types';
6
6
  import { applyTextStyles, useThemeTokens, applyOuterBorder, useTheme } from '../ThemeProvider';
7
- import { a11yProps, componentPropType, getTokensPropType, inputSupportsProps, selectSystemProps, useInputValue, variantProp, viewProps } from '../utils';
7
+ import { a11yProps, componentPropType, getTokensPropType, inputSupportsProps, selectSystemProps, useInputValue, variantProp, viewProps, htmlAttrs } from '../utils';
8
8
  import Picker from './Picker';
9
9
  import InputSupports from '../InputSupports';
10
10
  import { ANDROID_VALIDATION_ICON_CONTAINER_OFFSET } from './constants';
11
11
  import { jsx as _jsx } from "react/jsx-runtime";
12
12
  import { jsxs as _jsxs } from "react/jsx-runtime";
13
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, inputSupportsProps, viewProps]);
13
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, inputSupportsProps, viewProps, htmlAttrs]);
14
14
  const selectInputStyles = (_ref, themeOptions, inactive) => {
15
15
  let {
16
16
  backgroundColor,
@@ -106,6 +106,7 @@ const Tabs = /*#__PURE__*/React.forwardRef((_ref, ref) => {
106
106
  ref: itemRef,
107
107
  LinkRouter: ItemLinkRouter = LinkRouter,
108
108
  linkRouterProps: itemLinkRouterProps,
109
+ render,
109
110
  ...itemRest
110
111
  } = _ref3;
111
112
  const itemId = id ?? label;
@@ -131,6 +132,7 @@ const Tabs = /*#__PURE__*/React.forwardRef((_ref, ref) => {
131
132
  ...linkRouterProps,
132
133
  ...itemLinkRouterProps
133
134
  },
135
+ render: render,
134
136
  ...itemProps,
135
137
  children: label
136
138
  }, itemId);
@@ -151,7 +153,8 @@ Tabs.propTypes = {
151
153
  href: PropTypes.string,
152
154
  label: PropTypes.string,
153
155
  id: PropTypes.string,
154
- ref: ABBPropTypes.ref()
156
+ ref: ABBPropTypes.ref(),
157
+ render: PropTypes.func
155
158
  })),
156
159
  /**
157
160
  * `id` property of the current tab in the items array
@@ -95,6 +95,7 @@ const TabsItem = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
95
95
  selected
96
96
  } : undefined,
97
97
  id,
98
+ render,
98
99
  ...rawRest
99
100
  } = _ref4;
100
101
  // Convert onClick etc to onPress etc if used in an integration
@@ -150,6 +151,10 @@ const TabsItem = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
150
151
  }
151
152
  // itemPositions is a ref object so this should only re-run when `selected` (or `index`) change
152
153
  }, [selected, index, itemPositions]);
154
+ if (render) return render({
155
+ selected,
156
+ handlePress
157
+ });
153
158
  return /*#__PURE__*/_jsx(Pressable, {
154
159
  ref: ref,
155
160
  onPress: handlePress,
@@ -207,7 +212,8 @@ TabsItem.propTypes = {
207
212
  selected: PropTypes.bool,
208
213
  itemPositions: itemPositionsPropType,
209
214
  children: PropTypes.string,
210
- id: PropTypes.string
215
+ id: PropTypes.string,
216
+ render: PropTypes.func
211
217
  };
212
218
  const staticStyles = StyleSheet.create({
213
219
  center: {
@@ -1,4 +1,5 @@
1
1
  export { default as A11yText } from './A11yText';
2
+ export { default as ActionCard } from './ActionCard';
2
3
  export { default as ActivityIndicator } from './ActivityIndicator';
3
4
  export { default as Autocomplete } from './Autocomplete';
4
5
  export { default as Box } from './Box';
@@ -52,8 +52,8 @@ function useVerticalExpandAnimation(_ref) {
52
52
  if (isAnimating || expandStateChanged) containerStyles.overflow = 'hidden';
53
53
  if (!isExpanded && !isAnimating && !expandStateChanged) {
54
54
  if (Platform.OS === 'web') {
55
- // Without `visibility: 'hidden', descendents are focusable on web even when collapsed.
56
- containerStyles.visibility = 'hidden';
55
+ // Without `display: 'none', descendents are focusable on web even when collapsed.
56
+ containerStyles.display = 'none';
57
57
  } else {
58
58
  // There's no `visibility: hidden` in React Native, and display: none causes a flicker on expand.
59
59
  // Without some form of hiding, some children leak through even when closed e.g. `List.Item` bullets.
@@ -67,7 +67,7 @@ function useVerticalExpandAnimation(_ref) {
67
67
  // on web we can hide the contents until we have the container measured and avoid occasional jitter
68
68
  // this won't work on native platforms
69
69
  containerStyles.height = 0;
70
- containerStyles.visibility = 'hidden';
70
+ containerStyles.display = 'none';
71
71
  }
72
72
  } else if (Platform.OS === 'web') {
73
73
  const transitionDuration = isExpanded ? expandDuration : collapseDuration;
@@ -105,7 +105,7 @@ export const getTokensPropType = function () {
105
105
  * For example, for a set of tokens used in a common style, or for a set of tokens required by
106
106
  * a themeless component whose tokens are set by a parent but requires tokens of a certain shape.
107
107
  *
108
- * By default, requires an object with a complete set of tokens (allowing `null`, but not `undefined`).
108
+ * By default, requires an object with a complete set of tokens (allowing `null` and `undefined`).
109
109
  *
110
110
  * @param {string[]} componentTokenKeys - array of strings of token names
111
111
  * @param {object} [options]
@@ -124,7 +124,7 @@ export const getTokensSetPropType = function (componentTokenKeys) {
124
124
  for (var _len2 = arguments.length, args = new Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
125
125
  args[_key2 - 2] = arguments[_key2];
126
126
  }
127
- return props[propName] !== null && tokenValueType.isRequired(props, propName, ...args);
127
+ return props[propName] !== null && props[propName] !== undefined && tokenValueType.isRequired(props, propName, ...args);
128
128
  }])));
129
129
  return allowFunction ? PropTypes.oneOfType([tokensObjectValidator, PropTypes.func]) : tokensObjectValidator;
130
130
  };
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.56.0",
14
+ "@telus-uds/system-theme-tokens": "^2.58.0",
15
15
  "airbnb-prop-types": "^2.16.0",
16
16
  "css-mediaquery": "^0.1.2",
17
17
  "expo-linear-gradient": "^12.5.0",
@@ -85,6 +85,6 @@
85
85
  "standard-engine": {
86
86
  "skip": true
87
87
  },
88
- "version": "1.85.1",
88
+ "version": "1.87.0",
89
89
  "types": "types/index.d.ts"
90
90
  }
@@ -0,0 +1,306 @@
1
+ import React from 'react'
2
+ import { Animated, View, StyleSheet, Text } from 'react-native'
3
+ import PropTypes from 'prop-types'
4
+
5
+ import { applyTextStyles, useThemeTokens } from '../ThemeProvider'
6
+ import {
7
+ a11yProps,
8
+ getTokensPropType,
9
+ selectSystemProps,
10
+ variantProp,
11
+ viewProps,
12
+ wrapStringsInText,
13
+ clickProps,
14
+ hrefAttrsProp,
15
+ linkProps
16
+ } from '../utils'
17
+ import Icon from '../Icon'
18
+ import Card from '../Card'
19
+
20
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, linkProps, viewProps])
21
+
22
+ const selectCardStyles = (styles) =>
23
+ ['borderColor', 'borderRadius', 'borderWidth'].reduce((acc, key) => {
24
+ if (styles[key]) acc[key] = styles[key]
25
+ return acc
26
+ }, {})
27
+
28
+ const selectInteractiveCardStyles = ({
29
+ backgroundColor,
30
+ paddingBottom,
31
+ paddingLeft,
32
+ paddingRight,
33
+ paddingTop
34
+ }) => ({
35
+ ...(backgroundColor && { backgroundColor }),
36
+ paddingBottom,
37
+ paddingLeft,
38
+ paddingRight,
39
+ paddingTop
40
+ })
41
+
42
+ const selectIconStyles = ({
43
+ iconMarginBottom,
44
+ iconMarginLeft,
45
+ iconMarginRight,
46
+ iconMarginTop
47
+ }) => ({
48
+ marginBottom: iconMarginBottom,
49
+ marginLeft: iconMarginLeft,
50
+ marginRight: iconMarginRight,
51
+ marginTop: iconMarginTop
52
+ })
53
+
54
+ const selectIconProps = ({ icon, iconBackgroundColor, iconColor }) => ({
55
+ icon,
56
+ variant: { background: true, padding: 'small' },
57
+ tokens: { backgroundColor: iconBackgroundColor, color: iconColor }
58
+ })
59
+
60
+ const selectTitleStyles = ({
61
+ titleMarginBottom,
62
+ titleMarginLeft,
63
+ titleMarginRight,
64
+ titleMarginTop
65
+ }) => ({
66
+ marginBottom: titleMarginBottom,
67
+ marginLeft: titleMarginLeft,
68
+ marginRight: titleMarginRight,
69
+ marginTop: titleMarginTop
70
+ })
71
+
72
+ const selectTitleTextStyles = ({
73
+ titleFontColor,
74
+ titleFontName,
75
+ titleFontSize,
76
+ titleFontWeight,
77
+ titleLineHeight
78
+ }) =>
79
+ applyTextStyles({
80
+ fontColor: titleFontColor,
81
+ fontName: titleFontName,
82
+ fontSize: titleFontSize,
83
+ fontWeight: titleFontWeight,
84
+ lineHeight: titleLineHeight
85
+ })
86
+
87
+ const selectContentStyles = ({
88
+ contentMarginBottom,
89
+ contentMarginLeft,
90
+ contentMarginRight,
91
+ contentMarginTop
92
+ }) => ({
93
+ marginBottom: contentMarginBottom,
94
+ marginLeft: contentMarginLeft,
95
+ marginRight: contentMarginRight,
96
+ marginTop: contentMarginTop
97
+ })
98
+
99
+ const selectContentTextStyles = ({
100
+ contentFontColor,
101
+ contentFontName,
102
+ contentFontSize,
103
+ contentFontWeight,
104
+ contentLineHeight
105
+ }) =>
106
+ applyTextStyles({
107
+ fontColor: contentFontColor,
108
+ fontName: contentFontName,
109
+ fontSize: contentFontSize,
110
+ fontWeight: contentFontWeight,
111
+ lineHeight: contentLineHeight
112
+ })
113
+
114
+ const selectStatusIconProps = ({ statusIcon, statusIconColor }) => ({
115
+ icon: statusIcon,
116
+ tokens: { color: statusIconColor },
117
+ variant: { size: 'micro' }
118
+ })
119
+
120
+ const selectActionIconStyles = ({
121
+ actionIconMarginBottom,
122
+ actionIconMarginLeft,
123
+ actionIconMarginRight,
124
+ actionIconMarginTop
125
+ }) => ({
126
+ marginBottom: actionIconMarginBottom,
127
+ marginLeft: actionIconMarginLeft,
128
+ marginRight: actionIconMarginRight,
129
+ marginTop: actionIconMarginTop
130
+ })
131
+
132
+ const selectActionIconProps = ({ actionIcon, actionIconColor }) => ({
133
+ icon: actionIcon,
134
+ tokens: { color: actionIconColor }
135
+ })
136
+
137
+ const ACTION_ICON_ANIMATION_DURATION = 100
138
+
139
+ /**
140
+ * ActionCard component represents a card with an action that can be triggered by the user.
141
+ *
142
+ * @component
143
+ * @example
144
+ * return (
145
+ * <ActionCard
146
+ * icon={<Icon icon={Icons.EyeMasked} />}
147
+ * title="Like"
148
+ * href="/like"
149
+ * accessibilityRole="link"
150
+ * tokens={themeTokens}
151
+ * variant={{validation: 'warning'}}
152
+ * onPress={() => handleLike()}
153
+ * >
154
+ * Click here to like the post
155
+ * </ActionCard>
156
+ * )
157
+ */
158
+ const ActionCard = React.forwardRef(
159
+ (
160
+ { icon, title, children, href, direction = true, accessibilityRole, tokens, variant, ...rest },
161
+ ref
162
+ ) => {
163
+ const themeTokens = useThemeTokens('ActionCard', tokens, variant)
164
+
165
+ const actionIconAnimation = React.useRef(new Animated.Value(0)).current
166
+
167
+ const { onPress, ...props } = clickProps.toPressProps(rest)
168
+ const { hrefAttrs, rawRest } = hrefAttrsProp.bundle(props)
169
+ const selectedProps = selectProps({
170
+ accessibilityRole,
171
+ href,
172
+ onPress: linkProps.handleHref({ href, onPress }),
173
+ hrefAttrs,
174
+ ...rawRest
175
+ })
176
+
177
+ return (
178
+ <Card
179
+ ref={ref}
180
+ interactiveCard={{
181
+ body: (pressableState) => {
182
+ const animate = pressableState.pressed || pressableState.hover || pressableState.focus
183
+ Animated.timing(actionIconAnimation, {
184
+ toValue: animate ? themeTokens.actionIconTranslate : 0,
185
+ duration: ACTION_ICON_ANIMATION_DURATION,
186
+ useNativeDriver: false
187
+ }).start()
188
+
189
+ return (
190
+ <View style={staticStyles.container}>
191
+ {icon && (
192
+ <View style={selectIconStyles(themeTokens)}>
193
+ <Icon
194
+ {...selectIconProps({
195
+ icon,
196
+ ...themeTokens
197
+ })}
198
+ />
199
+ </View>
200
+ )}
201
+ <View style={staticStyles.content}>
202
+ <View style={staticStyles.header}>
203
+ <View style={[selectTitleStyles(themeTokens), staticStyles.title]}>
204
+ <Text>
205
+ {wrapStringsInText(title, { style: selectTitleTextStyles(themeTokens) })}
206
+ {themeTokens.statusIcon && (
207
+ <View style={staticStyles.statusIcon}>
208
+ <Icon {...selectStatusIconProps(themeTokens)} />
209
+ </View>
210
+ )}
211
+ </Text>
212
+ </View>
213
+ <View style={staticStyles.icons}>
214
+ {direction && (
215
+ <Animated.View
216
+ style={[
217
+ selectActionIconStyles(themeTokens),
218
+ { transform: [{ translateX: actionIconAnimation }] }
219
+ ]}
220
+ >
221
+ <Icon {...selectActionIconProps(themeTokens)} />
222
+ </Animated.View>
223
+ )}
224
+ </View>
225
+ </View>
226
+ <View style={[staticStyles.body, selectContentStyles(themeTokens)]}>
227
+ {typeof children === 'string'
228
+ ? wrapStringsInText(children, {
229
+ style: selectContentTextStyles(themeTokens)
230
+ })
231
+ : children}
232
+ </View>
233
+ </View>
234
+ </View>
235
+ )
236
+ },
237
+ tokens: selectInteractiveCardStyles(themeTokens)
238
+ }}
239
+ tokens={selectCardStyles(themeTokens)}
240
+ {...selectedProps}
241
+ />
242
+ )
243
+ }
244
+ )
245
+
246
+ ActionCard.displayName = 'ActionCard'
247
+
248
+ ActionCard.propTypes = {
249
+ ...selectedSystemPropTypes,
250
+ tokens: getTokensPropType('ActionCard'),
251
+ variant: variantProp.propType,
252
+ /**
253
+ * Icon for the ActionCard
254
+ */
255
+ icon: PropTypes.elementType,
256
+ /**
257
+ * Title for the ActionCard
258
+ */
259
+ title: PropTypes.string,
260
+ /**
261
+ * Children for the ActionCard
262
+ */
263
+ children: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
264
+ /**
265
+ * href for the ActionCard
266
+ */
267
+ href: PropTypes.string,
268
+ /**
269
+ * Enable or disable the direction of the ActionCard
270
+ */
271
+ direction: PropTypes.bool,
272
+ /**
273
+ * AccesibilityRole for the ActionCard
274
+ */
275
+ accessibilityRole: PropTypes.string
276
+ }
277
+
278
+ const staticStyles = StyleSheet.create({
279
+ body: {
280
+ flexDirection: 'column'
281
+ },
282
+ container: {
283
+ flex: 1,
284
+ flexDirection: 'row'
285
+ },
286
+ content: {
287
+ flex: 1,
288
+ flexDirection: 'column'
289
+ },
290
+ icons: {
291
+ flexDirection: 'row',
292
+ alignItems: 'center'
293
+ },
294
+ header: {
295
+ flexDirection: 'row',
296
+ justifyContent: 'space-between'
297
+ },
298
+ title: {
299
+ flexShrink: 1
300
+ },
301
+ statusIcon: {
302
+ marginLeft: 4
303
+ }
304
+ })
305
+
306
+ export default ActionCard
@@ -0,0 +1,3 @@
1
+ import ActionCard from './ActionCard'
2
+
3
+ export default ActionCard
package/src/Card/Card.jsx CHANGED
@@ -9,13 +9,13 @@ import {
9
9
  } from '../ThemeProvider'
10
10
  import { getTokensPropType, variantProp, StyleSheet, createMediaQueryStyles } from '../utils'
11
11
  import { useViewport } from '../ViewportProvider'
12
- import { a11yProps, selectSystemProps, viewProps } from '../utils/props'
12
+ import { a11yProps, linkProps, selectSystemProps, viewProps } from '../utils/props'
13
13
  import CardBase from './CardBase'
14
14
  import PressableCardBase from './PressableCardBase'
15
15
  import CheckboxButton from '../Checkbox/CheckboxButton'
16
16
  import RadioButton from '../Radio/RadioButton'
17
17
 
18
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
18
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps, linkProps])
19
19
 
20
20
  const SelectionType = {
21
21
  Checkbox: 'checkbox',
@@ -253,7 +253,9 @@ const Card = React.forwardRef(
253
253
  onPress
254
254
  })
255
255
  )}
256
- {interactiveCard?.body}
256
+ {typeof interactiveCard?.body === 'function'
257
+ ? interactiveCard.body(cardState)
258
+ : interactiveCard.body}
257
259
  </>
258
260
  )
259
261
  }}
@@ -304,7 +306,7 @@ Card.propTypes = {
304
306
  * - variant: The variant to be used for the interactive card
305
307
  */
306
308
  interactiveCard: PropTypes.shape({
307
- body: PropTypes.node,
309
+ body: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
308
310
  tokens: getTokensPropType('Card'),
309
311
  selectionType: PropTypes.oneOf(Object.values(SelectionType)),
310
312
  variant: variantProp.propType
@@ -135,7 +135,7 @@ PressableCardBase.displayName = 'PressableCardBase'
135
135
  PressableCardBase.propTypes = {
136
136
  ...selectedSystemPropTypes,
137
137
  children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
138
- tokens: getTokensSetPropType(tokenKeys, { requireAll: true, allowFunction: true }),
138
+ tokens: getTokensSetPropType(tokenKeys, { partial: true, allowFunction: true }),
139
139
  variant: variantProp.propType
140
140
  }
141
141
 
@@ -192,6 +192,7 @@ const Checkbox = React.forwardRef(
192
192
  onKeyDown={handleKeyDown}
193
193
  onPress={handleChange}
194
194
  {...selectedProps}
195
+ style={staticStyles.removeOutline}
195
196
  >
196
197
  {({ focused: focus, hovered: hover, pressed }) => {
197
198
  const { icon: IconComponent, ...stateTokens } = getTokens({ focus, hover, pressed })
@@ -311,5 +312,6 @@ const staticStyles = StyleSheet.create({
311
312
  wrapper: { backgroundColor: 'transparent' },
312
313
  container: { flexDirection: 'row', alignItems: 'center' },
313
314
  defaultInputStyles: { alignItems: 'center', justifyContent: 'center' },
314
- alignWithLabel: { alignSelf: 'flex-start', justifyContent: 'center' }
315
+ alignWithLabel: { alignSelf: 'flex-start', justifyContent: 'center' },
316
+ removeOutline: { outlineStyle: 'none' }
315
317
  })