@telus-uds/components-base 4.0.0-alpha.0 → 4.0.0-alpha.2

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 (82) hide show
  1. package/CHANGELOG.md +62 -6
  2. package/jest.config.cjs +1 -0
  3. package/lib/cjs/ActionCard/ActionCard.js +10 -3
  4. package/lib/cjs/Card/Card.js +3 -3
  5. package/lib/cjs/Carousel/Carousel.js +6 -1
  6. package/lib/cjs/Carousel/CarouselThumbnail.js +125 -33
  7. package/lib/cjs/Carousel/CarouselThumbnailNavigation.js +8 -1
  8. package/lib/cjs/Footnote/FootnoteLink.js +18 -17
  9. package/lib/cjs/InputSupports/InputSupports.js +1 -2
  10. package/lib/cjs/InputSupports/useInputSupports.js +1 -3
  11. package/lib/cjs/Link/ChevronLink.js +1 -2
  12. package/lib/cjs/Notification/Notification.js +4 -12
  13. package/lib/cjs/Progress/Progress.js +30 -5
  14. package/lib/cjs/Progress/ProgressBar.js +3 -3
  15. package/lib/cjs/Progress/ProgressBarBackground.js +2 -3
  16. package/lib/cjs/QuickLinks/QuickLinks.js +7 -0
  17. package/lib/cjs/Scroll/Scroll.js +472 -0
  18. package/lib/cjs/Scroll/ScrollContext.js +55 -0
  19. package/lib/cjs/Scroll/ScrollItem.js +106 -0
  20. package/lib/cjs/Scroll/ScrollProgress.js +100 -0
  21. package/lib/cjs/Scroll/dictionary.js +18 -0
  22. package/lib/cjs/StackView/StackWrapBox.js +9 -1
  23. package/lib/cjs/StackView/StackWrapGap.js +3 -1
  24. package/lib/cjs/StackView/getStackedContent.js +20 -10
  25. package/lib/cjs/index.js +14 -0
  26. package/lib/cjs/utils/animation/useAnimatedValue.js +46 -0
  27. package/lib/cjs/utils/children.js +2 -1
  28. package/lib/esm/ActionCard/ActionCard.js +10 -3
  29. package/lib/esm/Card/Card.js +3 -3
  30. package/lib/esm/Carousel/Carousel.js +6 -1
  31. package/lib/esm/Carousel/CarouselThumbnail.js +126 -34
  32. package/lib/esm/Carousel/CarouselThumbnailNavigation.js +8 -1
  33. package/lib/esm/Footnote/FootnoteLink.js +18 -17
  34. package/lib/esm/InputSupports/InputSupports.js +1 -2
  35. package/lib/esm/InputSupports/useInputSupports.js +1 -3
  36. package/lib/esm/Link/ChevronLink.js +1 -2
  37. package/lib/esm/Notification/Notification.js +4 -12
  38. package/lib/esm/Progress/Progress.js +30 -5
  39. package/lib/esm/Progress/ProgressBar.js +3 -3
  40. package/lib/esm/Progress/ProgressBarBackground.js +2 -3
  41. package/lib/esm/QuickLinks/QuickLinks.js +7 -0
  42. package/lib/esm/Scroll/Scroll.js +465 -0
  43. package/lib/esm/Scroll/ScrollContext.js +46 -0
  44. package/lib/esm/Scroll/ScrollItem.js +100 -0
  45. package/lib/esm/Scroll/ScrollProgress.js +93 -0
  46. package/lib/esm/Scroll/dictionary.js +12 -0
  47. package/lib/esm/StackView/StackWrapBox.js +9 -1
  48. package/lib/esm/StackView/StackWrapGap.js +3 -1
  49. package/lib/esm/StackView/getStackedContent.js +20 -10
  50. package/lib/esm/index.js +2 -0
  51. package/lib/esm/utils/animation/useAnimatedValue.js +38 -0
  52. package/lib/esm/utils/children.js +2 -1
  53. package/lib/package.json +2 -2
  54. package/package.json +2 -2
  55. package/src/ActionCard/ActionCard.jsx +13 -4
  56. package/src/Card/Card.jsx +3 -3
  57. package/src/Carousel/Carousel.jsx +6 -1
  58. package/src/Carousel/CarouselThumbnail.jsx +117 -30
  59. package/src/Carousel/CarouselThumbnailNavigation.jsx +8 -2
  60. package/src/Footnote/FootnoteLink.jsx +17 -12
  61. package/src/InputSupports/InputSupports.jsx +1 -6
  62. package/src/InputSupports/useInputSupports.js +1 -1
  63. package/src/Link/ChevronLink.jsx +1 -2
  64. package/src/Notification/Notification.jsx +5 -21
  65. package/src/Progress/Progress.jsx +22 -3
  66. package/src/Progress/ProgressBar.jsx +3 -3
  67. package/src/Progress/ProgressBarBackground.jsx +2 -3
  68. package/src/QuickLinks/QuickLinks.jsx +8 -0
  69. package/src/Scroll/Scroll.jsx +483 -0
  70. package/src/Scroll/ScrollContext.jsx +39 -0
  71. package/src/Scroll/ScrollItem.jsx +87 -0
  72. package/src/Scroll/ScrollProgress.jsx +74 -0
  73. package/src/Scroll/dictionary.js +12 -0
  74. package/src/StackView/StackWrapBox.jsx +13 -1
  75. package/src/StackView/StackWrapGap.jsx +2 -1
  76. package/src/StackView/getStackedContent.jsx +22 -8
  77. package/src/index.js +2 -0
  78. package/src/utils/animation/useAnimatedValue.js +35 -0
  79. package/src/utils/children.jsx +4 -1
  80. package/types/Link.d.ts +1 -2
  81. package/types/Scroll.d.ts +66 -0
  82. package/types/index.d.ts +9 -0
@@ -6,10 +6,12 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.QuickLinks = void 0;
7
7
  var _react = _interopRequireDefault(require("react"));
8
8
  var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
9
10
  var _useThemeTokens = require("../ThemeProvider/useThemeTokens");
10
11
  var _useViewport = require("../ViewportProvider/useViewport");
11
12
  var _tokens = require("../utils/props/tokens");
12
13
  var _variantProp = require("../utils/props/variantProp");
14
+ var _semantics = require("../utils/a11y/semantics");
13
15
  var _List = require("../List/List");
14
16
  var _StackWrap = require("../StackView/StackWrap");
15
17
  var _QuickLinksCard = require("./QuickLinksCard");
@@ -42,6 +44,10 @@ const QuickLinks = exports.QuickLinks = /*#__PURE__*/_react.default.forwardRef((
42
44
  } = (0, _useThemeTokens.useThemeTokens)('QuickLinks', tokens, variant, {
43
45
  viewport
44
46
  });
47
+ const itemAccessibilityRole = _Platform.default.select({
48
+ web: ['ul', 'ol'].includes(tag) ? _semantics.tagsToRoles.li : undefined,
49
+ default: undefined
50
+ });
45
51
  const content = list && /*#__PURE__*/(0, _jsxRuntime.jsx)(_List.List, {
46
52
  ref: ref,
47
53
  tokens: listTokens,
@@ -56,6 +62,7 @@ const QuickLinks = exports.QuickLinks = /*#__PURE__*/_react.default.forwardRef((
56
62
  justifyContent: stackJustify
57
63
  },
58
64
  tag: tag,
65
+ itemAccessibilityRole: itemAccessibilityRole,
59
66
  ...rest,
60
67
  children: children
61
68
  });
@@ -0,0 +1,472 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Scroll = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
10
+ var _Pressable = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Pressable"));
11
+ var _StyleSheet = _interopRequireDefault(require("react-native-web/dist/cjs/exports/StyleSheet"));
12
+ var _View = _interopRequireDefault(require("react-native-web/dist/cjs/exports/View"));
13
+ var _useThemeTokens = require("../ThemeProvider/useThemeTokens");
14
+ var _a11yProps = require("../utils/props/a11yProps");
15
+ var _copyPropTypes = require("../utils/props/copyPropTypes");
16
+ var _tokens = require("../utils/props/tokens");
17
+ var _selectSystemProps = require("../utils/props/selectSystemProps");
18
+ var _useCopy = require("../utils/useCopy");
19
+ var _useUniqueId = require("../utils/useUniqueId");
20
+ var _variantProp = require("../utils/props/variantProp");
21
+ var _viewProps = require("../utils/props/viewProps");
22
+ var _A11yText = require("../A11yText/A11yText");
23
+ var _ScrollViewEnd = require("../HorizontalScroll/ScrollViewEnd");
24
+ var _dictionary = require("./dictionary");
25
+ var _ScrollProgress = require("./ScrollProgress");
26
+ var _ScrollContext = require("./ScrollContext");
27
+ var _jsxRuntime = require("react/jsx-runtime");
28
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
29
+ const [selectProps, selectedSystemPropTypes] = (0, _selectSystemProps.selectSystemProps)([_a11yProps.a11yProps, _viewProps.viewProps]);
30
+ const SCROLL_DESCRIPTION_PREFIX = 'scroll-description';
31
+ const KEY_ARROW_RIGHT = 'ArrowRight';
32
+ const KEY_ARROW_LEFT = 'ArrowLeft';
33
+ const KEY_SPACE = ' ';
34
+ const MAX_PERCENTAGE = 100;
35
+ const MIN_BAR_PERCENTAGE = 1;
36
+ const ROLE_REGION = 'region';
37
+ const ROLE_NONE = 'none';
38
+ const COMPONENT_NAME = 'Scroll';
39
+ const SCROLL_ITEM_SELECTOR = '[data-scroll-item]';
40
+ const TAB_INDEX = 0;
41
+ const INITIAL_DIMENSION = 0;
42
+ const HALF_DIVISOR = 2;
43
+ const selectPressableStyles = _ref => {
44
+ let {
45
+ progressHitAreaHeight,
46
+ progressSpacing
47
+ } = _ref;
48
+ return {
49
+ height: progressHitAreaHeight,
50
+ marginTop: progressSpacing
51
+ };
52
+ };
53
+ const selectThumbWrapperStyles = _ref2 => {
54
+ let {
55
+ thumbWidthPercent,
56
+ thumbPositionPercent
57
+ } = _ref2;
58
+ return {
59
+ width: `${thumbWidthPercent}%`,
60
+ marginLeft: `${thumbPositionPercent}%`
61
+ };
62
+ };
63
+ const selectContainerA11yProps = _ref3 => {
64
+ let {
65
+ accessibilityLabel,
66
+ descriptionId,
67
+ handleKeyDown
68
+ } = _ref3;
69
+ return _Platform.default.OS === 'web' ? {
70
+ role: ROLE_REGION,
71
+ 'aria-label': accessibilityLabel,
72
+ 'aria-describedby': descriptionId,
73
+ tabIndex: TAB_INDEX,
74
+ onKeyDown: handleKeyDown
75
+ } : {
76
+ accessibilityRole: ROLE_NONE,
77
+ accessibilityLabel
78
+ };
79
+ };
80
+ const selectScrollContentStyle = itemGap => ({
81
+ columnGap: itemGap
82
+ });
83
+ const calculateThumbWidth = _ref4 => {
84
+ let {
85
+ containerWidth,
86
+ contentWidth
87
+ } = _ref4;
88
+ if (contentWidth <= INITIAL_DIMENSION) return MAX_PERCENTAGE;
89
+ return Math.max(MIN_BAR_PERCENTAGE, Math.min(MAX_PERCENTAGE, containerWidth / contentWidth * MAX_PERCENTAGE));
90
+ };
91
+ const calculateThumbPosition = _ref5 => {
92
+ let {
93
+ scrollOffset,
94
+ scrollMax,
95
+ thumbWidthPercent
96
+ } = _ref5;
97
+ if (scrollMax <= INITIAL_DIMENSION) return INITIAL_DIMENSION;
98
+ return scrollOffset / scrollMax * (MAX_PERCENTAGE - thumbWidthPercent);
99
+ };
100
+ const getScrollableNode = ref => ref.current?.getScrollableNode?.() ?? null;
101
+
102
+ /**
103
+ * The `Scroll` component provides a continuous horizontal scrollable container with a progress
104
+ * bar indicator. Items are displayed in a flow layout where all items share the width of the
105
+ * widest item and the height of the tallest item.
106
+ *
107
+ * ## Props
108
+ *
109
+ * - Use `children` to pass `ScrollItem` components
110
+ * - Use `accessibilityLabel` to provide a meaningful label for the scrollable region
111
+ * - Use `copy` to select English or French for accessible labels
112
+ * - Use `dictionary` to override the default translations
113
+ *
114
+ * ## Usability and A11y guidelines
115
+ *
116
+ * - The scroll container is keyboard-focusable with `tabIndex="0"`
117
+ * - Each `ScrollItem` is individually focusable via Tab navigation
118
+ * - Use left and right arrow keys to scroll by one item width
119
+ * - Press Space bar to scroll right
120
+ * - When tabbing through items, the scroll follows focus to keep the active item visible
121
+ * - Screen readers announce the component as a region with a descriptive label
122
+ * - Touch and swipe gestures are supported on mobile and mobile web
123
+ *
124
+ */
125
+ const Scroll = exports.Scroll = /*#__PURE__*/_react.default.forwardRef((_ref6, ref) => {
126
+ let {
127
+ children,
128
+ tokens,
129
+ variant,
130
+ copy,
131
+ dictionary: customDictionary = _dictionary.dictionary,
132
+ accessibilityLabel,
133
+ ...rest
134
+ } = _ref6;
135
+ const getCopy = (0, _useCopy.useCopy)({
136
+ dictionary: customDictionary,
137
+ copy
138
+ });
139
+ const resolvedAccessibilityLabel = accessibilityLabel || getCopy('accessibilityLabel');
140
+ const getTokens = (0, _useThemeTokens.useThemeTokensCallback)(COMPONENT_NAME, tokens, variant);
141
+ const defaultTokens = _react.default.useMemo(() => getTokens(), [getTokens]);
142
+ const descriptionId = (0, _useUniqueId.useUniqueId)(SCROLL_DESCRIPTION_PREFIX);
143
+ const scrollRef = _react.default.useRef(null);
144
+ const pressableRef = _react.default.useRef(null);
145
+ const scrollContainerRef = _react.default.useRef(null);
146
+ const scrollMaxRef = _react.default.useRef(INITIAL_DIMENSION);
147
+ const scrollOffsetRef = _react.default.useRef(INITIAL_DIMENSION);
148
+ const thumbWidthPercentRef = _react.default.useRef(MAX_PERCENTAGE);
149
+ const thumbRef = _react.default.useRef(null);
150
+ const isDraggingRef = _react.default.useRef(false);
151
+ const dragStartRef = _react.default.useRef({
152
+ mouseX: INITIAL_DIMENSION,
153
+ scrollOffset: INITIAL_DIMENSION
154
+ });
155
+ const [scrollOffset, setScrollOffset] = _react.default.useState(INITIAL_DIMENSION);
156
+ const [containerWidth, setContainerWidth] = _react.default.useState(INITIAL_DIMENSION);
157
+ const [contentWidth, setContentWidth] = _react.default.useState(INITIAL_DIMENSION);
158
+ const [itemWidth, setItemWidth] = _react.default.useState(INITIAL_DIMENSION);
159
+ const [itemHeight, setItemHeight] = _react.default.useState(INITIAL_DIMENSION);
160
+ const {
161
+ itemGap
162
+ } = defaultTokens;
163
+ const scrollMax = Math.max(INITIAL_DIMENSION, contentWidth - containerWidth);
164
+ _react.default.useEffect(() => {
165
+ scrollMaxRef.current = scrollMax;
166
+ }, [scrollMax]);
167
+ const thumbWidthPercent = calculateThumbWidth({
168
+ containerWidth,
169
+ contentWidth
170
+ });
171
+ thumbWidthPercentRef.current = thumbWidthPercent;
172
+ const thumbPositionPercent = calculateThumbPosition({
173
+ scrollOffset,
174
+ scrollMax,
175
+ thumbWidthPercent
176
+ });
177
+ const thumbWrapperStyle = _react.default.useMemo(() => [staticStyles.thumbWrapper, selectThumbWrapperStyles({
178
+ thumbWidthPercent,
179
+ thumbPositionPercent
180
+ }), _Platform.default.OS === 'web' ? staticStyles.thumbCursor : null], [thumbWidthPercent, thumbPositionPercent]);
181
+ const handleScroll = _react.default.useCallback(_ref7 => {
182
+ let {
183
+ nativeEvent: {
184
+ contentOffset: {
185
+ x
186
+ }
187
+ }
188
+ } = _ref7;
189
+ scrollOffsetRef.current = x;
190
+ setScrollOffset(x);
191
+ }, []);
192
+ const handleContainerLayout = _react.default.useCallback(_ref8 => {
193
+ let {
194
+ nativeEvent: {
195
+ layout: {
196
+ width
197
+ }
198
+ }
199
+ } = _ref8;
200
+ setContainerWidth(width);
201
+ }, []);
202
+ const handleContentWidth = _react.default.useCallback(width => {
203
+ setContentWidth(width);
204
+ }, []);
205
+ const handleItemLayout = _react.default.useCallback((width, height) => {
206
+ setItemWidth(prev => Math.max(prev, width));
207
+ setItemHeight(prev => Math.max(prev, height));
208
+ }, []);
209
+ _react.default.useEffect(() => {
210
+ if (_Platform.default.OS !== 'web') return undefined;
211
+ const element = scrollContainerRef.current;
212
+ if (!element) return undefined;
213
+ const handleWheel = event => {
214
+ event.preventDefault();
215
+ event.stopPropagation();
216
+ const isHorizontal = Math.abs(event.deltaX) > Math.abs(event.deltaY);
217
+ const delta = isHorizontal ? event.deltaX : event.deltaY;
218
+ const scrollableNode = getScrollableNode(scrollRef);
219
+ if (scrollableNode) {
220
+ const maxScroll = scrollMaxRef.current;
221
+ const newScrollLeft = Math.max(INITIAL_DIMENSION, Math.min(maxScroll, scrollableNode.scrollLeft + delta));
222
+ scrollableNode.scrollLeft = newScrollLeft;
223
+ scrollOffsetRef.current = newScrollLeft;
224
+ setScrollOffset(newScrollLeft);
225
+ }
226
+ };
227
+ element.addEventListener('wheel', handleWheel, {
228
+ passive: false
229
+ });
230
+ return () => element.removeEventListener('wheel', handleWheel);
231
+ }, []);
232
+ _react.default.useEffect(() => {
233
+ if (_Platform.default.OS !== 'web') return undefined;
234
+ const element = scrollContainerRef.current;
235
+ if (!element) return undefined;
236
+ const handleFocusIn = event => {
237
+ const scrollableNode = getScrollableNode(scrollRef);
238
+ if (!scrollableNode) return;
239
+ const focusedItem = event.target.closest(SCROLL_ITEM_SELECTOR);
240
+ if (!focusedItem) return;
241
+ const itemLeft = focusedItem.offsetLeft;
242
+ const itemRight = itemLeft + focusedItem.offsetWidth;
243
+ const visibleLeft = scrollableNode.scrollLeft;
244
+ const currentContainerWidth = containerWidth;
245
+ const visibleRight = visibleLeft + currentContainerWidth;
246
+ if (itemRight > visibleRight) {
247
+ scrollRef.current.scrollTo({
248
+ x: itemRight - currentContainerWidth,
249
+ animated: true
250
+ });
251
+ } else if (itemLeft < visibleLeft) {
252
+ scrollRef.current.scrollTo({
253
+ x: itemLeft,
254
+ animated: true
255
+ });
256
+ }
257
+ };
258
+ element.addEventListener('focusin', handleFocusIn);
259
+ return () => element.removeEventListener('focusin', handleFocusIn);
260
+ }, [containerWidth]);
261
+ const hasScrollbar = scrollMax > INITIAL_DIMENSION;
262
+ _react.default.useEffect(() => {
263
+ if (_Platform.default.OS !== 'web' || !hasScrollbar) return undefined;
264
+ const thumbElement = thumbRef.current;
265
+ if (!thumbElement) return undefined;
266
+ const handleMouseDown = event => {
267
+ event.preventDefault();
268
+ event.stopPropagation();
269
+ isDraggingRef.current = true;
270
+ dragStartRef.current = {
271
+ mouseX: event.clientX,
272
+ scrollOffset: getScrollableNode(scrollRef)?.scrollLeft ?? INITIAL_DIMENSION
273
+ };
274
+ thumbElement.style.cursor = 'grabbing';
275
+ document.body.style.cursor = 'grabbing';
276
+ document.body.style.userSelect = 'none';
277
+ };
278
+ const handleMouseMove = event => {
279
+ if (!isDraggingRef.current) return;
280
+ const trackElement = pressableRef.current;
281
+ if (!trackElement) return;
282
+ const trackWidth = trackElement.offsetWidth;
283
+ const thumbPixelWidth = thumbWidthPercentRef.current / MAX_PERCENTAGE * trackWidth;
284
+ const availableTrackPixels = trackWidth - thumbPixelWidth;
285
+ if (availableTrackPixels <= INITIAL_DIMENSION) return;
286
+ const mouseDelta = event.clientX - dragStartRef.current.mouseX;
287
+ const scrollDelta = mouseDelta / availableTrackPixels * scrollMaxRef.current;
288
+ const targetScroll = Math.max(INITIAL_DIMENSION, Math.min(scrollMaxRef.current, dragStartRef.current.scrollOffset + scrollDelta));
289
+ const scrollableNode = getScrollableNode(scrollRef);
290
+ if (scrollableNode) {
291
+ scrollableNode.scrollLeft = targetScroll;
292
+ scrollOffsetRef.current = targetScroll;
293
+ setScrollOffset(targetScroll);
294
+ }
295
+ };
296
+ const handleMouseUp = () => {
297
+ if (!isDraggingRef.current) return;
298
+ isDraggingRef.current = false;
299
+ thumbElement.style.cursor = '';
300
+ document.body.style.cursor = '';
301
+ document.body.style.userSelect = '';
302
+ };
303
+ thumbElement.addEventListener('mousedown', handleMouseDown);
304
+ document.addEventListener('mousemove', handleMouseMove);
305
+ document.addEventListener('mouseup', handleMouseUp);
306
+ return () => {
307
+ thumbElement.removeEventListener('mousedown', handleMouseDown);
308
+ document.removeEventListener('mousemove', handleMouseMove);
309
+ document.removeEventListener('mouseup', handleMouseUp);
310
+ };
311
+ }, [hasScrollbar]);
312
+ const handleKeyDown = _react.default.useCallback(event => {
313
+ if (event.key === KEY_ARROW_RIGHT || event.key === KEY_SPACE) {
314
+ event.preventDefault();
315
+ if (scrollRef.current) {
316
+ scrollRef.current.scrollTo({
317
+ x: scrollOffsetRef.current + itemWidth,
318
+ animated: true
319
+ });
320
+ }
321
+ } else if (event.key === KEY_ARROW_LEFT) {
322
+ event.preventDefault();
323
+ if (scrollRef.current) {
324
+ scrollRef.current.scrollTo({
325
+ x: Math.max(INITIAL_DIMENSION, scrollOffsetRef.current - itemWidth),
326
+ animated: true
327
+ });
328
+ }
329
+ }
330
+ }, [itemWidth]);
331
+ const handleProgressPress = _react.default.useCallback(event => {
332
+ if (scrollMax <= INITIAL_DIMENSION || isDraggingRef.current) return;
333
+ if (_Platform.default.OS === 'web' && pressableRef.current) {
334
+ const {
335
+ offsetX
336
+ } = event.nativeEvent;
337
+ const {
338
+ offsetWidth
339
+ } = pressableRef.current;
340
+ const thumbPixelWidth = thumbWidthPercent / MAX_PERCENTAGE * offsetWidth;
341
+ const availableTrack = offsetWidth - thumbPixelWidth;
342
+ if (availableTrack <= INITIAL_DIMENSION) return;
343
+ const clickPosition = Math.max(INITIAL_DIMENSION, Math.min(availableTrack, offsetX - thumbPixelWidth / HALF_DIVISOR));
344
+ const targetX = clickPosition / availableTrack * scrollMax;
345
+ if (scrollRef.current) {
346
+ scrollRef.current.scrollTo({
347
+ x: targetX,
348
+ animated: true
349
+ });
350
+ }
351
+ } else if (pressableRef.current) {
352
+ const {
353
+ locationX
354
+ } = event.nativeEvent;
355
+ pressableRef.current.measure((_x, _y, width) => {
356
+ const thumbPixelWidth = thumbWidthPercent / MAX_PERCENTAGE * width;
357
+ const availableTrack = width - thumbPixelWidth;
358
+ if (availableTrack <= INITIAL_DIMENSION) return;
359
+ const clickPosition = Math.max(INITIAL_DIMENSION, Math.min(availableTrack, locationX - thumbPixelWidth / HALF_DIVISOR));
360
+ const targetX = clickPosition / availableTrack * scrollMax;
361
+ if (scrollRef.current) {
362
+ scrollRef.current.scrollTo({
363
+ x: targetX,
364
+ animated: true
365
+ });
366
+ }
367
+ });
368
+ }
369
+ }, [scrollMax, thumbWidthPercent]);
370
+ const handleContainerRef = _react.default.useCallback(node => {
371
+ scrollContainerRef.current = node;
372
+ if (typeof ref === 'function') ref(node);else if (ref) Object.assign(ref, {
373
+ current: node
374
+ });
375
+ }, [ref]);
376
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_View.default, {
377
+ ref: handleContainerRef,
378
+ style: staticStyles.container,
379
+ ...selectContainerA11yProps({
380
+ accessibilityLabel: resolvedAccessibilityLabel,
381
+ descriptionId,
382
+ handleKeyDown
383
+ }),
384
+ ...selectProps(rest),
385
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_A11yText.A11yText, {
386
+ text: getCopy('instructionText'),
387
+ nativeID: descriptionId
388
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
389
+ style: staticStyles.scrollContainer,
390
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ScrollViewEnd.ScrollViewEnd, {
391
+ ref: scrollRef,
392
+ horizontal: true,
393
+ onScrollEnd: handleScroll,
394
+ onScroll: handleScroll,
395
+ onContentSizeChange: handleContentWidth,
396
+ onLayout: handleContainerLayout,
397
+ showsHorizontalScrollIndicator: false,
398
+ contentContainerStyle: [staticStyles.scrollContent, selectScrollContentStyle(itemGap)],
399
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ScrollContext.ScrollProvider, {
400
+ itemWidth: itemWidth,
401
+ itemHeight: itemHeight,
402
+ onItemLayout: handleItemLayout,
403
+ children: children
404
+ })
405
+ })
406
+ }), scrollMax > INITIAL_DIMENSION && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Pressable.default, {
407
+ ref: pressableRef,
408
+ style: [staticStyles.pressable, selectPressableStyles(defaultTokens)],
409
+ onPress: handleProgressPress,
410
+ children: pressableState => /*#__PURE__*/(0, _jsxRuntime.jsx)(_ScrollProgress.ScrollProgress, {
411
+ ref: thumbRef,
412
+ pressableState: pressableState,
413
+ getTokens: getTokens,
414
+ thumbWrapperStyle: thumbWrapperStyle,
415
+ getCopy: getCopy
416
+ })
417
+ })]
418
+ });
419
+ });
420
+ Scroll.displayName = 'Scroll';
421
+ Scroll.propTypes = {
422
+ ...selectedSystemPropTypes,
423
+ /**
424
+ * Scroll items to display. Should be `ScrollItem` components.
425
+ */
426
+ children: _propTypes.default.node.isRequired,
427
+ /**
428
+ * Theme tokens for customizing the Scroll appearance
429
+ */
430
+ tokens: (0, _tokens.getTokensPropType)('Scroll'),
431
+ /**
432
+ * Variant for the Scroll component
433
+ */
434
+ variant: _variantProp.variantProp.propType,
435
+ /**
436
+ * Select English or French copy for accessible labels.
437
+ */
438
+ copy: _copyPropTypes.copyPropTypes,
439
+ /**
440
+ * Override the default dictionary by passing the complete dictionary object for `en` and `fr`.
441
+ */
442
+ dictionary: _propTypes.default.shape({
443
+ en: _propTypes.default.object,
444
+ fr: _propTypes.default.object
445
+ }),
446
+ /**
447
+ * Accessibility label for the scrollable region
448
+ */
449
+ accessibilityLabel: _propTypes.default.string
450
+ };
451
+ const staticStyles = _StyleSheet.default.create({
452
+ container: {
453
+ width: '100%'
454
+ },
455
+ scrollContainer: {
456
+ width: '100%'
457
+ },
458
+ pressable: {
459
+ justifyContent: 'center',
460
+ width: '100%'
461
+ },
462
+ scrollContent: {
463
+ alignSelf: 'flex-start',
464
+ flexGrow: 1
465
+ },
466
+ thumbWrapper: {
467
+ height: '100%'
468
+ },
469
+ thumbCursor: {
470
+ cursor: 'grab'
471
+ }
472
+ });
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ScrollProvider = void 0;
7
+ exports.useScroll = useScroll;
8
+ var _react = _interopRequireDefault(require("react"));
9
+ var _propTypes = _interopRequireDefault(require("prop-types"));
10
+ var _jsxRuntime = require("react/jsx-runtime");
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ const ScrollContext = /*#__PURE__*/_react.default.createContext();
13
+ const ScrollProvider = _ref => {
14
+ let {
15
+ children,
16
+ itemWidth,
17
+ itemHeight,
18
+ onItemLayout
19
+ } = _ref;
20
+ const value = _react.default.useMemo(() => ({
21
+ itemWidth,
22
+ itemHeight,
23
+ onItemLayout
24
+ }), [itemWidth, itemHeight, onItemLayout]);
25
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(ScrollContext.Provider, {
26
+ value: value,
27
+ children: children
28
+ });
29
+ };
30
+ exports.ScrollProvider = ScrollProvider;
31
+ function useScroll() {
32
+ const context = _react.default.useContext(ScrollContext);
33
+ if (context === undefined) {
34
+ throw new Error(`'useScroll' must be used within a 'ScrollProvider'`);
35
+ }
36
+ return context;
37
+ }
38
+ ScrollProvider.propTypes = {
39
+ /**
40
+ * Content to be rendered within the scroll context provider.
41
+ */
42
+ children: _propTypes.default.node.isRequired,
43
+ /**
44
+ * Width of each scroll item in pixels.
45
+ */
46
+ itemWidth: _propTypes.default.number.isRequired,
47
+ /**
48
+ * Height of each scroll item in pixels.
49
+ */
50
+ itemHeight: _propTypes.default.number.isRequired,
51
+ /**
52
+ * Callback invoked when a scroll item layout is measured.
53
+ */
54
+ onItemLayout: _propTypes.default.func.isRequired
55
+ };
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ScrollItem = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
10
+ var _View = _interopRequireDefault(require("react-native-web/dist/cjs/exports/View"));
11
+ var _a11yProps = require("../utils/props/a11yProps");
12
+ var _selectSystemProps = require("../utils/props/selectSystemProps");
13
+ var _viewProps = require("../utils/props/viewProps");
14
+ var _semantics = require("../utils/a11y/semantics");
15
+ var _ScrollContext = require("./ScrollContext");
16
+ var _jsxRuntime = require("react/jsx-runtime");
17
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
18
+ const [selectProps, selectedSystemPropTypes] = (0, _selectSystemProps.selectSystemProps)([_a11yProps.a11yProps, _viewProps.viewProps]);
19
+ const DEFAULT_TAG = 'li';
20
+ const DATA_SCROLL_ITEM = 'scrollItem';
21
+ const INITIAL_DIMENSION = 0;
22
+ const TAB_INDEX = 0;
23
+ const selectContainerStyle = _ref => {
24
+ let {
25
+ itemWidth,
26
+ itemHeight
27
+ } = _ref;
28
+ const style = {};
29
+ if (itemWidth > INITIAL_DIMENSION) {
30
+ style.width = itemWidth;
31
+ }
32
+ if (itemHeight > INITIAL_DIMENSION) {
33
+ style.height = itemHeight;
34
+ }
35
+ return style;
36
+ };
37
+
38
+ /**
39
+ * The `ScrollItem` component wraps individual items within a `Scroll` container. Each item
40
+ * is individually focusable and reports its natural dimensions so the parent can apply uniform
41
+ * sizing across all items.
42
+ *
43
+ * ## Props
44
+ *
45
+ * - Use `children` to provide the content of the scroll item
46
+ * - Use `tag` to set the HTML tag of the outer container (defaults to `'li'`)
47
+ *
48
+ */
49
+ const ScrollItem = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
50
+ let {
51
+ children,
52
+ tag = DEFAULT_TAG,
53
+ ...rest
54
+ } = _ref2;
55
+ const {
56
+ itemWidth,
57
+ itemHeight,
58
+ onItemLayout
59
+ } = (0, _ScrollContext.useScroll)();
60
+ const handleLayout = _react.default.useCallback(_ref3 => {
61
+ let {
62
+ nativeEvent: {
63
+ layout: {
64
+ width,
65
+ height
66
+ }
67
+ }
68
+ } = _ref3;
69
+ onItemLayout?.(width, height);
70
+ }, [onItemLayout]);
71
+ const selectedProps = selectProps({
72
+ ...rest,
73
+ ...(0, _semantics.getA11yPropsFromHtmlTag)(tag, rest.accessibilityRole)
74
+ });
75
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
76
+ ref: ref,
77
+ style: selectContainerStyle({
78
+ itemWidth,
79
+ itemHeight
80
+ }),
81
+ onLayout: handleLayout,
82
+ dataSet: {
83
+ [DATA_SCROLL_ITEM]: true
84
+ },
85
+ ...(_Platform.default.OS === 'web' ? {
86
+ tabIndex: TAB_INDEX
87
+ } : {
88
+ focusable: true
89
+ }),
90
+ ...selectedProps,
91
+ children: children
92
+ });
93
+ });
94
+ ScrollItem.displayName = 'ScrollItem';
95
+ ScrollItem.propTypes = {
96
+ ...selectedSystemPropTypes,
97
+ /**
98
+ * Content of the scroll item
99
+ */
100
+ children: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.node), _propTypes.default.node]).isRequired,
101
+ /**
102
+ * Sets the HTML tag of the outer container. Defaults to `'li'`.
103
+ */
104
+ tag: _propTypes.default.oneOf(_semantics.layoutTags)
105
+ };
106
+ const MemoizedScrollItem = exports.ScrollItem = /*#__PURE__*/_react.default.memo(ScrollItem);