@telus-uds/components-base 0.0.2-prerelease.1 → 0.0.2-prerelease.5

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 (161) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/__fixtures__/testTheme.js +264 -84
  3. package/__tests__/Box/Box.test.jsx +81 -58
  4. package/__tests__/Card/Card.test.jsx +63 -0
  5. package/__tests__/Divider/Divider.test.jsx +26 -5
  6. package/__tests__/Feedback/Feedback.test.jsx +42 -0
  7. package/__tests__/FlexGrid/Col.test.jsx +5 -0
  8. package/__tests__/Pagination/Pagination.test.jsx +160 -0
  9. package/__tests__/Spacer/Spacer.test.jsx +63 -0
  10. package/__tests__/StackView/StackView.test.jsx +242 -0
  11. package/__tests__/StackView/StackWrap.test.jsx +47 -0
  12. package/__tests__/StackView/getStackedContent.test.jsx +295 -0
  13. package/__tests__/TextInput/TextInput.test.jsx +146 -0
  14. package/__tests__/ThemeProvider/useThemeTokens.test.jsx +5 -3
  15. package/__tests__/utils/spacing.test.jsx +273 -0
  16. package/__tests__/utils/useUniqueId.test.js +31 -0
  17. package/babel.config.json +8 -0
  18. package/jest.config.js +7 -6
  19. package/lib/A11yInfoProvider/index.js +2 -2
  20. package/lib/A11yText/index.js +1 -3
  21. package/lib/ActivityIndicator/Spinner.web.js +3 -5
  22. package/lib/Box/Box.js +117 -82
  23. package/lib/Button/Button.js +1 -3
  24. package/lib/Button/ButtonBase.js +9 -21
  25. package/lib/Button/ButtonGroup.js +14 -25
  26. package/lib/Button/ButtonLink.js +1 -3
  27. package/lib/Card/Card.js +103 -0
  28. package/lib/Card/index.js +2 -0
  29. package/lib/Divider/Divider.js +40 -4
  30. package/lib/ExpandCollapse/Accordion.js +1 -3
  31. package/lib/ExpandCollapse/Control.js +3 -5
  32. package/lib/ExpandCollapse/Panel.js +2 -4
  33. package/lib/Feedback/Feedback.js +110 -0
  34. package/lib/Feedback/index.js +2 -0
  35. package/lib/FlexGrid/Col/Col.js +3 -5
  36. package/lib/FlexGrid/FlexGrid.js +1 -3
  37. package/lib/FlexGrid/Row/Row.js +1 -3
  38. package/lib/FlexGrid/providers/GutterContext.js +1 -1
  39. package/lib/Icon/Icon.js +1 -1
  40. package/lib/InputLabel/InputLabel.js +86 -0
  41. package/lib/InputLabel/LabelContent.native.js +8 -0
  42. package/lib/InputLabel/LabelContent.web.js +17 -0
  43. package/lib/InputLabel/index.js +2 -0
  44. package/lib/Link/ChevronLink.js +1 -3
  45. package/lib/Link/Link.js +1 -3
  46. package/lib/Link/LinkBase.js +11 -7
  47. package/lib/Link/TextButton.js +1 -3
  48. package/lib/Pagination/PageButton.js +85 -0
  49. package/lib/Pagination/Pagination.js +118 -0
  50. package/lib/Pagination/SideButton.js +108 -0
  51. package/lib/Pagination/dictionary.js +18 -0
  52. package/lib/Pagination/index.js +2 -0
  53. package/lib/Pagination/useCopy.js +10 -0
  54. package/lib/Pagination/usePagination.js +70 -0
  55. package/lib/SideNav/Item.js +4 -6
  56. package/lib/SideNav/ItemsGroup.js +11 -11
  57. package/lib/SideNav/SideNav.js +2 -4
  58. package/lib/Spacer/Spacer.js +98 -0
  59. package/lib/Spacer/index.js +2 -0
  60. package/lib/StackView/StackView.js +105 -0
  61. package/lib/StackView/StackWrap.js +32 -0
  62. package/lib/StackView/StackWrap.native.js +3 -0
  63. package/lib/StackView/StackWrapBox.js +85 -0
  64. package/lib/StackView/StackWrapGap.js +45 -0
  65. package/lib/StackView/common.js +30 -0
  66. package/lib/StackView/getStackedContent.js +111 -0
  67. package/lib/StackView/index.js +5 -0
  68. package/lib/TextInput/TextInput.js +337 -0
  69. package/lib/TextInput/index.js +2 -0
  70. package/lib/ThemeProvider/ThemeProvider.js +2 -2
  71. package/lib/ThemeProvider/useThemeTokens.js +34 -6
  72. package/lib/ThemeProvider/utils/theme-tokens.js +37 -9
  73. package/lib/ToggleSwitch/ToggleSwitch.js +17 -47
  74. package/lib/Typography/Typography.js +1 -7
  75. package/lib/ViewportProvider/index.js +1 -1
  76. package/lib/index.js +8 -1
  77. package/lib/utils/index.js +2 -1
  78. package/lib/utils/input.js +3 -1
  79. package/lib/utils/propTypes.js +103 -8
  80. package/lib/utils/spacing/index.js +2 -0
  81. package/lib/utils/spacing/useSpacingScale.js +102 -0
  82. package/lib/utils/spacing/utils.js +32 -0
  83. package/lib/utils/useUniqueId.js +12 -0
  84. package/package.json +6 -9
  85. package/release-context.json +4 -4
  86. package/src/Box/Box.jsx +117 -80
  87. package/src/Button/ButtonBase.jsx +8 -21
  88. package/src/Button/ButtonGroup.jsx +13 -17
  89. package/src/Card/Card.jsx +101 -0
  90. package/src/Card/index.js +3 -0
  91. package/src/Divider/Divider.jsx +38 -3
  92. package/src/ExpandCollapse/Control.jsx +2 -3
  93. package/src/Feedback/Feedback.jsx +99 -0
  94. package/src/Feedback/index.js +3 -0
  95. package/src/FlexGrid/Col/Col.jsx +4 -2
  96. package/src/Icon/Icon.jsx +2 -1
  97. package/src/InputLabel/InputLabel.jsx +99 -0
  98. package/src/InputLabel/LabelContent.native.jsx +6 -0
  99. package/src/InputLabel/LabelContent.web.jsx +13 -0
  100. package/src/InputLabel/index.js +3 -0
  101. package/src/Link/LinkBase.jsx +9 -3
  102. package/src/Pagination/PageButton.jsx +80 -0
  103. package/src/Pagination/Pagination.jsx +135 -0
  104. package/src/Pagination/SideButton.jsx +93 -0
  105. package/src/Pagination/dictionary.js +18 -0
  106. package/src/Pagination/index.js +3 -0
  107. package/src/Pagination/useCopy.js +7 -0
  108. package/src/Pagination/usePagination.js +69 -0
  109. package/src/SideNav/Item.jsx +3 -3
  110. package/src/SideNav/ItemsGroup.jsx +11 -13
  111. package/src/Spacer/Spacer.jsx +91 -0
  112. package/src/Spacer/index.js +3 -0
  113. package/src/StackView/StackView.jsx +103 -0
  114. package/src/StackView/StackWrap.jsx +33 -0
  115. package/src/StackView/StackWrap.native.jsx +4 -0
  116. package/src/StackView/StackWrapBox.jsx +82 -0
  117. package/src/StackView/StackWrapGap.jsx +39 -0
  118. package/src/StackView/common.jsx +28 -0
  119. package/src/StackView/getStackedContent.jsx +106 -0
  120. package/src/StackView/index.js +6 -0
  121. package/src/TextInput/TextInput.jsx +325 -0
  122. package/src/TextInput/index.js +3 -0
  123. package/src/ThemeProvider/useThemeTokens.js +34 -7
  124. package/src/ThemeProvider/utils/theme-tokens.js +37 -8
  125. package/src/ToggleSwitch/ToggleSwitch.jsx +23 -43
  126. package/src/Typography/Typography.jsx +0 -4
  127. package/src/index.js +8 -1
  128. package/src/utils/index.js +1 -0
  129. package/src/utils/input.js +2 -1
  130. package/src/utils/propTypes.js +105 -16
  131. package/src/utils/spacing/index.js +3 -0
  132. package/src/utils/spacing/useSpacingScale.js +93 -0
  133. package/src/utils/spacing/utils.js +28 -0
  134. package/src/utils/useUniqueId.js +14 -0
  135. package/stories/A11yText/A11yText.stories.jsx +11 -5
  136. package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +11 -2
  137. package/stories/Box/Box.stories.jsx +46 -17
  138. package/stories/Button/Button.stories.jsx +17 -21
  139. package/stories/Button/ButtonGroup.stories.jsx +2 -1
  140. package/stories/Button/ButtonLink.stories.jsx +6 -4
  141. package/stories/Card/Card.stories.jsx +62 -0
  142. package/stories/Divider/Divider.stories.jsx +26 -2
  143. package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +74 -79
  144. package/stories/Feedback/Feedback.stories.jsx +97 -0
  145. package/stories/FlexGrid/01 FlexGrid.stories.jsx +20 -7
  146. package/stories/Icon/Icon.stories.jsx +11 -3
  147. package/stories/InputLabel/InputLabel.stories.jsx +37 -0
  148. package/stories/Link/ChevronLink.stories.jsx +20 -4
  149. package/stories/Link/Link.stories.jsx +24 -3
  150. package/stories/Link/TextButton.stories.jsx +24 -3
  151. package/stories/Pagination/Pagination.stories.jsx +64 -0
  152. package/stories/SideNav/SideNav.stories.jsx +17 -2
  153. package/stories/Spacer/Spacer.stories.jsx +33 -0
  154. package/stories/StackView/StackView.stories.jsx +65 -0
  155. package/stories/StackView/StackWrap.stories.jsx +52 -0
  156. package/stories/TextInput/TextInput.stories.jsx +103 -0
  157. package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +16 -3
  158. package/stories/Typography/Typography.stories.jsx +12 -3
  159. package/stories/platform-supports.web.jsx +1 -1
  160. package/stories/supports.jsx +113 -13
  161. package/babel.config.js +0 -3
@@ -0,0 +1,86 @@
1
+ import React from 'react';
2
+ import { View, Text, StyleSheet } from 'react-native';
3
+ import PropTypes from 'prop-types';
4
+ import { applyTextStyles, useThemeTokens } from '../ThemeProvider';
5
+ import { getTokensPropType, selectTokens, variantProp } from '../utils';
6
+ import LabelContent from './LabelContent';
7
+
8
+ const selectLabelStyles = tokens => applyTextStyles(selectTokens('Typography', tokens));
9
+
10
+ const selectHintStyles = ({
11
+ hintColor,
12
+ hintFontName,
13
+ hintFontSize,
14
+ hintFontWeight,
15
+ hintLineHeight
16
+ }) => applyTextStyles({
17
+ color: hintColor,
18
+ fontName: hintFontName,
19
+ fontSize: hintFontSize,
20
+ fontWeight: hintFontWeight,
21
+ lineHeight: hintLineHeight
22
+ });
23
+
24
+ const selectGapStyles = ({
25
+ gap
26
+ }) => ({
27
+ marginRight: gap
28
+ });
29
+
30
+ function InputLabel({
31
+ label,
32
+ forId,
33
+ hint,
34
+ hintPosition = 'inline',
35
+ hintId,
36
+ tooltip,
37
+ tokens,
38
+ variant
39
+ }) {
40
+ const themeTokens = useThemeTokens('InputLabel', tokens, variant); // TODO: use the actual Tooltip component here
41
+
42
+ const hasTooltip = tooltip !== undefined;
43
+ const isHintInline = hintPosition === 'inline';
44
+ return /*#__PURE__*/React.createElement(View, {
45
+ style: [staticStyles.container, !isHintInline && staticStyles.containerWithHintBelow]
46
+ }, /*#__PURE__*/React.createElement(Text, {
47
+ style: [selectLabelStyles(themeTokens), selectGapStyles(themeTokens), staticStyles.label]
48
+ }, /*#__PURE__*/React.createElement(LabelContent, {
49
+ forId: forId
50
+ }, label)), hint && isHintInline && /*#__PURE__*/React.createElement(Text, {
51
+ style: [selectHintStyles(themeTokens), hasTooltip && selectGapStyles(themeTokens)],
52
+ nativeID: hintId
53
+ }, hint), tooltip && /*#__PURE__*/React.createElement(Text, null, "?"), hint && !isHintInline && /*#__PURE__*/React.createElement(Text, {
54
+ style: [selectHintStyles(themeTokens), staticStyles.hintBelow],
55
+ nativeID: hintId
56
+ }, hint));
57
+ }
58
+
59
+ InputLabel.propTypes = {
60
+ label: PropTypes.string.isRequired,
61
+ forId: PropTypes.string,
62
+ hint: PropTypes.string,
63
+ hintPosition: PropTypes.oneOf(['inline', 'below']),
64
+ hintId: PropTypes.string,
65
+ tooltip: PropTypes.string,
66
+ tokens: getTokensPropType('InputLabel'),
67
+ variant: variantProp.propType
68
+ };
69
+ export default InputLabel;
70
+ const staticStyles = StyleSheet.create({
71
+ container: {
72
+ display: 'flex',
73
+ flexDirection: 'row',
74
+ alignItems: 'center'
75
+ },
76
+ containerWithHintBelow: {
77
+ flexWrap: 'wrap'
78
+ },
79
+ label: {
80
+ flexShrink: 0
81
+ },
82
+ hintBelow: {
83
+ flexBasis: '100%',
84
+ flexShrink: 0
85
+ }
86
+ });
@@ -0,0 +1,8 @@
1
+ // Since there's no equivalent for web's <label> element we're simply rendering whatever is passed to this component.
2
+ function LabelContent({
3
+ children
4
+ }) {
5
+ return children;
6
+ }
7
+
8
+ export default LabelContent;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ function LabelContent({
5
+ children,
6
+ forId
7
+ }) {
8
+ return /*#__PURE__*/React.createElement("label", {
9
+ htmlFor: forId
10
+ }, children);
11
+ }
12
+
13
+ export default LabelContent;
14
+ LabelContent.propTypes = {
15
+ children: PropTypes.string,
16
+ forId: PropTypes.string
17
+ };
@@ -0,0 +1,2 @@
1
+ import InputLabel from './InputLabel';
2
+ export default InputLabel;
@@ -1,5 +1,3 @@
1
- function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
-
3
1
  import React from 'react';
4
2
  import PropTypes from 'prop-types';
5
3
  import Link from './Link';
@@ -9,7 +7,7 @@ const ChevronLink = ({
9
7
  children,
10
8
  variant = {},
11
9
  ...linkProps
12
- }) => /*#__PURE__*/React.createElement(Link, _extends({
10
+ }) => /*#__PURE__*/React.createElement(Link, Object.assign({
13
11
  variant: {
14
12
  component: 'ChevronLink',
15
13
  ...variant
package/lib/Link/Link.js CHANGED
@@ -1,5 +1,3 @@
1
- function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
-
3
1
  import React from 'react';
4
2
  import LinkBase from './LinkBase';
5
3
 
@@ -9,7 +7,7 @@ const Link = ({
9
7
  accessibilityRole = 'link',
10
8
  variant = {},
11
9
  ...linkProps
12
- }) => /*#__PURE__*/React.createElement(LinkBase, _extends({
10
+ }) => /*#__PURE__*/React.createElement(LinkBase, Object.assign({
13
11
  href: href,
14
12
  accessibilityRole: accessibilityRole,
15
13
  variant: {
@@ -1,5 +1,3 @@
1
- function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
-
3
1
  import React, { useState } from 'react';
4
2
  import PropTypes from 'prop-types';
5
3
  import { Text, Platform, StyleSheet, Pressable, PixelRatio, View, TouchableWithoutFeedback } from 'react-native';
@@ -27,7 +25,10 @@ Platform.OS === 'web' ? {
27
25
  outline: outerBorderOutline,
28
26
  borderWidth: outerBorderWidth,
29
27
  borderColor: outerBorderColor,
30
- borderRadius: outerBorderRadius
28
+ borderRadius: outerBorderRadius,
29
+ // Stops focus ring stretching horizontally if parent has display: block
30
+ // width: fit-content isn't supported on Firefox; can't cascade props like CSS `width: fit-content; width: --moz-fit-content;`
31
+ display: 'inline-flex'
31
32
  } : {};
32
33
 
33
34
  const selectTextStyles = ({
@@ -55,10 +56,12 @@ const selectIconStyles = ({
55
56
  iconGapBefore,
56
57
  iconGapAfter,
57
58
  iconScale,
58
- iconTranslateX
59
+ iconTranslateX,
60
+ iconTranslateY
59
61
  }) => ({
60
62
  scale: iconScale,
61
63
  translateX: iconTranslateX,
64
+ translateY: iconTranslateY,
62
65
  size: iconSize,
63
66
  gapBefore: iconGapBefore,
64
67
  gapAfter: iconGapAfter
@@ -173,7 +176,7 @@ const LinkBase = ({
173
176
  onPressIn: onPressIn,
174
177
  onPressOut: onPressOut,
175
178
  onPress: _onPress
176
- }, /*#__PURE__*/React.createElement(Text, _extends({}, textProps, {
179
+ }, /*#__PURE__*/React.createElement(Text, Object.assign({}, textProps, {
177
180
  style: [outerBorderStyles, blockLeftStyle, contentStyles, blockTextStyles, textStyles]
178
181
  }), children))
179
182
  );
@@ -185,7 +188,8 @@ const LinkBase = ({
185
188
  size: iconStyles.size ? iconStyles.size * iconScale : undefined,
186
189
  color: contentStyles.color ?? undefined,
187
190
  scale: iconStyles.scale ?? undefined,
188
- translateX: iconStyles.translateX ?? undefined
191
+ translateX: iconStyles.translateX ?? undefined,
192
+ translateY: iconStyles.translateY ?? undefined
189
193
  };
190
194
  const iconContent = /*#__PURE__*/React.createElement(IconComponent, {
191
195
  tokens: iconTokens,
@@ -203,7 +207,7 @@ const LinkBase = ({
203
207
  // properly on Android which purely inline links can't do. Add an isNested case that is similar to
204
208
  // https://github.com/telus/universal-design-system/pull/233
205
209
 
206
- return /*#__PURE__*/React.createElement(Pressable, _extends({}, linkPropSet, {
210
+ return /*#__PURE__*/React.createElement(Pressable, Object.assign({}, linkPropSet, {
207
211
  style: [outerBorderStyles, blockLeftStyle, staticStyles.rowContainer]
208
212
  }), iconPosition === 'left' && iconBlock, /*#__PURE__*/React.createElement(Text, {
209
213
  style: [contentStyles, blockTextStyles, textStyles, Platform.select({
@@ -1,5 +1,3 @@
1
- function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
-
3
1
  import React from 'react';
4
2
  import PropTypes from 'prop-types';
5
3
  import LinkBase from './LinkBase';
@@ -10,7 +8,7 @@ const TextButton = ({
10
8
  variant = {},
11
9
  accessibilityRole = 'button',
12
10
  ...linkProps
13
- }) => /*#__PURE__*/React.createElement(LinkBase, _extends({
11
+ }) => /*#__PURE__*/React.createElement(LinkBase, Object.assign({
14
12
  onPress: onPress,
15
13
  accessibilityRole: accessibilityRole,
16
14
  variant: {
@@ -0,0 +1,85 @@
1
+ import React from 'react';
2
+ import { Text } from 'react-native';
3
+ import PropTypes from 'prop-types';
4
+ import ButtonBase from '../Button/ButtonBase';
5
+ import { applyTextStyles, useThemeTokensCallback } from '../ThemeProvider';
6
+ import { a11yProps, copyPropTypes, getTokensPropType, hrefAttrsProp, linkProps, selectTokens, variantProp } from '../utils';
7
+ import useCopy from './useCopy';
8
+ import dictionary from './dictionary';
9
+
10
+ const selectTextStyles = ({
11
+ color,
12
+ fontName,
13
+ fontSize,
14
+ fontWeight,
15
+ lineHeight,
16
+ textLine
17
+ }) => applyTextStyles({
18
+ color,
19
+ fontName,
20
+ fontSize,
21
+ fontWeight,
22
+ lineHeight,
23
+ textDecorationLine: textLine
24
+ });
25
+
26
+ function PageButton({
27
+ label,
28
+ onPress,
29
+ href,
30
+ isActive,
31
+ copy,
32
+ variant,
33
+ tokens,
34
+ ...props
35
+ }) {
36
+ const getTokens = useThemeTokensCallback('PaginationPageButton', tokens, variant);
37
+ const getCopy = useCopy({
38
+ dictionary,
39
+ copy
40
+ });
41
+
42
+ const getButtonTokens = buttonState => selectTokens('Button', getTokens(buttonState));
43
+
44
+ const getTextStyles = buttonState => selectTextStyles(getTokens(buttonState));
45
+
46
+ const activeProps = isActive ? {
47
+ selected: true,
48
+ ...a11yProps.nonFocusableProps,
49
+ // a brute fix for the focus state being stuck on an active item since it becomes non-focusable
50
+ // (see https://github.com/telus/universal-design-system/pull/577#issuecomment-931344107)
51
+ key: 'active-item'
52
+ } : {};
53
+ const accessibilityRole = href !== undefined ? 'link' : 'button';
54
+ const activeLabel = isActive ? ` ${getCopy('currentLabel')}` : '';
55
+ const accessibilityLabel = `${getCopy('goToLabel')} ${label}${activeLabel}`;
56
+ const {
57
+ hrefAttrs,
58
+ rest
59
+ } = hrefAttrsProp.bundle(props);
60
+ const buttonProps = {
61
+ accessibilityRole,
62
+ accessibilityLabel,
63
+ onPress,
64
+ href,
65
+ hrefAttrs,
66
+ ...rest
67
+ };
68
+ return /*#__PURE__*/React.createElement(ButtonBase, Object.assign({}, buttonProps, {
69
+ tokens: getButtonTokens
70
+ }, activeProps), buttonState => {
71
+ return /*#__PURE__*/React.createElement(Text, {
72
+ style: getTextStyles(buttonState)
73
+ }, label);
74
+ });
75
+ }
76
+
77
+ PageButton.propTypes = {
78
+ label: PropTypes.string,
79
+ isActive: PropTypes.bool,
80
+ copy: copyPropTypes,
81
+ variant: variantProp.propType,
82
+ tokens: getTokensPropType('PaginationPageButton'),
83
+ ...linkProps.types
84
+ };
85
+ export default PageButton;
@@ -0,0 +1,118 @@
1
+ import React from 'react';
2
+ import { View, Text, StyleSheet } from 'react-native';
3
+ import { componentPropType, copyPropTypes, getTokensPropType, variantProp } from '../utils';
4
+ import { applyTextStyles, useThemeTokens } from '../ThemeProvider';
5
+ import { useViewport } from '../ViewportProvider';
6
+ import Box from '../Box';
7
+ import usePagination from './usePagination';
8
+ import PageButton from './PageButton';
9
+ import SideButton from './SideButton';
10
+
11
+ const selectTextStyles = ({
12
+ color,
13
+ fontName,
14
+ fontSize,
15
+ fontWeight,
16
+ lineHeight
17
+ }) => applyTextStyles({
18
+ color,
19
+ fontName,
20
+ fontSize,
21
+ fontWeight,
22
+ lineHeight
23
+ });
24
+
25
+ function Pagination({
26
+ children,
27
+ copy = 'en',
28
+ variant,
29
+ tokens,
30
+ sideButtonVariant,
31
+ sideButtonTokens
32
+ }) {
33
+ const viewport = useViewport();
34
+ const {
35
+ truncateAbove,
36
+ gap,
37
+ ...themeTokens
38
+ } = useThemeTokens('Pagination', tokens, variant, {
39
+ viewport
40
+ });
41
+ const items = React.Children.toArray(children);
42
+ const {
43
+ isItemActive,
44
+ getItemProps,
45
+ showPrevious,
46
+ showNext,
47
+ nextItemProps,
48
+ previousItemProps,
49
+ shouldRenderButton,
50
+ shouldRenderEllipsis
51
+ } = usePagination({
52
+ items,
53
+ truncateAbove
54
+ });
55
+ const ellipsisTextStyles = selectTextStyles(themeTokens);
56
+
57
+ if (items.length === 0) {
58
+ return null;
59
+ } // TODO: replace with a spacing component when https://github.com/telus/universal-design-system/pull/531 is implemented
60
+ // concatenate all items to easily wrap them in identical spacing components
61
+
62
+
63
+ const buttons = [showPrevious && /*#__PURE__*/React.createElement(SideButton, Object.assign({}, previousItemProps, {
64
+ direction: "previous",
65
+ copy: copy,
66
+ variant: sideButtonVariant,
67
+ tokens: sideButtonTokens
68
+ })), ...items.map((child, itemIndex) => {
69
+ const buttonLabel = `${itemIndex + 1}`;
70
+ const itemProps = getItemProps(itemIndex);
71
+
72
+ if (shouldRenderButton(itemIndex)) {
73
+ return /*#__PURE__*/React.createElement(PageButton, Object.assign({}, itemProps, {
74
+ label: buttonLabel,
75
+ copy: copy,
76
+ isActive: isItemActive(itemIndex)
77
+ }));
78
+ }
79
+
80
+ if (shouldRenderEllipsis(itemIndex)) {
81
+ return /*#__PURE__*/React.createElement(Text, {
82
+ style: ellipsisTextStyles
83
+ }, "...");
84
+ }
85
+
86
+ return null;
87
+ }), showNext && /*#__PURE__*/React.createElement(SideButton, Object.assign({}, nextItemProps, {
88
+ direction: "next",
89
+ copy: copy,
90
+ variant: sideButtonVariant,
91
+ tokens: sideButtonTokens
92
+ }))];
93
+ return /*#__PURE__*/React.createElement(View, {
94
+ style: staticStyles.container
95
+ }, buttons // keep the keys in-line with the page numbers regardless of which buttons are actually rendered
96
+ .map((element, index) => [element, `page-${index + 1}`]).filter(([element]) => element !== null).map(([element, key]) => /*#__PURE__*/React.createElement(Box, {
97
+ right: gap,
98
+ key: key
99
+ }, element)));
100
+ }
101
+
102
+ Pagination.PageButton = PageButton;
103
+ Pagination.propTypes = {
104
+ children: componentPropType('PageButton'),
105
+ copy: copyPropTypes,
106
+ variant: variantProp.propType,
107
+ tokens: getTokensPropType('Pagination'),
108
+ sideButtonVariant: variantProp.propType,
109
+ sideButtonTokens: getTokensPropType('PaginationSideButton')
110
+ };
111
+ const staticStyles = StyleSheet.create({
112
+ container: {
113
+ flexDirection: 'row',
114
+ flexWrap: 'wrap',
115
+ alignItems: 'center'
116
+ }
117
+ });
118
+ export default Pagination;
@@ -0,0 +1,108 @@
1
+ import React from 'react';
2
+ import { StyleSheet, View, Text, PixelRatio } from 'react-native';
3
+ import PropTypes from 'prop-types';
4
+ import ButtonBase from '../Button/ButtonBase';
5
+ import { applyTextStyles, useThemeTokensCallback } from '../ThemeProvider';
6
+ import { useViewport } from '../ViewportProvider';
7
+ import { copyPropTypes, hrefAttrsProp, linkProps, selectTokens } from '../utils';
8
+ import dictionary from './dictionary';
9
+ import useCopy from './useCopy';
10
+
11
+ const selectTextStyles = ({
12
+ color,
13
+ fontName,
14
+ fontSize,
15
+ fontWeight,
16
+ lineHeight
17
+ }) => applyTextStyles({
18
+ color,
19
+ fontName,
20
+ fontSize,
21
+ fontWeight,
22
+ lineHeight
23
+ });
24
+
25
+ const selectIconTokens = ({
26
+ color,
27
+ iconSize,
28
+ iconTranslateX
29
+ }) => {
30
+ // Scale icon with text, but with a cap so text isn't squashed at large scales
31
+ const iconScale = Math.min(PixelRatio.getFontScale(), 2);
32
+ return {
33
+ color,
34
+ size: iconSize * iconScale,
35
+ translateX: iconTranslateX
36
+ };
37
+ };
38
+
39
+ function SideButton({
40
+ direction = 'previous',
41
+ onPress,
42
+ href,
43
+ copy,
44
+ variant,
45
+ tokens,
46
+ ...props
47
+ }) {
48
+ const viewport = useViewport();
49
+ const buttonVariant = {
50
+ direction,
51
+ viewport,
52
+ ...variant
53
+ };
54
+ const getTokens = useThemeTokensCallback('PaginationSideButton', tokens, buttonVariant);
55
+ const getCopy = useCopy({
56
+ dictionary,
57
+ copy
58
+ });
59
+ const {
60
+ icon: IconComponent
61
+ } = getTokens(tokens, buttonVariant);
62
+
63
+ const getButtonTokens = buttonState => selectTokens('Button', getTokens(buttonState));
64
+
65
+ const getIconTokens = buttonState => selectIconTokens(getTokens(buttonState));
66
+
67
+ const getTextStyles = buttonState => selectTextStyles(getTokens(buttonState));
68
+
69
+ const label = direction === 'previous' ? getCopy('previousText') : getCopy('nextText');
70
+ const showLabel = viewport !== 'sm' && viewport !== 'xs';
71
+ const accessibilityLabel = direction === 'previous' ? getCopy('previousLabel') : getCopy('nextLabel');
72
+ const accessibilityRole = href !== undefined ? 'link' : 'button';
73
+ const {
74
+ hrefAttrs,
75
+ rest
76
+ } = hrefAttrsProp.bundle(props);
77
+ const buttonProps = {
78
+ accessibilityRole,
79
+ accessibilityLabel,
80
+ onPress,
81
+ href,
82
+ hrefAttrs,
83
+ ...rest
84
+ };
85
+ return /*#__PURE__*/React.createElement(ButtonBase, Object.assign({}, buttonProps, {
86
+ tokens: getButtonTokens
87
+ }), buttonState => /*#__PURE__*/React.createElement(View, {
88
+ style: staticStyles.contentContainer
89
+ }, direction === 'previous' && IconComponent && /*#__PURE__*/React.createElement(IconComponent, {
90
+ tokens: getIconTokens(buttonState)
91
+ }), showLabel && /*#__PURE__*/React.createElement(Text, {
92
+ style: getTextStyles(buttonState)
93
+ }, label), direction === 'next' && IconComponent && /*#__PURE__*/React.createElement(IconComponent, {
94
+ tokens: getIconTokens(buttonState)
95
+ })));
96
+ }
97
+
98
+ SideButton.propTypes = {
99
+ direction: PropTypes.oneOf(['previous', 'next']),
100
+ copy: copyPropTypes,
101
+ ...linkProps.types
102
+ };
103
+ export default SideButton;
104
+ const staticStyles = StyleSheet.create({
105
+ contentContainer: {
106
+ flexDirection: 'row'
107
+ }
108
+ });
@@ -0,0 +1,18 @@
1
+ export default {
2
+ en: {
3
+ goToLabel: 'Go to page number',
4
+ currentLabel: '(current)',
5
+ previousLabel: 'Go to previous page',
6
+ previousText: 'Previous',
7
+ nextLabel: 'Go to next page',
8
+ nextText: 'Next'
9
+ },
10
+ fr: {
11
+ goToLabel: 'Aller au page n°',
12
+ currentLabel: '(page actuelle)',
13
+ previousLabel: 'Aller au page précédent',
14
+ previousText: 'Précédent',
15
+ nextLabel: 'Aller au prochain page',
16
+ nextText: 'Suivant'
17
+ }
18
+ };
@@ -0,0 +1,2 @@
1
+ import Pagination from './Pagination';
2
+ export default Pagination;
@@ -0,0 +1,10 @@
1
+ function useCopy({
2
+ dictionary,
3
+ copy
4
+ }) {
5
+ const getCopy = key => dictionary[copy][key];
6
+
7
+ return getCopy;
8
+ }
9
+
10
+ export default useCopy;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Handles configurable truncating of pagination items.
3
+ *
4
+ * @param {object} props
5
+ * @param {React.Element[]} items
6
+ * @param {number} truncateAbove
7
+ */
8
+ function usePagination({
9
+ items,
10
+ truncateAbove
11
+ }) {
12
+ const activeItemIndex = Math.max(items.findIndex(item => item.props.isActive), 0 // default to the first item if none is marked as active
13
+ );
14
+
15
+ const getItemProps = index => {
16
+ const {
17
+ onPress,
18
+ href,
19
+ hrefAttrs,
20
+ variant,
21
+ tokens
22
+ } = items[index]?.props ?? {};
23
+ return {
24
+ onPress,
25
+ href,
26
+ hrefAttrs,
27
+ variant,
28
+ tokens
29
+ };
30
+ };
31
+
32
+ const windowSize = truncateAbove > 4 ? 3 : 1;
33
+ const truncateEnabled = items.length > truncateAbove;
34
+ const truncateWindowStart = windowSize;
35
+ const truncateWindowEnd = items.length - 1 - windowSize; // basically how many items next to the currently active one do we want to render
36
+
37
+ const activeWindowStart = activeItemIndex - (windowSize - 1) / 2;
38
+ const activeWindowEnd = activeItemIndex + (windowSize - 1) / 2;
39
+ const isFirstActive = activeItemIndex === 0;
40
+ const isLastActive = activeItemIndex === items.length - 1; // start truncating when the active item is within the truncate window
41
+
42
+ const isLeftTruncated = truncateEnabled && activeItemIndex >= truncateWindowStart;
43
+ const isRightTruncated = truncateEnabled && activeItemIndex <= truncateWindowEnd;
44
+
45
+ function shouldRenderEllipsis(itemIndex) {
46
+ const isNextToFirst = itemIndex === 1;
47
+ const isNextToLast = itemIndex === items.length - 2;
48
+ return isLeftTruncated && isNextToFirst || isRightTruncated && isNextToLast;
49
+ }
50
+
51
+ function shouldRenderButton(itemIndex) {
52
+ const isFirst = itemIndex === 0;
53
+ const isLast = itemIndex === items.length - 1;
54
+ const isInActiveWindow = itemIndex >= activeWindowStart && itemIndex <= activeWindowEnd;
55
+ return isFirst || isLast || isInActiveWindow || !isLeftTruncated && itemIndex <= truncateWindowStart || !isRightTruncated && itemIndex >= truncateWindowEnd;
56
+ }
57
+
58
+ return {
59
+ showPrevious: !isFirstActive,
60
+ showNext: !isLastActive,
61
+ shouldRenderButton,
62
+ shouldRenderEllipsis,
63
+ isItemActive: itemIndex => itemIndex === activeItemIndex,
64
+ getItemProps,
65
+ nextItemProps: getItemProps(activeItemIndex + 1),
66
+ previousItemProps: getItemProps(activeItemIndex - 1)
67
+ };
68
+ }
69
+
70
+ export default usePagination;
@@ -1,5 +1,3 @@
1
- function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
-
3
1
  import React from 'react';
4
2
  import { Pressable } from 'react-native';
5
3
  import PropTypes from 'prop-types';
@@ -66,7 +64,7 @@ const Item = ({
66
64
  ...hrefAttrsProp.spread(hrefAttrs),
67
65
  ...rest
68
66
  });
69
- const getTokens = useThemeTokensCallback('SideNavItem');
67
+ const getTokens = useThemeTokensCallback('SideNavItem', tokens, variant);
70
68
 
71
69
  const getAppearanceState = ({
72
70
  hovered
@@ -76,9 +74,9 @@ const Item = ({
76
74
  expanded: isExpanded
77
75
  });
78
76
 
79
- const getPressableStyle = pressableState => selectItemStyles(getTokens(tokens, variant, getAppearanceState(pressableState)));
77
+ const getPressableStyle = pressableState => selectItemStyles(getTokens(getAppearanceState(pressableState)));
80
78
 
81
- return /*#__PURE__*/React.createElement(Pressable, _extends({
79
+ return /*#__PURE__*/React.createElement(Pressable, Object.assign({
82
80
  style: getPressableStyle
83
81
  }, linkPropSet, {
84
82
  accessibilityState: {
@@ -86,7 +84,7 @@ const Item = ({
86
84
  },
87
85
  testID: testID
88
86
  }), pressableState => {
89
- const themeTokens = getTokens(tokens, variant, getAppearanceState(pressableState));
87
+ const themeTokens = getTokens(getAppearanceState(pressableState));
90
88
  return /*#__PURE__*/React.createElement(ItemContent, {
91
89
  tokens: themeTokens
92
90
  }, children);