@telus-uds/components-base 1.52.0 → 1.54.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.
@@ -2,6 +2,7 @@ import React, { forwardRef, useState } from 'react';
2
2
  import Animated from "react-native-web/dist/exports/Animated";
3
3
  import Platform from "react-native-web/dist/exports/Platform";
4
4
  import View from "react-native-web/dist/exports/View";
5
+ import Text from "react-native-web/dist/exports/Text";
5
6
  import PropTypes from 'prop-types';
6
7
  import ABBPropTypes from 'airbnb-prop-types';
7
8
  import ExpandCollapseControl from './Control';
@@ -22,7 +23,50 @@ const selectContainerStyles = _ref => {
22
23
  paddingLeft: contentPaddingLeft,
23
24
  paddingRight: contentPaddingRight,
24
25
  paddingTop: contentPaddingTop,
25
- paddingBottom: contentPaddingBottom
26
+ paddingBottom: contentPaddingBottom,
27
+ flex: 1
28
+ };
29
+ };
30
+
31
+ const selectTextStyles = _ref2 => {
32
+ let {
33
+ contentPanelFontSize,
34
+ contentPanelFontName,
35
+ contentPanelFontColor,
36
+ contentPanelFontWeight,
37
+ contentPanelLineHeight
38
+ } = _ref2;
39
+ return {
40
+ fontSize: contentPanelFontSize,
41
+ fontFamily: `${contentPanelFontName}${contentPanelFontWeight}normal`,
42
+ lineHeight: contentPanelFontSize * contentPanelLineHeight,
43
+ color: contentPanelFontColor
44
+ };
45
+ };
46
+
47
+ const selectContentPanelStyles = _ref3 => {
48
+ let {
49
+ contentPanelBackgroundColor,
50
+ contentPanelPaddingTop,
51
+ contentPanelPaddingBottom,
52
+ contentPanelPaddingLeft,
53
+ contentPanelPaddingRight,
54
+ contentPanelBorderWidth,
55
+ contentPanelBorderColor,
56
+ borderRadius,
57
+ marginBottom
58
+ } = _ref3;
59
+ return {
60
+ backgroundColor: contentPanelBackgroundColor,
61
+ paddingTop: contentPanelPaddingTop,
62
+ paddingBottom: contentPanelPaddingBottom,
63
+ paddingLeft: contentPanelPaddingLeft,
64
+ paddingRight: contentPanelPaddingRight,
65
+ borderWidth: contentPanelBorderWidth,
66
+ borderColor: contentPanelBorderColor,
67
+ borderStyle: 'solid',
68
+ borderRadius,
69
+ marginBottom
26
70
  };
27
71
  };
28
72
  /**
@@ -37,7 +81,7 @@ const selectContainerStyles = _ref => {
37
81
  */
38
82
 
39
83
 
40
- const ExpandCollapsePanel = /*#__PURE__*/forwardRef((_ref2, ref) => {
84
+ const ExpandCollapsePanel = /*#__PURE__*/forwardRef((_ref4, ref) => {
41
85
  let {
42
86
  openIds = [],
43
87
  panelId,
@@ -49,8 +93,9 @@ const ExpandCollapsePanel = /*#__PURE__*/forwardRef((_ref2, ref) => {
49
93
  tokens,
50
94
  variant,
51
95
  controlRef,
96
+ content,
52
97
  ...rest
53
- } = _ref2;
98
+ } = _ref4;
54
99
  const [containerHeight, setContainerHeight] = useState(null);
55
100
  const isExpanded = openIds.includes(panelId);
56
101
  const selectedProps = selectProps({ ...rest,
@@ -85,7 +130,13 @@ const ExpandCollapsePanel = /*#__PURE__*/forwardRef((_ref2, ref) => {
85
130
  tokens: themeTokens
86
131
  });
87
132
  const focusabilityProps = isExpanded ? {} : a11yProps.nonFocusableProps;
88
- return /*#__PURE__*/_jsxs(View, {
133
+ return content ? /*#__PURE__*/_jsx(View, {
134
+ style: selectContentPanelStyles(themeTokens),
135
+ children: typeof children === 'string' ? /*#__PURE__*/_jsx(Text, {
136
+ style: selectTextStyles(themeTokens),
137
+ children: children
138
+ }) : children
139
+ }) : /*#__PURE__*/_jsxs(View, {
89
140
  ref: ref,
90
141
  style: themeTokens,
91
142
  children: [/*#__PURE__*/_jsx(ExpandCollapseControl, { ...selectedProps,
@@ -135,7 +186,7 @@ ExpandCollapsePanel.propTypes = { ...selectedSystemPropTypes,
135
186
  * Function to call on pressing the panel's control, which should open or close the panel.
136
187
  * If Panel is a direct child of `ExpandCollapse`, this prop will be provided by the ExpandCollapse parent.
137
188
  */
138
- onToggle: PropTypes.func.isRequired,
189
+ onToggle: PropTypes.func,
139
190
 
140
191
  /**
141
192
  * Optional function to call on pressing the panel's control, in addition to opening or closing the panel.
@@ -149,9 +200,9 @@ ExpandCollapsePanel.propTypes = { ...selectedSystemPropTypes,
149
200
  children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
150
201
 
151
202
  /**
152
- * The content inside the always-visible control element that opens and closes the ExpandCollapse when pressed.
203
+ * The content inside the control element that opens and closes the ExpandCollapse when pressed.
153
204
  */
154
- control: ExpandCollapseControl.propTypes.children.isRequired,
205
+ control: ExpandCollapseControl.propTypes.children,
155
206
 
156
207
  /**
157
208
  * Optional theme token overrides that may be passed to the ExpandCollapseControl element.
@@ -161,6 +212,11 @@ ExpandCollapsePanel.propTypes = { ...selectedSystemPropTypes,
161
212
  /**
162
213
  * An optional ref to be attached to the control
163
214
  */
164
- controlRef: ABBPropTypes.ref()
215
+ controlRef: ABBPropTypes.ref(),
216
+
217
+ /**
218
+ * A boolean prop to determine if the panel is a content panel or not. If true, the panel will not have a control
219
+ */
220
+ content: PropTypes.bool
165
221
  };
166
222
  export default ExpandCollapsePanel;
@@ -2,6 +2,7 @@ import React, { forwardRef } from 'react';
2
2
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
3
3
  import TouchableWithoutFeedback from "react-native-web/dist/exports/TouchableWithoutFeedback";
4
4
  import View from "react-native-web/dist/exports/View";
5
+ import ScrollView from "react-native-web/dist/exports/ScrollView";
5
6
  import NativeModal from "react-native-web/dist/exports/Modal";
6
7
  import Platform from "react-native-web/dist/exports/Platform";
7
8
  import PropTypes from 'prop-types';
@@ -140,8 +141,8 @@ const Modal = /*#__PURE__*/forwardRef((_ref5, ref) => {
140
141
  return /*#__PURE__*/_jsx(NativeModal, {
141
142
  transparent: true,
142
143
  ...selectProps(rest),
143
- children: /*#__PURE__*/_jsxs(View, {
144
- style: [staticStyles.positioningContainer],
144
+ children: /*#__PURE__*/_jsxs(ScrollView, {
145
+ contentContainerStyle: [staticStyles.positioningContainer],
145
146
  ref: modalRef,
146
147
  children: [/*#__PURE__*/_jsx(View, {
147
148
  style: [staticStyles.sizingContainer, selectContainerStyles(themeTokens)],
@@ -230,7 +231,11 @@ const staticStyles = StyleSheet.create({
230
231
  modal: {
231
232
  maxHeight: '100%',
232
233
  // so that the modal can expand vertically up to the sizing container's height (exclusive of its vertical padding)
233
- overflow: 'auto'
234
+ ...Platform.select({
235
+ web: {
236
+ overflow: 'auto'
237
+ }
238
+ })
234
239
  },
235
240
  closeButtonContainer: {
236
241
  position: 'absolute',
@@ -34,10 +34,19 @@ const selectStyles = _ref => {
34
34
 
35
35
  const selectTextStyles = _ref2 => {
36
36
  let {
37
- color
37
+ color,
38
+ textLine,
39
+ fontName,
40
+ fontSize,
41
+ fontWeight,
42
+ lineHeight
38
43
  } = _ref2;
39
44
  return {
40
- color
45
+ color,
46
+ textDecorationLine: textLine,
47
+ fontFamily: `${fontName}${fontWeight}normal`,
48
+ lineHeight: fontSize * lineHeight,
49
+ fontSize
41
50
  };
42
51
  };
43
52
  /**
@@ -110,9 +119,6 @@ const SkipLink = /*#__PURE__*/forwardRef((_ref3, ref) => {
110
119
  ...rest
111
120
  } = clickProps.toPressProps(rawRest);
112
121
  const getTokens = useThemeTokensCallback('SkipLink', tokens, variant);
113
- const defaultTokens = getTokens();
114
-
115
- const resolveLinkTokens = pressState => resolvePressableTokens(defaultTokens, pressState);
116
122
 
117
123
  const handlePress = event => {
118
124
  if (typeof onPress === 'function') onPress(event); // TODO - support native apps with something based on refs and/or setAccessibilityFocus
@@ -123,19 +129,17 @@ const SkipLink = /*#__PURE__*/forwardRef((_ref3, ref) => {
123
129
  accessibilityRole: "link",
124
130
  onPress: handlePress,
125
131
  href: href,
126
- style: _ref4 => {
127
- let {
128
- focused: focus
129
- } = _ref4;
130
- const themeTokens = getTokens({
131
- focus
132
- });
132
+ style: pressableState => {
133
+ const themeTokens = resolvePressableTokens(getTokens, pressableState);
133
134
  const skipLinkStyle = selectStyles(themeTokens);
134
- return [staticStyles.absolute, skipLinkStyle, !focus && staticStyles.hidden];
135
+ const {
136
+ focused
137
+ } = pressableState;
138
+ return [staticStyles.absolute, skipLinkStyle, !focused && staticStyles.hidden];
135
139
  },
136
140
  ...selectProps(rest),
137
- children: pressState => {
138
- const themeTokens = resolveLinkTokens(pressState);
141
+ children: pressableState => {
142
+ const themeTokens = resolvePressableTokens(getTokens, pressableState);
139
143
  const textStyles = selectTextStyles(themeTokens);
140
144
  return /*#__PURE__*/_jsx(Text, {
141
145
  style: [textStyles, staticStyles.baseline],
@@ -14,36 +14,54 @@ import { jsxs as _jsxs } from "react/jsx-runtime";
14
14
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, pressProps, viewProps]); // We need to drop the icon before passing it to the `ButtonBase`, because it's
15
15
  // being handled separately in this case
16
16
 
17
- const selectButtonTokens = _ref => {
17
+ const selectButtonTokens = (_ref, _ref2) => {
18
18
  let {
19
19
  icon: _,
20
20
  ...tokens
21
21
  } = _ref;
22
+ let {
23
+ trackHeight,
24
+ outerBorderGapButton
25
+ } = _ref2;
22
26
  return selectTokens('Button', { ...tokens,
23
27
  // Width tokens are applied to our inner track. Disable Button width token so it wraps our track width.
28
+ outerBorderGap: outerBorderGapButton,
29
+ height: trackHeight,
24
30
  width: null
25
31
  });
26
32
  }; // Map and rename icon-specific tokens to name used within Icon
27
33
 
28
34
 
29
- const selectIconTokens = _ref2 => {
35
+ const selectIconTokens = _ref3 => {
30
36
  let {
31
37
  iconSize,
32
38
  iconColor
33
- } = _ref2;
39
+ } = _ref3;
34
40
  return {
35
41
  size: iconSize,
36
42
  color: iconColor
37
43
  };
38
44
  };
39
45
 
40
- const selectTrackStyles = _ref3 => {
46
+ const selectTrackSwitchStyles = _ref4 => {
47
+ let {
48
+ switchSize,
49
+ width,
50
+ trackHeight
51
+ } = _ref4;
52
+ return {
53
+ width,
54
+ height: Math.max(switchSize, trackHeight)
55
+ };
56
+ };
57
+
58
+ const selectTrackStyles = _ref5 => {
41
59
  let {
42
60
  trackBorderWidth,
43
61
  trackBorderColor,
44
62
  trackBorderRadius,
45
63
  width
46
- } = _ref3;
64
+ } = _ref5;
47
65
  return {
48
66
  borderWidth: trackBorderWidth,
49
67
  borderColor: trackBorderColor,
@@ -52,7 +70,7 @@ const selectTrackStyles = _ref3 => {
52
70
  };
53
71
  };
54
72
 
55
- const selectSwitchStyles = _ref4 => {
73
+ const selectSwitchStyles = _ref6 => {
56
74
  let {
57
75
  switchSize,
58
76
  switchColor,
@@ -60,8 +78,9 @@ const selectSwitchStyles = _ref4 => {
60
78
  switchBorderColor,
61
79
  switchBorderRadius,
62
80
  switchShadow
63
- } = _ref4;
81
+ } = _ref6;
64
82
  return {
83
+ position: 'absolute',
65
84
  width: switchSize,
66
85
  height: switchSize,
67
86
  backgroundColor: switchColor,
@@ -78,23 +97,23 @@ const selectSwitchStyles = _ref4 => {
78
97
  };
79
98
  };
80
99
 
81
- const selectLabelStyles = _ref5 => {
100
+ const selectLabelStyles = _ref7 => {
82
101
  let {
83
102
  labelMarginLeft
84
- } = _ref5;
103
+ } = _ref7;
85
104
  return {
86
105
  marginLeft: labelMarginLeft
87
106
  };
88
107
  };
89
108
 
90
- const selectLabelTokens = _ref6 => {
109
+ const selectLabelTokens = _ref8 => {
91
110
  let {
92
111
  labelColor,
93
112
  labelFontName,
94
113
  labelFontSize,
95
114
  labelFontWeight,
96
115
  labelLineHeight
97
- } = _ref6;
116
+ } = _ref8;
98
117
  return {
99
118
  color: labelColor,
100
119
  fontName: labelFontName,
@@ -104,7 +123,7 @@ const selectLabelTokens = _ref6 => {
104
123
  };
105
124
  };
106
125
 
107
- const ToggleSwitch = /*#__PURE__*/forwardRef((_ref7, ref) => {
126
+ const ToggleSwitch = /*#__PURE__*/forwardRef((_ref9, ref) => {
108
127
  let {
109
128
  copy = 'en',
110
129
  value,
@@ -119,7 +138,7 @@ const ToggleSwitch = /*#__PURE__*/forwardRef((_ref7, ref) => {
119
138
  accessibilityRole = 'switch',
120
139
  accessibilityLabel = label,
121
140
  ...rest
122
- } = _ref7;
141
+ } = _ref9;
123
142
  const getTokens = useThemeTokensCallback('ToggleSwitch', tokens, variant);
124
143
  const themeTokens = getTokens();
125
144
  const {
@@ -133,13 +152,16 @@ const ToggleSwitch = /*#__PURE__*/forwardRef((_ref7, ref) => {
133
152
 
134
153
  const handlePress = event => setValue(!currentValue, event);
135
154
 
136
- const getButtonTokens = buttonState => selectButtonTokens(getTokens(buttonState));
155
+ const getButtonTokens = buttonState => selectButtonTokens(getTokens(buttonState), getTokens(themeTokens));
137
156
 
138
157
  const uniqueId = useUniqueId('toggleSwitch');
139
158
  const inputId = id ?? uniqueId;
140
159
  return /*#__PURE__*/_jsxs(StackView, {
141
- space: 2,
160
+ space: themeTokens.space,
142
161
  direction: "row",
162
+ tokens: {
163
+ alignItems: 'center'
164
+ },
143
165
  children: [Boolean(label) && /*#__PURE__*/_jsx(View, {
144
166
  style: [selectLabelStyles(themeTokens), staticStyles.containText],
145
167
  children: /*#__PURE__*/_jsx(InputLabel, {
@@ -167,24 +189,36 @@ const ToggleSwitch = /*#__PURE__*/forwardRef((_ref7, ref) => {
167
189
  const IconComponent = stateTokens.icon;
168
190
  const switchStyles = selectSwitchStyles(stateTokens);
169
191
  const trackStyles = selectTrackStyles(stateTokens);
170
- const iconTokens = selectIconTokens(stateTokens); // If drag-slide support is needed, use a PanResponder and apply these to an Animated value.
192
+ const iconTokens = selectIconTokens(stateTokens);
193
+ const trackSwitchStyles = selectTrackSwitchStyles(stateTokens);
194
+ const {
195
+ switchSize,
196
+ trackHeight,
197
+ width,
198
+ trackBorderWidth
199
+ } = stateTokens; // If drag-slide support is needed, use a PanResponder and apply these to an Animated value.
171
200
  // Use translate transforms for smoothest non-thread-blocking animations and to allow drag.
201
+ // Two different translates depending on the switchSize and trackSize relationship.
172
202
 
173
- const slideStart = 0;
174
- const slideEnd = stateTokens.width - stateTokens.switchSize - stateTokens.trackBorderWidth * 2;
203
+ const isSwitchTallerThanTrack = switchSize >= trackHeight;
204
+ const slideStart = isSwitchTallerThanTrack ? 0 : trackBorderWidth;
205
+ const slideEnd = isSwitchTallerThanTrack ? width - switchSize : width - switchSize - trackBorderWidth;
175
206
  const switchOffset = buttonState.selected ? slideEnd : slideStart;
176
207
  const switchPositionStyle = {
177
208
  transform: [{
178
209
  translateX: switchOffset
179
210
  }]
180
211
  };
181
- return /*#__PURE__*/_jsx(View, {
182
- style: [staticStyles.track, trackStyles],
183
- children: /*#__PURE__*/_jsx(View, {
212
+ return /*#__PURE__*/_jsxs(View, {
213
+ nativeID: "trackSwitch",
214
+ style: [trackSwitchStyles, staticStyles.trackSwitch],
215
+ children: [/*#__PURE__*/_jsx(View, {
216
+ style: [staticStyles.track, trackStyles]
217
+ }), /*#__PURE__*/_jsx(View, {
184
218
  style: [staticStyles.switch, switchStyles, switchPositionStyle],
185
219
  children: IconComponent && /*#__PURE__*/_jsx(IconComponent, { ...iconTokens
186
220
  })
187
- })
221
+ })]
188
222
  });
189
223
  }
190
224
  })]
@@ -250,6 +284,10 @@ const staticStyles = StyleSheet.create({
250
284
  },
251
285
  containText: {
252
286
  flexShrink: 1
287
+ },
288
+ trackSwitch: {
289
+ flexDirection: 'row',
290
+ alignItems: 'center'
253
291
  }
254
292
  });
255
293
  export default ToggleSwitch;
@@ -49,7 +49,9 @@ const TooltipButton = _ref3 => {
49
49
  icon: IconComponent
50
50
  } = themeTokens;
51
51
  return /*#__PURE__*/_jsx(View, {
52
- style: applyOuterBorder(themeTokens),
52
+ style: [applyOuterBorder(themeTokens), themeTokens.outerBorderWidth && {
53
+ margin: -themeTokens.outerBorderWidth
54
+ }],
53
55
  ...selectProps(rest),
54
56
  children: /*#__PURE__*/_jsx(View, {
55
57
  style: selectInnerContainerStyles(themeTokens),
@@ -54,7 +54,10 @@ const Typography = /*#__PURE__*/forwardRef((_ref2, ref) => {
54
54
  ...rest
55
55
  } = _ref2;
56
56
  const viewport = useViewport();
57
- const themeTokens = useThemeTokens('Typography', tokens, variant, {
57
+ const {
58
+ superScriptFontSize,
59
+ ...themeTokens
60
+ } = useThemeTokens('Typography', tokens, variant, {
58
61
  viewport
59
62
  });
60
63
  const {
@@ -77,8 +80,13 @@ const Typography = /*#__PURE__*/forwardRef((_ref2, ref) => {
77
80
  if (typeof child === 'object' && ((child === null || child === void 0 ? void 0 : child.type) === 'sub' || (child === null || child === void 0 ? void 0 : child.type) === 'sup')) {
78
81
  var _child$props;
79
82
 
83
+ const childStyles = (child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.style) || {};
84
+ const supFontSize = childStyles.fontSize ?? superScriptFontSize;
80
85
  const sanitizedChild = /*#__PURE__*/React.cloneElement(child, {
81
- style: { ...(child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.style),
86
+ style: { ...childStyles,
87
+ ...(supFontSize ? {
88
+ fontSize: supFontSize
89
+ } : {}),
82
90
  lineHeight: 0
83
91
  }
84
92
  });
package/package.json CHANGED
@@ -11,13 +11,13 @@
11
11
  "@floating-ui/react-native": "^0.8.1",
12
12
  "@gorhom/portal": "^1.0.14",
13
13
  "@telus-uds/system-constants": "^1.2.1",
14
- "@telus-uds/system-theme-tokens": "^2.35.0",
14
+ "@telus-uds/system-theme-tokens": "^2.37.0",
15
15
  "airbnb-prop-types": "^2.16.0",
16
16
  "lodash.debounce": "^4.0.8",
17
17
  "lodash.merge": "^4.6.2",
18
18
  "prop-types": "^15.7.2",
19
19
  "react-native-picker-select": "^8.0.4",
20
- "semver": "7.3.5"
20
+ "semver": "7.5.2"
21
21
  },
22
22
  "description": "Base components",
23
23
  "devDependencies": {
@@ -72,5 +72,5 @@
72
72
  "standard-engine": {
73
73
  "skip": true
74
74
  },
75
- "version": "1.52.0"
75
+ "version": "1.54.0"
76
76
  }
@@ -173,6 +173,13 @@ const selectWebOnlyStyles = (inactive, themeTokens, { accessibilityRole }) => {
173
173
  })
174
174
  }
175
175
 
176
+ const selectButtonStyles = ({ textAlign }) => {
177
+ return {
178
+ flexDirection: 'row',
179
+ justifyContent: textAlign
180
+ }
181
+ }
182
+
176
183
  const selectItemIconTokens = ({ color, iconColor, iconSize }) => ({
177
184
  size: iconSize,
178
185
  color: iconColor || color
@@ -237,6 +244,8 @@ const ButtonBase = forwardRef(
237
244
  const stretchStyles = themeTokens.width ? staticStyles.stretch : staticStyles.align
238
245
  const IconComponent = icon || themeTokens.icon
239
246
 
247
+ const rowStyles = selectButtonStyles(themeTokens)
248
+
240
249
  return (
241
250
  <View
242
251
  id={id}
@@ -249,7 +258,8 @@ const ButtonBase = forwardRef(
249
258
  web: {
250
259
  maxWidth: '100%', // ensure overflowing content wraps
251
260
  // TODO: https://github.com/telus/universal-design-system/issues/487
252
- transition: 'background-color 200ms, border-color 200ms'
261
+ transition: 'background-color 200ms, border-color 200ms',
262
+ ...rowStyles
253
263
  }
254
264
  })
255
265
  ]}
@@ -1,6 +1,6 @@
1
1
  import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
- import { View } from 'react-native'
3
+ import { View, Platform } from 'react-native'
4
4
 
5
5
  import { applyShadowToken } from '../ThemeProvider'
6
6
  import { getTokensPropType } from '../utils'
@@ -20,20 +20,31 @@ const selectStyles = ({
20
20
  paddingRight,
21
21
  paddingTop,
22
22
  minWidth,
23
- shadow
24
- }) => ({
25
- flex,
26
- backgroundColor,
27
- borderColor,
28
- borderRadius,
29
- borderWidth,
30
- paddingBottom,
31
- paddingLeft,
32
- paddingRight,
33
- paddingTop,
34
- minWidth,
35
- ...applyShadowToken(shadow)
36
- })
23
+ shadow,
24
+ gradient
25
+ }) => {
26
+ return {
27
+ flex,
28
+ backgroundColor,
29
+ borderColor,
30
+ borderRadius,
31
+ borderWidth,
32
+ paddingBottom,
33
+ paddingLeft,
34
+ paddingRight,
35
+ paddingTop,
36
+ minWidth,
37
+ ...applyShadowToken(shadow),
38
+ ...(gradient && Platform.OS === 'web'
39
+ ? {
40
+ backgroundImage: `linear-gradient(${gradient.angle}deg, ${gradient.stops[0].color}, ${gradient.stops[1].color})`,
41
+ backgroundOrigin: `border-box`,
42
+ boxShadow: `inset 0 1000px white`,
43
+ border: `${borderWidth}px solid transparent`
44
+ }
45
+ : {})
46
+ }
47
+ }
37
48
 
38
49
  /**
39
50
  * A themeless base component for Card which components can apply theme tokens to. Not
@@ -204,7 +204,11 @@ const Carousel = React.forwardRef(
204
204
 
205
205
  const getCopy = useCopy({ dictionary, copy })
206
206
 
207
- const childrenArray = unpackFragment(children)
207
+ let childrenArray = unpackFragment(children)
208
+ // if `Carousel` only has one `Carousel.Item`, convert this to a single-item array
209
+ if (!Array.isArray(childrenArray)) {
210
+ childrenArray = [childrenArray]
211
+ }
208
212
  const systemProps = selectProps({
209
213
  ...rest,
210
214
  accessibilityRole,
@@ -322,11 +326,14 @@ const Carousel = React.forwardRef(
322
326
  )
323
327
 
324
328
  const isSwipeAllowed = React.useCallback(() => {
329
+ if (childrenArray.length === 1) {
330
+ return false
331
+ }
325
332
  if (Platform.OS === 'web') {
326
333
  return !!(viewport === 'xs' || viewport === 'sm')
327
334
  }
328
335
  return true
329
- }, [viewport])
336
+ }, [viewport, childrenArray.length])
330
337
 
331
338
  const panResponder = React.useMemo(
332
339
  () =>
@@ -477,7 +484,7 @@ const Carousel = React.forwardRef(
477
484
  {...systemProps}
478
485
  {...containerProps}
479
486
  >
480
- {showPreviousNextNavigation && (
487
+ {showPreviousNextNavigation && childrenArray.length > 1 ? (
481
488
  <View
482
489
  style={selectPreviousNextNavigationButtonStyles(
483
490
  previousNextNavigationButtonWidth,
@@ -500,7 +507,7 @@ const Carousel = React.forwardRef(
500
507
  )}
501
508
  />
502
509
  </View>
503
- )}
510
+ ) : null}
504
511
  {Boolean(skipLinkHref) && (
505
512
  <SkipLink ref={firstFocusRef} href={skipLinkHref}>
506
513
  {getCopyWithPlaceholders('skipLink')}
@@ -538,7 +545,7 @@ const Carousel = React.forwardRef(
538
545
  })}
539
546
  </Animated.View>
540
547
  </View>
541
- {showPreviousNextNavigation && (
548
+ {showPreviousNextNavigation && childrenArray.length > 1 ? (
542
549
  <View
543
550
  style={selectPreviousNextNavigationButtonStyles(
544
551
  previousNextNavigationButtonWidth,
@@ -561,7 +568,7 @@ const Carousel = React.forwardRef(
561
568
  )}
562
569
  />
563
570
  </View>
564
- )}
571
+ ) : null}
565
572
  </View>
566
573
  {showPanelNavigation ? activePanelNavigation : null}
567
574
  </CarouselProvider>