@telus-uds/components-base 3.29.1 → 3.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -1
- package/lib/cjs/Autocomplete/Autocomplete.js +13 -1
- package/lib/cjs/Autocomplete/constants.js +1 -1
- package/lib/cjs/Carousel/Carousel.js +6 -1
- package/lib/cjs/Carousel/CarouselThumbnail.js +123 -31
- package/lib/cjs/Carousel/CarouselThumbnailNavigation.js +8 -1
- package/lib/cjs/Footnote/FootnoteLink.js +18 -17
- package/lib/cjs/Listbox/ListboxOverlay.js +7 -1
- package/lib/cjs/MultiSelectFilter/ModalOverlay.js +7 -1
- package/lib/cjs/MultiSelectFilter/MultiSelectFilter.js +2 -28
- package/lib/cjs/Progress/Progress.js +30 -5
- package/lib/cjs/Scroll/Scroll.js +466 -0
- package/lib/cjs/Scroll/ScrollContext.js +55 -0
- package/lib/cjs/Scroll/ScrollItem.js +103 -0
- package/lib/cjs/Scroll/ScrollProgress.js +99 -0
- package/lib/cjs/Scroll/dictionary.js +18 -0
- package/lib/cjs/Scroll/index.js +29 -0
- package/lib/cjs/TextInput/TextInputBase.js +2 -1
- package/lib/cjs/index.js +15 -0
- package/lib/cjs/utils/animation/index.js +7 -0
- package/lib/cjs/utils/animation/useAnimatedValue.js +46 -0
- package/lib/cjs/utils/children.js +2 -1
- package/lib/cjs/utils/useOverlaidPosition.js +83 -42
- package/lib/esm/Autocomplete/Autocomplete.js +13 -1
- package/lib/esm/Autocomplete/constants.js +1 -1
- package/lib/esm/Carousel/Carousel.js +6 -1
- package/lib/esm/Carousel/CarouselThumbnail.js +124 -32
- package/lib/esm/Carousel/CarouselThumbnailNavigation.js +8 -1
- package/lib/esm/Footnote/FootnoteLink.js +18 -17
- package/lib/esm/Listbox/ListboxOverlay.js +7 -1
- package/lib/esm/MultiSelectFilter/ModalOverlay.js +8 -2
- package/lib/esm/MultiSelectFilter/MultiSelectFilter.js +2 -28
- package/lib/esm/Progress/Progress.js +30 -5
- package/lib/esm/Scroll/Scroll.js +459 -0
- package/lib/esm/Scroll/ScrollContext.js +47 -0
- package/lib/esm/Scroll/ScrollItem.js +96 -0
- package/lib/esm/Scroll/ScrollProgress.js +92 -0
- package/lib/esm/Scroll/dictionary.js +12 -0
- package/lib/esm/Scroll/index.js +4 -0
- package/lib/esm/TextInput/TextInputBase.js +2 -1
- package/lib/esm/index.js +1 -0
- package/lib/esm/utils/animation/index.js +1 -1
- package/lib/esm/utils/animation/useAnimatedValue.js +39 -0
- package/lib/esm/utils/children.js +2 -1
- package/lib/esm/utils/useOverlaidPosition.js +83 -42
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Autocomplete/Autocomplete.jsx +11 -2
- package/src/Autocomplete/constants.js +1 -1
- package/src/Carousel/Carousel.jsx +6 -1
- package/src/Carousel/CarouselThumbnail.jsx +153 -64
- package/src/Carousel/CarouselThumbnailNavigation.jsx +8 -2
- package/src/Footnote/FootnoteLink.jsx +17 -12
- package/src/Listbox/ListboxOverlay.jsx +6 -2
- package/src/MultiSelectFilter/ModalOverlay.jsx +9 -3
- package/src/MultiSelectFilter/MultiSelectFilter.jsx +1 -31
- package/src/Progress/Progress.jsx +22 -3
- package/src/Scroll/Scroll.jsx +488 -0
- package/src/Scroll/ScrollContext.jsx +41 -0
- package/src/Scroll/ScrollItem.jsx +89 -0
- package/src/Scroll/ScrollProgress.jsx +75 -0
- package/src/Scroll/dictionary.js +12 -0
- package/src/Scroll/index.js +5 -0
- package/src/TextInput/TextInputBase.jsx +2 -1
- package/src/index.js +1 -0
- package/src/utils/animation/index.js +1 -1
- package/src/utils/animation/useAnimatedValue.js +37 -0
- package/src/utils/children.jsx +4 -1
- package/src/utils/useOverlaidPosition.js +84 -34
- package/types/Scroll.d.ts +66 -0
- package/types/index.d.ts +9 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireDefault(require("react"));
|
|
8
|
+
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
9
|
+
var _View = _interopRequireDefault(require("react-native-web/dist/cjs/exports/View"));
|
|
10
|
+
var _utils = require("../utils");
|
|
11
|
+
var _Progress = _interopRequireDefault(require("../Progress"));
|
|
12
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
const MAX_PERCENTAGE = 100;
|
|
15
|
+
const BORDER_RADIUS_DIVISOR = 2;
|
|
16
|
+
const staticProgressTokens = {
|
|
17
|
+
borderWidth: 0,
|
|
18
|
+
borderColor: 'transparent'
|
|
19
|
+
};
|
|
20
|
+
const staticProgressBarTokens = {
|
|
21
|
+
gradient: null,
|
|
22
|
+
outlineWidth: 0
|
|
23
|
+
};
|
|
24
|
+
const selectProgressTokens = _ref => {
|
|
25
|
+
let {
|
|
26
|
+
progressTrackHeight,
|
|
27
|
+
progressBarHeight,
|
|
28
|
+
progressTrackColor
|
|
29
|
+
} = _ref;
|
|
30
|
+
return {
|
|
31
|
+
height: progressTrackHeight,
|
|
32
|
+
barHeight: progressBarHeight,
|
|
33
|
+
backgroundColor: progressTrackColor,
|
|
34
|
+
borderRadius: progressTrackHeight / BORDER_RADIUS_DIVISOR
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
const selectBarTokens = _ref2 => {
|
|
38
|
+
let {
|
|
39
|
+
progressBarColor,
|
|
40
|
+
progressBarHeight
|
|
41
|
+
} = _ref2;
|
|
42
|
+
return {
|
|
43
|
+
backgroundColor: progressBarColor,
|
|
44
|
+
borderRadius: progressBarHeight / BORDER_RADIUS_DIVISOR
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
const ScrollProgress = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
|
|
48
|
+
let {
|
|
49
|
+
pressableState,
|
|
50
|
+
getTokens,
|
|
51
|
+
thumbWrapperStyle,
|
|
52
|
+
getCopy
|
|
53
|
+
} = _ref3;
|
|
54
|
+
const state = (0, _utils.resolvePressableState)(pressableState);
|
|
55
|
+
const themeTokens = getTokens(state);
|
|
56
|
+
const animatedBarHeight = (0, _utils.useAnimatedValue)(themeTokens.progressBarHeight);
|
|
57
|
+
const animatedTokens = {
|
|
58
|
+
...themeTokens,
|
|
59
|
+
progressBarHeight: animatedBarHeight
|
|
60
|
+
};
|
|
61
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Progress.default, {
|
|
62
|
+
tokens: {
|
|
63
|
+
...staticProgressTokens,
|
|
64
|
+
...selectProgressTokens(animatedTokens)
|
|
65
|
+
},
|
|
66
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
|
|
67
|
+
ref: ref,
|
|
68
|
+
style: thumbWrapperStyle,
|
|
69
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Progress.default.Bar, {
|
|
70
|
+
percentage: MAX_PERCENTAGE,
|
|
71
|
+
tokens: {
|
|
72
|
+
...staticProgressBarTokens,
|
|
73
|
+
...selectBarTokens(animatedTokens)
|
|
74
|
+
},
|
|
75
|
+
accessibilityLabel: getCopy('progressBarLabel')
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
ScrollProgress.displayName = 'ScrollProgress';
|
|
81
|
+
ScrollProgress.propTypes = {
|
|
82
|
+
/**
|
|
83
|
+
* State object from Pressable's render callback containing hovered, pressed, and focused states
|
|
84
|
+
*/
|
|
85
|
+
pressableState: _propTypes.default.object.isRequired,
|
|
86
|
+
/**
|
|
87
|
+
* Theme token resolver callback from useThemeTokensCallback that accepts appearance state
|
|
88
|
+
*/
|
|
89
|
+
getTokens: _propTypes.default.func.isRequired,
|
|
90
|
+
/**
|
|
91
|
+
* Style array for the thumb wrapper containing static and dynamic position styles
|
|
92
|
+
*/
|
|
93
|
+
thumbWrapperStyle: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.array]).isRequired,
|
|
94
|
+
/**
|
|
95
|
+
* Copy resolver function from useCopy for retrieving localized strings
|
|
96
|
+
*/
|
|
97
|
+
getCopy: _propTypes.default.func.isRequired
|
|
98
|
+
};
|
|
99
|
+
var _default = exports.default = ScrollProgress;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _default = exports.default = {
|
|
8
|
+
en: {
|
|
9
|
+
accessibilityLabel: 'Scrollable content',
|
|
10
|
+
instructionText: 'Use Tab, arrow keys, or space bar to navigate',
|
|
11
|
+
progressBarLabel: 'Scroll position'
|
|
12
|
+
},
|
|
13
|
+
fr: {
|
|
14
|
+
accessibilityLabel: 'Contenu défilable',
|
|
15
|
+
instructionText: "Utilisez Tab, les touches fléchées ou la barre d'espace pour naviguer",
|
|
16
|
+
progressBarLabel: 'Position de défilement'
|
|
17
|
+
}
|
|
18
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "ScrollItem", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _ScrollItem.default;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "ScrollProvider", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _ScrollContext.ScrollProvider;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
exports.default = void 0;
|
|
19
|
+
Object.defineProperty(exports, "useScroll", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () {
|
|
22
|
+
return _ScrollContext.useScroll;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
var _Scroll = _interopRequireDefault(require("./Scroll"));
|
|
26
|
+
var _ScrollItem = _interopRequireDefault(require("./ScrollItem"));
|
|
27
|
+
var _ScrollContext = require("./ScrollContext");
|
|
28
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
29
|
+
var _default = exports.default = _Scroll.default;
|
|
@@ -279,7 +279,8 @@ const TextInputBase = /*#__PURE__*/_react.default.forwardRef((_ref8, ref) => {
|
|
|
279
279
|
}, [element, pattern]);
|
|
280
280
|
const handleChangeText = event => {
|
|
281
281
|
const text = event.nativeEvent?.text ?? event.target?.value;
|
|
282
|
-
|
|
282
|
+
// Do NOT use `|| undefined`: empty string is falsy and would break the controlled contract
|
|
283
|
+
let filteredText = isNumeric ? text?.replace(/[^\d]/g, '') ?? '' : text;
|
|
283
284
|
if (type === 'card' && filteredText) {
|
|
284
285
|
const formattedValue = filteredText.replace(/[^a-zA-Z0-9]/g, '');
|
|
285
286
|
const regex = new RegExp(`([a-zA-Z0-9]{4})(?=[a-zA-Z0-9])`, 'g');
|
package/lib/cjs/index.js
CHANGED
|
@@ -57,6 +57,8 @@ var _exportNames = {
|
|
|
57
57
|
RadioCard: true,
|
|
58
58
|
RadioCardGroup: true,
|
|
59
59
|
Responsive: true,
|
|
60
|
+
Scroll: true,
|
|
61
|
+
ScrollItem: true,
|
|
60
62
|
Search: true,
|
|
61
63
|
Select: true,
|
|
62
64
|
Shortcuts: true,
|
|
@@ -422,6 +424,18 @@ Object.defineProperty(exports, "Responsive", {
|
|
|
422
424
|
return _Responsive.default;
|
|
423
425
|
}
|
|
424
426
|
});
|
|
427
|
+
Object.defineProperty(exports, "Scroll", {
|
|
428
|
+
enumerable: true,
|
|
429
|
+
get: function () {
|
|
430
|
+
return _Scroll.default;
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
Object.defineProperty(exports, "ScrollItem", {
|
|
434
|
+
enumerable: true,
|
|
435
|
+
get: function () {
|
|
436
|
+
return _Scroll.ScrollItem;
|
|
437
|
+
}
|
|
438
|
+
});
|
|
425
439
|
Object.defineProperty(exports, "Search", {
|
|
426
440
|
enumerable: true,
|
|
427
441
|
get: function () {
|
|
@@ -756,6 +770,7 @@ var _QuickLinksFeature = _interopRequireDefault(require("./QuickLinksFeature"));
|
|
|
756
770
|
var _Radio = _interopRequireWildcard(require("./Radio"));
|
|
757
771
|
var _RadioCard = _interopRequireWildcard(require("./RadioCard"));
|
|
758
772
|
var _Responsive = _interopRequireDefault(require("./Responsive"));
|
|
773
|
+
var _Scroll = _interopRequireWildcard(require("./Scroll"));
|
|
759
774
|
var _Search = _interopRequireDefault(require("./Search"));
|
|
760
775
|
var _Select = _interopRequireDefault(require("./Select"));
|
|
761
776
|
var _Shortcuts = _interopRequireWildcard(require("./Shortcuts"));
|
|
@@ -3,11 +3,18 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
Object.defineProperty(exports, "useAnimatedValue", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _useAnimatedValue.default;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
6
12
|
Object.defineProperty(exports, "useVerticalExpandAnimation", {
|
|
7
13
|
enumerable: true,
|
|
8
14
|
get: function () {
|
|
9
15
|
return _useVerticalExpandAnimation.default;
|
|
10
16
|
}
|
|
11
17
|
});
|
|
18
|
+
var _useAnimatedValue = _interopRequireDefault(require("./useAnimatedValue"));
|
|
12
19
|
var _useVerticalExpandAnimation = _interopRequireDefault(require("./useVerticalExpandAnimation"));
|
|
13
20
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireDefault(require("react"));
|
|
8
|
+
var _Animated = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Animated"));
|
|
9
|
+
var _Easing = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Easing"));
|
|
10
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
const ANIMATION_DURATION = 200;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Smoothly animates a numeric value to a new target using a quadratic ease-in-out
|
|
15
|
+
* curve. Returns the current interpolated value as a plain number so
|
|
16
|
+
* consumers can use it in non-Animated style props.
|
|
17
|
+
*
|
|
18
|
+
* @param {number} targetValue - The value to animate towards.
|
|
19
|
+
* @param {number} [duration=ANIMATION_DURATION] - Animation duration in ms.
|
|
20
|
+
*/
|
|
21
|
+
const useAnimatedValue = function (targetValue) {
|
|
22
|
+
let duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ANIMATION_DURATION;
|
|
23
|
+
const [currentValue, setCurrentValue] = _react.default.useState(targetValue);
|
|
24
|
+
const animatedValue = _react.default.useRef(new _Animated.default.Value(targetValue)).current;
|
|
25
|
+
_react.default.useEffect(() => {
|
|
26
|
+
const id = animatedValue.addListener(_ref => {
|
|
27
|
+
let {
|
|
28
|
+
value
|
|
29
|
+
} = _ref;
|
|
30
|
+
return setCurrentValue(value);
|
|
31
|
+
});
|
|
32
|
+
return () => animatedValue.removeListener(id);
|
|
33
|
+
}, [animatedValue]);
|
|
34
|
+
_react.default.useEffect(() => {
|
|
35
|
+
const animation = _Animated.default.timing(animatedValue, {
|
|
36
|
+
toValue: targetValue,
|
|
37
|
+
duration,
|
|
38
|
+
easing: _Easing.default.inOut(_Easing.default.quad),
|
|
39
|
+
useNativeDriver: false
|
|
40
|
+
});
|
|
41
|
+
animation.start();
|
|
42
|
+
return () => animation.stop();
|
|
43
|
+
}, [targetValue, animatedValue, duration]);
|
|
44
|
+
return currentValue;
|
|
45
|
+
};
|
|
46
|
+
var _default = exports.default = useAnimatedValue;
|
|
@@ -54,7 +54,8 @@ const unpackFragment = child => {
|
|
|
54
54
|
exports.unpackFragment = unpackFragment;
|
|
55
55
|
const isStringOrNumber = child => typeof child === 'string' || typeof child === 'number';
|
|
56
56
|
// Wrap an A11yText with neighouring text strings so it doesn't split them into multiple <Text>s
|
|
57
|
-
|
|
57
|
+
// Check displayName first as it is explicitly set and reliable regardless of export style (named vs default)
|
|
58
|
+
const isWrapable = child => isStringOrNumber(child) || child.type === _A11yText.default || child.type?.__UDS_COMPONENT_NAME__ === 'FootnoteLink';
|
|
58
59
|
const combineKeys = childrenArray => childrenArray.reduce((newKey, child) => `${newKey}${child.key || ''}`, '');
|
|
59
60
|
|
|
60
61
|
// Group wrappable children for one `<Text>` parent, merging adjacent text nodes
|
|
@@ -61,46 +61,45 @@ function getOverlaidPosition(_ref2) {
|
|
|
61
61
|
offsets = {},
|
|
62
62
|
align
|
|
63
63
|
} = _ref2;
|
|
64
|
-
// Web-only: this will be difficult to mimic on native because there's no global scroll position.
|
|
65
|
-
// TODO: wire something in e.g. a scroll ref accessible from a provider included in Allium provider
|
|
66
|
-
// that can be passed to the appropriate ScrollView?
|
|
67
|
-
const {
|
|
68
|
-
scrollX = 0,
|
|
69
|
-
scrollY = 0
|
|
70
|
-
} = typeof window === 'object' ? window : {};
|
|
71
|
-
|
|
72
64
|
// Will have top, bottom, left and/or right offsets depending on `align`
|
|
73
65
|
const positioning = {};
|
|
74
66
|
const verticalOffset = offsets.vertical ?? 0;
|
|
75
67
|
const horizontalOffset = offsets.horizontal ?? 0;
|
|
76
68
|
if (align.top) positioning.top = getPosition({
|
|
77
69
|
edge: getEdgeType(align, 'top'),
|
|
78
|
-
fromEdge: sourceLayout.y +
|
|
70
|
+
fromEdge: sourceLayout.y + verticalOffset,
|
|
79
71
|
sourceSize: sourceLayout.height
|
|
80
72
|
});
|
|
81
73
|
if (align.middle) positioning.top = getPosition({
|
|
82
74
|
edge: getEdgeType(align, 'middle'),
|
|
83
|
-
fromEdge: sourceLayout.y +
|
|
84
|
-
sourceSize: sourceLayout.height
|
|
85
|
-
});
|
|
86
|
-
if (align.bottom) positioning.bottom = getPosition({
|
|
87
|
-
edge: getEdgeType(align, 'bottom'),
|
|
88
|
-
fromEdge: windowDimensions.height - (sourceLayout.y + scrollY + sourceLayout.height - verticalOffset),
|
|
75
|
+
fromEdge: sourceLayout.y + verticalOffset - targetDimensions.height / 2,
|
|
89
76
|
sourceSize: sourceLayout.height
|
|
90
77
|
});
|
|
78
|
+
if (align.bottom) {
|
|
79
|
+
if (_Platform.default.OS !== 'web') {
|
|
80
|
+
// On native, position:absolute is parent-relative, so use negative top offset instead of window-based bottom math.
|
|
81
|
+
positioning.top = sourceLayout.y - targetDimensions.height - verticalOffset;
|
|
82
|
+
} else {
|
|
83
|
+
positioning.bottom = getPosition({
|
|
84
|
+
edge: getEdgeType(align, 'bottom'),
|
|
85
|
+
fromEdge: windowDimensions.height - (sourceLayout.y + sourceLayout.height - verticalOffset),
|
|
86
|
+
sourceSize: sourceLayout.height
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
91
90
|
if (align.left) positioning.left = getPosition({
|
|
92
91
|
edge: getEdgeType(align, 'left'),
|
|
93
|
-
fromEdge: sourceLayout.x +
|
|
92
|
+
fromEdge: sourceLayout.x + horizontalOffset,
|
|
94
93
|
sourceSize: sourceLayout.width
|
|
95
94
|
});
|
|
96
95
|
if (align.center) positioning.left = getPosition({
|
|
97
96
|
edge: getEdgeType(align, 'center'),
|
|
98
|
-
fromEdge: sourceLayout.x +
|
|
97
|
+
fromEdge: sourceLayout.x + horizontalOffset - targetDimensions.width / 2,
|
|
99
98
|
sourceSize: sourceLayout.width
|
|
100
99
|
});
|
|
101
100
|
if (align.right) positioning.right = getPosition({
|
|
102
101
|
edge: getEdgeType(align, 'right'),
|
|
103
|
-
fromEdge: windowDimensions.width - (sourceLayout.x +
|
|
102
|
+
fromEdge: windowDimensions.width - (sourceLayout.x + sourceLayout.width - horizontalOffset),
|
|
104
103
|
sourceSize: sourceLayout.width
|
|
105
104
|
});
|
|
106
105
|
if (!(align.left && align.right)) {
|
|
@@ -141,6 +140,32 @@ const useOverlaidPosition = _ref3 => {
|
|
|
141
140
|
const targetRef = (0, _react.useRef)(null);
|
|
142
141
|
const [targetDimensions, setTargetDimensions] = (0, _react.useState)(null);
|
|
143
142
|
const [windowDimensions, setWindowDimensions] = (0, _react.useState)(null);
|
|
143
|
+
const hasRemeasuredRef = (0, _react.useRef)(false);
|
|
144
|
+
const applySourceLayout = (0, _react.useCallback)((dims, x, y, width, height) => {
|
|
145
|
+
setWindowDimensions(dims);
|
|
146
|
+
setSourceLayout({
|
|
147
|
+
x,
|
|
148
|
+
y,
|
|
149
|
+
width,
|
|
150
|
+
height
|
|
151
|
+
});
|
|
152
|
+
}, []);
|
|
153
|
+
const measureSourceOnWeb = (0, _react.useCallback)(windowDims => {
|
|
154
|
+
const el = sourceRef.current;
|
|
155
|
+
if (!el) return;
|
|
156
|
+
const domNode = el._nativeTag ?? el;
|
|
157
|
+
const dims = windowDims ?? _Dimensions.default.get('window');
|
|
158
|
+
const rect = typeof domNode.getBoundingClientRect === 'function' ? domNode.getBoundingClientRect() : null;
|
|
159
|
+
if (rect) {
|
|
160
|
+
const x = rect.left + (typeof window.scrollX === 'number' ? window.scrollX : 0);
|
|
161
|
+
const y = rect.top + (typeof window.scrollY === 'number' ? window.scrollY : 0);
|
|
162
|
+
applySourceLayout(dims, x, y, rect.width, rect.height);
|
|
163
|
+
} else {
|
|
164
|
+
el.measureInWindow((mx, my, width, height) => {
|
|
165
|
+
applySourceLayout(dims, mx, my, width, height);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}, [applySourceLayout]);
|
|
144
169
|
const onTargetLayout = (0, _react.useCallback)(_ref4 => {
|
|
145
170
|
let {
|
|
146
171
|
nativeEvent: {
|
|
@@ -171,16 +196,19 @@ const useOverlaidPosition = _ref3 => {
|
|
|
171
196
|
let {
|
|
172
197
|
window
|
|
173
198
|
} = _ref5;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
199
|
+
if (_Platform.default.OS === 'web') {
|
|
200
|
+
measureSourceOnWeb(window);
|
|
201
|
+
} else {
|
|
202
|
+
sourceRef.current?.measure((x, y, width, height) => {
|
|
203
|
+
setWindowDimensions(window);
|
|
204
|
+
setSourceLayout({
|
|
205
|
+
x,
|
|
206
|
+
y,
|
|
207
|
+
width,
|
|
208
|
+
height
|
|
209
|
+
});
|
|
182
210
|
});
|
|
183
|
-
}
|
|
211
|
+
}
|
|
184
212
|
};
|
|
185
213
|
let subscription;
|
|
186
214
|
const unsubscribe = () => {
|
|
@@ -193,6 +221,7 @@ const useOverlaidPosition = _ref3 => {
|
|
|
193
221
|
}
|
|
194
222
|
setSourceLayout(null);
|
|
195
223
|
setTargetDimensions(null);
|
|
224
|
+
hasRemeasuredRef.current = false;
|
|
196
225
|
};
|
|
197
226
|
if (readyToShow) {
|
|
198
227
|
subscription = _Dimensions.default.addEventListener('change', handleDimensionsChange);
|
|
@@ -203,36 +232,48 @@ const useOverlaidPosition = _ref3 => {
|
|
|
203
232
|
unsubscribe();
|
|
204
233
|
}
|
|
205
234
|
return unsubscribe;
|
|
206
|
-
}, [readyToShow]);
|
|
235
|
+
}, [readyToShow, measureSourceOnWeb]);
|
|
236
|
+
|
|
237
|
+
// Re-measure source when targetDimensions first becomes available.
|
|
238
|
+
// Without this, there is a race condition: getBoundingClientRect() resolves faster than
|
|
239
|
+
// measureInWindow used to, so sourceLayout may be set before the dropdown has rendered
|
|
240
|
+
// and reported its dimensions via onTargetLayout. When targetDimensions finally arrives,
|
|
241
|
+
// sourceLayout is stale (no scroll has occurred to trigger handleScroll).
|
|
242
|
+
// Setting hasRemeasuredRef.current=true here also prevents the blink: isReady (on web)
|
|
243
|
+
// won't become true until this effect completes, guaranteeing the dropdown is always shown
|
|
244
|
+
// at its final correct position without an intermediate incorrect-position frame.
|
|
245
|
+
(0, _react.useEffect)(() => {
|
|
246
|
+
if (!isShown || !sourceRef.current || !targetDimensions || _Platform.default.OS !== 'web') return;
|
|
247
|
+
measureSourceOnWeb();
|
|
248
|
+
hasRemeasuredRef.current = true;
|
|
249
|
+
}, [targetDimensions, isShown, measureSourceOnWeb]);
|
|
207
250
|
(0, _react.useEffect)(() => {
|
|
208
251
|
if (_Platform.default.OS !== 'web') {
|
|
209
252
|
return undefined;
|
|
210
253
|
}
|
|
211
|
-
const handleScroll = (0, _lodash.default)(
|
|
212
|
-
sourceRef.current?.measureInWindow((x, y, width, height) => {
|
|
213
|
-
setWindowDimensions(window);
|
|
214
|
-
setSourceLayout({
|
|
215
|
-
x,
|
|
216
|
-
y,
|
|
217
|
-
width,
|
|
218
|
-
height
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
}, DEBOUNCE_DELAY);
|
|
254
|
+
const handleScroll = (0, _lodash.default)(measureSourceOnWeb, DEBOUNCE_DELAY);
|
|
222
255
|
window.addEventListener('scroll', handleScroll);
|
|
223
256
|
return () => {
|
|
224
257
|
window.removeEventListener('scroll', handleScroll);
|
|
225
258
|
handleScroll.cancel();
|
|
226
259
|
};
|
|
227
|
-
}, [
|
|
228
|
-
|
|
260
|
+
}, [measureSourceOnWeb]);
|
|
261
|
+
|
|
262
|
+
// On web, require hasRemeasuredRef to be true before declaring isReady. This ensures
|
|
263
|
+
// the dropdown is never shown with an intermediate stale position (the blink).
|
|
264
|
+
// On native, hasRemeasuredRef stays false (the web-only effect never runs), so we skip
|
|
265
|
+
// that check to preserve the original native behaviour.
|
|
266
|
+
const isReady = Boolean(isShown && sourceLayout && windowDimensions && targetDimensions && (_Platform.default.OS !== 'web' || hasRemeasuredRef.current));
|
|
229
267
|
const overlaidPosition = isReady ? getOverlaidPosition({
|
|
230
268
|
sourceLayout,
|
|
231
269
|
targetDimensions,
|
|
232
270
|
windowDimensions,
|
|
233
271
|
offsets,
|
|
234
272
|
align
|
|
235
|
-
}) : {
|
|
273
|
+
}) : {
|
|
274
|
+
top: 0,
|
|
275
|
+
left: 0
|
|
276
|
+
};
|
|
236
277
|
return {
|
|
237
278
|
overlaidPosition,
|
|
238
279
|
sourceRef,
|
|
@@ -119,6 +119,7 @@ const Autocomplete = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
119
119
|
value,
|
|
120
120
|
helpText = '',
|
|
121
121
|
loadingLabel,
|
|
122
|
+
dropdownPosition = 'bottom',
|
|
122
123
|
tokens,
|
|
123
124
|
...rest
|
|
124
125
|
} = _ref2;
|
|
@@ -174,6 +175,13 @@ const Autocomplete = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
174
175
|
isShown: isExpanded || hintExpansionEnabled,
|
|
175
176
|
offsets: {
|
|
176
177
|
vertical: Platform.OS !== 'web' && (hint || inputLabel) ? 28 : 4
|
|
178
|
+
},
|
|
179
|
+
align: dropdownPosition === 'top' ? {
|
|
180
|
+
center: 'center',
|
|
181
|
+
bottom: 'top'
|
|
182
|
+
} : {
|
|
183
|
+
center: 'center',
|
|
184
|
+
top: 'bottom'
|
|
177
185
|
}
|
|
178
186
|
});
|
|
179
187
|
const targetRef = React.useRef(null);
|
|
@@ -526,6 +534,10 @@ Autocomplete.propTypes = {
|
|
|
526
534
|
/**
|
|
527
535
|
* Input value for controlled usage
|
|
528
536
|
*/
|
|
529
|
-
value: PropTypes.string
|
|
537
|
+
value: PropTypes.string,
|
|
538
|
+
/**
|
|
539
|
+
* Controls whether the dropdown renders above or below the input. Defaults to 'bottom'.
|
|
540
|
+
*/
|
|
541
|
+
dropdownPosition: PropTypes.oneOf(['top', 'bottom'])
|
|
530
542
|
};
|
|
531
543
|
export default Autocomplete;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const DEFAULT_MIN_TO_SUGGESTION = 1;
|
|
2
2
|
export const DEFAULT_MAX_SUGGESTIONS = 5;
|
|
3
|
-
export const DEFAULT_MAX_DROPDOWN_HEIGHT =
|
|
3
|
+
export const DEFAULT_MAX_DROPDOWN_HEIGHT = 500;
|
|
4
4
|
export const INPUT_LEFT_PADDING = 16;
|
|
5
5
|
export const MIN_LISTBOX_WIDTH = 288;
|
|
@@ -1170,9 +1170,14 @@ Carousel.propTypes = {
|
|
|
1170
1170
|
* An array of objects containing information on the thumbnails to be rendered as navigation panel
|
|
1171
1171
|
*/
|
|
1172
1172
|
thumbnails: PropTypes.arrayOf(PropTypes.shape({
|
|
1173
|
+
/** Accessibility label for the thumbnail image, used by assistive technologies. */
|
|
1173
1174
|
accessibilityLabel: PropTypes.string,
|
|
1175
|
+
/** Alternative text for the thumbnail image, displayed when the image cannot be rendered. */
|
|
1174
1176
|
alt: PropTypes.string,
|
|
1175
|
-
|
|
1177
|
+
/** When true, renders a play icon overlay on the thumbnail to indicate that the slide contains a video. */
|
|
1178
|
+
isVideo: PropTypes.bool,
|
|
1179
|
+
/** URL or path of the thumbnail image. When used with `isVideo`, this should be the video's poster/preview image. */
|
|
1180
|
+
src: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
|
1176
1181
|
})),
|
|
1177
1182
|
/**
|
|
1178
1183
|
* Minimal part of slide width must be swiped for changing index.
|