@telus-uds/components-base 4.0.0-alpha.1 → 4.0.0-beta.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 +43 -1
- package/lib/cjs/Autocomplete/Autocomplete.js +13 -1
- package/lib/cjs/Autocomplete/constants.js +1 -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 +472 -0
- package/lib/cjs/Scroll/ScrollContext.js +55 -0
- package/lib/cjs/Scroll/ScrollItem.js +106 -0
- package/lib/cjs/Scroll/ScrollProgress.js +100 -0
- package/lib/cjs/Scroll/dictionary.js +18 -0
- package/lib/cjs/TextInput/TextInputBase.js +2 -1
- package/lib/cjs/index.js +14 -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/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 +465 -0
- package/lib/esm/Scroll/ScrollContext.js +46 -0
- package/lib/esm/Scroll/ScrollItem.js +100 -0
- package/lib/esm/Scroll/ScrollProgress.js +93 -0
- package/lib/esm/Scroll/dictionary.js +12 -0
- package/lib/esm/TextInput/TextInputBase.js +2 -1
- package/lib/esm/index.js +2 -0
- package/lib/esm/utils/animation/useAnimatedValue.js +38 -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/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 +483 -0
- package/src/Scroll/ScrollContext.jsx +39 -0
- package/src/Scroll/ScrollItem.jsx +87 -0
- package/src/Scroll/ScrollProgress.jsx +74 -0
- package/src/Scroll/dictionary.js +12 -0
- package/src/TextInput/TextInputBase.jsx +2 -1
- package/src/index.js +2 -0
- package/src/utils/animation/useAnimatedValue.js +35 -0
- package/src/utils/children.jsx +4 -1
- package/src/utils/useOverlaidPosition.js +84 -34
- package/types/A11yInfoProvider.d.ts +9 -0
- package/types/A11yText.d.ts +13 -0
- package/types/ActionCard.d.ts +19 -0
- package/types/ActivityIndicator.d.ts +13 -0
- package/types/Autocomplete.d.ts +27 -0
- package/types/Badge.d.ts +15 -4
- package/types/BaseProvider.d.ts +26 -0
- package/types/Box.d.ts +13 -3
- package/types/Button.d.ts +35 -0
- package/types/Card.d.ts +19 -0
- package/types/CardGroup.d.ts +23 -0
- package/types/Carousel.d.ts +25 -0
- package/types/Checkbox.d.ts +15 -2
- package/types/CheckboxCard.d.ts +20 -0
- package/types/CheckboxCardGroup.d.ts +10 -4
- package/types/ChevronLink.d.ts +9 -2
- package/types/ColourToggle.d.ts +16 -0
- package/types/Common.d.ts +7 -3
- package/types/Divider.d.ts +13 -1
- package/types/DownloadApp.d.ts +19 -0
- package/types/ExpandCollapse.d.ts +16 -9
- package/types/ExpandCollapseMini.d.ts +16 -0
- package/types/Feedback.d.ts +16 -0
- package/types/Fieldset.d.ts +17 -0
- package/types/FileUpload.d.ts +6 -0
- package/types/FlexGrid.d.ts +57 -0
- package/types/Footnote.d.ts +18 -0
- package/types/HorizontalScroll.d.ts +15 -0
- package/types/HorizontalScrollButton.d.ts +17 -1
- package/types/Icon.d.ts +13 -2
- package/types/IconButton.d.ts +19 -0
- package/types/InputLabel.d.ts +12 -0
- package/types/InputSupports.d.ts +19 -0
- package/types/Link.d.ts +20 -4
- package/types/List.d.ts +11 -4
- package/types/Listbox.d.ts +13 -3
- package/types/Modal.d.ts +25 -0
- package/types/MultiSelectFilter.d.ts +24 -0
- package/types/Notification.d.ts +19 -0
- package/types/OrderedList.d.ts +17 -0
- package/types/Pagination.d.ts +25 -0
- package/types/Portal.d.ts +5 -1
- package/types/PriceLockup.d.ts +28 -0
- package/types/ProductCard.d.ts +20 -0
- package/types/ProductCardGroup.d.ts +21 -0
- package/types/Progress.d.ts +22 -0
- package/types/QuickLinks.d.ts +27 -0
- package/types/QuickLinksFeature.d.ts +27 -0
- package/types/Radio.d.ts +22 -0
- package/types/RadioCard.d.ts +21 -0
- package/types/Responsive.d.ts +14 -0
- package/types/Scroll.d.ts +66 -0
- package/types/Search.d.ts +15 -2
- package/types/Select.d.ts +12 -4
- package/types/SideNav.d.ts +38 -0
- package/types/Skeleton.d.ts +17 -0
- package/types/SkipLink.d.ts +14 -0
- package/types/Spacer.d.ts +8 -1
- package/types/StackView.d.ts +8 -2
- package/types/Status.d.ts +2 -3
- package/types/StepTracker.d.ts +16 -0
- package/types/TabBar.d.ts +25 -0
- package/types/Tabs.d.ts +12 -3
- package/types/Tags.d.ts +24 -0
- package/types/TextButton.d.ts +11 -4
- package/types/TextInput.d.ts +38 -0
- package/types/ThemeProvider.d.ts +29 -0
- package/types/Timeline.d.ts +16 -0
- package/types/ToggleSwitch.d.ts +11 -2
- package/types/ToolTip.d.ts +7 -3
- package/types/TooltipButton.d.ts +20 -0
- package/types/Typography.d.ts +8 -2
- package/types/Validator.d.ts +16 -0
- package/types/ViewportProvider.d.ts +12 -0
- package/types/index.d.ts +205 -49
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import { View } from 'react-native'
|
|
4
|
+
import { resolvePressableState } from '../utils/pressability'
|
|
5
|
+
import { useAnimatedValue } from '../utils/animation/useAnimatedValue'
|
|
6
|
+
import { Progress } from '../Progress/Progress'
|
|
7
|
+
import { ProgressBar } from '../Progress/ProgressBar'
|
|
8
|
+
|
|
9
|
+
const MAX_PERCENTAGE = 100
|
|
10
|
+
const BORDER_RADIUS_DIVISOR = 2
|
|
11
|
+
|
|
12
|
+
const staticProgressTokens = {
|
|
13
|
+
borderWidth: 0,
|
|
14
|
+
borderColor: 'transparent'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const staticProgressBarTokens = {
|
|
18
|
+
gradient: null,
|
|
19
|
+
outlineWidth: 0
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const selectProgressTokens = ({ progressTrackHeight, progressBarHeight, progressTrackColor }) => ({
|
|
23
|
+
height: progressTrackHeight,
|
|
24
|
+
barHeight: progressBarHeight,
|
|
25
|
+
backgroundColor: progressTrackColor,
|
|
26
|
+
borderRadius: progressTrackHeight / BORDER_RADIUS_DIVISOR
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const selectBarTokens = ({ progressBarColor, progressBarHeight }) => ({
|
|
30
|
+
backgroundColor: progressBarColor,
|
|
31
|
+
borderRadius: progressBarHeight / BORDER_RADIUS_DIVISOR
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
export const ScrollProgress = React.forwardRef(
|
|
35
|
+
({ pressableState, getTokens, thumbWrapperStyle, getCopy }, ref) => {
|
|
36
|
+
const state = resolvePressableState(pressableState)
|
|
37
|
+
const themeTokens = getTokens(state)
|
|
38
|
+
const animatedBarHeight = useAnimatedValue(themeTokens.progressBarHeight)
|
|
39
|
+
const animatedTokens = { ...themeTokens, progressBarHeight: animatedBarHeight }
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Progress tokens={{ ...staticProgressTokens, ...selectProgressTokens(animatedTokens) }}>
|
|
43
|
+
<View ref={ref} style={thumbWrapperStyle}>
|
|
44
|
+
<ProgressBar
|
|
45
|
+
percentage={MAX_PERCENTAGE}
|
|
46
|
+
tokens={{ ...staticProgressBarTokens, ...selectBarTokens(animatedTokens) }}
|
|
47
|
+
accessibilityLabel={getCopy('progressBarLabel')}
|
|
48
|
+
/>
|
|
49
|
+
</View>
|
|
50
|
+
</Progress>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
ScrollProgress.displayName = 'ScrollProgress'
|
|
56
|
+
|
|
57
|
+
ScrollProgress.propTypes = {
|
|
58
|
+
/**
|
|
59
|
+
* State object from Pressable's render callback containing hovered, pressed, and focused states
|
|
60
|
+
*/
|
|
61
|
+
pressableState: PropTypes.object.isRequired,
|
|
62
|
+
/**
|
|
63
|
+
* Theme token resolver callback from useThemeTokensCallback that accepts appearance state
|
|
64
|
+
*/
|
|
65
|
+
getTokens: PropTypes.func.isRequired,
|
|
66
|
+
/**
|
|
67
|
+
* Style array for the thumb wrapper containing static and dynamic position styles
|
|
68
|
+
*/
|
|
69
|
+
thumbWrapperStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
|
|
70
|
+
/**
|
|
71
|
+
* Copy resolver function from useCopy for retrieving localized strings
|
|
72
|
+
*/
|
|
73
|
+
getCopy: PropTypes.func.isRequired
|
|
74
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const dictionary = {
|
|
2
|
+
en: {
|
|
3
|
+
accessibilityLabel: 'Scrollable content',
|
|
4
|
+
instructionText: 'Use Tab, arrow keys, or space bar to navigate',
|
|
5
|
+
progressBarLabel: 'Scroll position'
|
|
6
|
+
},
|
|
7
|
+
fr: {
|
|
8
|
+
accessibilityLabel: 'Contenu défilable',
|
|
9
|
+
instructionText: "Utilisez Tab, les touches fléchées ou la barre d'espace pour naviguer",
|
|
10
|
+
progressBarLabel: 'Position de défilement'
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -256,7 +256,8 @@ export const TextInputBase = React.forwardRef(
|
|
|
256
256
|
|
|
257
257
|
const handleChangeText = (event) => {
|
|
258
258
|
const text = event.nativeEvent?.text ?? event.target?.value
|
|
259
|
-
|
|
259
|
+
// Do NOT use `|| undefined`: empty string is falsy and would break the controlled contract
|
|
260
|
+
let filteredText = isNumeric ? text?.replace(/[^\d]/g, '') ?? '' : text
|
|
260
261
|
if (type === 'card' && filteredText) {
|
|
261
262
|
const formattedValue = filteredText.replace(/[^a-zA-Z0-9]/g, '')
|
|
262
263
|
const regex = new RegExp(`([a-zA-Z0-9]{4})(?=[a-zA-Z0-9])`, 'g')
|
package/src/index.js
CHANGED
|
@@ -76,6 +76,8 @@ export { RadioGroup } from './Radio/RadioGroup'
|
|
|
76
76
|
export { RadioCard } from './RadioCard/RadioCard'
|
|
77
77
|
export { RadioCardGroup } from './RadioCard/RadioCardGroup'
|
|
78
78
|
export { Responsive } from './Responsive/Responsive'
|
|
79
|
+
export { Scroll } from './Scroll/Scroll'
|
|
80
|
+
export { ScrollItem } from './Scroll/ScrollItem'
|
|
79
81
|
export { Search } from './Search/Search'
|
|
80
82
|
export { Select } from './Select/Select'
|
|
81
83
|
export { SelectItem } from './Select/SelectItem'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Animated, Easing } from 'react-native'
|
|
3
|
+
|
|
4
|
+
const ANIMATION_DURATION = 200
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Smoothly animates a numeric value to a new target using a quadratic ease-in-out
|
|
8
|
+
* curve. Returns the current interpolated value as a plain number so
|
|
9
|
+
* consumers can use it in non-Animated style props.
|
|
10
|
+
*
|
|
11
|
+
* @param {number} targetValue - The value to animate towards.
|
|
12
|
+
* @param {number} [duration=ANIMATION_DURATION] - Animation duration in ms.
|
|
13
|
+
*/
|
|
14
|
+
export const useAnimatedValue = (targetValue, duration = ANIMATION_DURATION) => {
|
|
15
|
+
const [currentValue, setCurrentValue] = React.useState(targetValue)
|
|
16
|
+
const animatedValue = React.useRef(new Animated.Value(targetValue)).current
|
|
17
|
+
|
|
18
|
+
React.useEffect(() => {
|
|
19
|
+
const id = animatedValue.addListener(({ value }) => setCurrentValue(value))
|
|
20
|
+
return () => animatedValue.removeListener(id)
|
|
21
|
+
}, [animatedValue])
|
|
22
|
+
|
|
23
|
+
React.useEffect(() => {
|
|
24
|
+
const animation = Animated.timing(animatedValue, {
|
|
25
|
+
toValue: targetValue,
|
|
26
|
+
duration,
|
|
27
|
+
easing: Easing.inOut(Easing.quad),
|
|
28
|
+
useNativeDriver: false
|
|
29
|
+
})
|
|
30
|
+
animation.start()
|
|
31
|
+
return () => animation.stop()
|
|
32
|
+
}, [targetValue, animatedValue, duration])
|
|
33
|
+
|
|
34
|
+
return currentValue
|
|
35
|
+
}
|
package/src/utils/children.jsx
CHANGED
|
@@ -47,8 +47,11 @@ export const unpackFragment = (child) => {
|
|
|
47
47
|
|
|
48
48
|
const isStringOrNumber = (child) => typeof child === 'string' || typeof child === 'number'
|
|
49
49
|
// Wrap an A11yText with neighouring text strings so it doesn't split them into multiple <Text>s
|
|
50
|
+
// Check displayName first as it is explicitly set and reliable regardless of export style (named vs default)
|
|
50
51
|
const isWrapable = (child) =>
|
|
51
|
-
isStringOrNumber(child) ||
|
|
52
|
+
isStringOrNumber(child) ||
|
|
53
|
+
child.type === A11yText ||
|
|
54
|
+
child.type?.__UDS_COMPONENT_NAME__ === 'FootnoteLink'
|
|
52
55
|
const combineKeys = (childrenArray) =>
|
|
53
56
|
childrenArray.reduce((newKey, child) => `${newKey}${child.key || ''}`, '')
|
|
54
57
|
|
|
@@ -54,11 +54,6 @@ function getOverlaidPosition({
|
|
|
54
54
|
offsets = {},
|
|
55
55
|
align
|
|
56
56
|
}) {
|
|
57
|
-
// Web-only: this will be difficult to mimic on native because there's no global scroll position.
|
|
58
|
-
// TODO: wire something in e.g. a scroll ref accessible from a provider included in Allium provider
|
|
59
|
-
// that can be passed to the appropriate ScrollView?
|
|
60
|
-
const { scrollX = 0, scrollY = 0 } = typeof window === 'object' ? window : {}
|
|
61
|
-
|
|
62
57
|
// Will have top, bottom, left and/or right offsets depending on `align`
|
|
63
58
|
const positioning = {}
|
|
64
59
|
|
|
@@ -68,40 +63,44 @@ function getOverlaidPosition({
|
|
|
68
63
|
if (align.top)
|
|
69
64
|
positioning.top = getPosition({
|
|
70
65
|
edge: getEdgeType(align, 'top'),
|
|
71
|
-
fromEdge: sourceLayout.y +
|
|
66
|
+
fromEdge: sourceLayout.y + verticalOffset,
|
|
72
67
|
sourceSize: sourceLayout.height
|
|
73
68
|
})
|
|
74
69
|
if (align.middle)
|
|
75
70
|
positioning.top = getPosition({
|
|
76
71
|
edge: getEdgeType(align, 'middle'),
|
|
77
|
-
fromEdge: sourceLayout.y +
|
|
78
|
-
sourceSize: sourceLayout.height
|
|
79
|
-
})
|
|
80
|
-
if (align.bottom)
|
|
81
|
-
positioning.bottom = getPosition({
|
|
82
|
-
edge: getEdgeType(align, 'bottom'),
|
|
83
|
-
fromEdge:
|
|
84
|
-
windowDimensions.height - (sourceLayout.y + scrollY + sourceLayout.height - verticalOffset),
|
|
72
|
+
fromEdge: sourceLayout.y + verticalOffset - targetDimensions.height / 2,
|
|
85
73
|
sourceSize: sourceLayout.height
|
|
86
74
|
})
|
|
75
|
+
if (align.bottom) {
|
|
76
|
+
if (Platform.OS !== 'web') {
|
|
77
|
+
// On native, position:absolute is parent-relative, so use negative top offset instead of window-based bottom math.
|
|
78
|
+
positioning.top = sourceLayout.y - targetDimensions.height - verticalOffset
|
|
79
|
+
} else {
|
|
80
|
+
positioning.bottom = getPosition({
|
|
81
|
+
edge: getEdgeType(align, 'bottom'),
|
|
82
|
+
fromEdge: windowDimensions.height - (sourceLayout.y + sourceLayout.height - verticalOffset),
|
|
83
|
+
sourceSize: sourceLayout.height
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
87
|
|
|
88
88
|
if (align.left)
|
|
89
89
|
positioning.left = getPosition({
|
|
90
90
|
edge: getEdgeType(align, 'left'),
|
|
91
|
-
fromEdge: sourceLayout.x +
|
|
91
|
+
fromEdge: sourceLayout.x + horizontalOffset,
|
|
92
92
|
sourceSize: sourceLayout.width
|
|
93
93
|
})
|
|
94
94
|
if (align.center)
|
|
95
95
|
positioning.left = getPosition({
|
|
96
96
|
edge: getEdgeType(align, 'center'),
|
|
97
|
-
fromEdge: sourceLayout.x +
|
|
97
|
+
fromEdge: sourceLayout.x + horizontalOffset - targetDimensions.width / 2,
|
|
98
98
|
sourceSize: sourceLayout.width
|
|
99
99
|
})
|
|
100
100
|
if (align.right)
|
|
101
101
|
positioning.right = getPosition({
|
|
102
102
|
edge: getEdgeType(align, 'right'),
|
|
103
|
-
fromEdge:
|
|
104
|
-
windowDimensions.width - (sourceLayout.x + scrollX + sourceLayout.width - horizontalOffset),
|
|
103
|
+
fromEdge: windowDimensions.width - (sourceLayout.x + sourceLayout.width - horizontalOffset),
|
|
105
104
|
sourceSize: sourceLayout.width
|
|
106
105
|
})
|
|
107
106
|
|
|
@@ -149,6 +148,34 @@ export const useOverlaidPosition = ({
|
|
|
149
148
|
|
|
150
149
|
const [windowDimensions, setWindowDimensions] = useState(null)
|
|
151
150
|
|
|
151
|
+
const hasRemeasuredRef = useRef(false)
|
|
152
|
+
|
|
153
|
+
const applySourceLayout = useCallback((dims, x, y, width, height) => {
|
|
154
|
+
setWindowDimensions(dims)
|
|
155
|
+
setSourceLayout({ x, y, width, height })
|
|
156
|
+
}, [])
|
|
157
|
+
|
|
158
|
+
const measureSourceOnWeb = useCallback(
|
|
159
|
+
(windowDims) => {
|
|
160
|
+
const el = sourceRef.current
|
|
161
|
+
if (!el) return
|
|
162
|
+
const domNode = el._nativeTag ?? el
|
|
163
|
+
const dims = windowDims ?? Dimensions.get('window')
|
|
164
|
+
const rect =
|
|
165
|
+
typeof domNode.getBoundingClientRect === 'function' ? domNode.getBoundingClientRect() : null
|
|
166
|
+
if (rect) {
|
|
167
|
+
const x = rect.left + (typeof window.scrollX === 'number' ? window.scrollX : 0)
|
|
168
|
+
const y = rect.top + (typeof window.scrollY === 'number' ? window.scrollY : 0)
|
|
169
|
+
applySourceLayout(dims, x, y, rect.width, rect.height)
|
|
170
|
+
} else {
|
|
171
|
+
el.measureInWindow((mx, my, width, height) => {
|
|
172
|
+
applySourceLayout(dims, mx, my, width, height)
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
[applySourceLayout]
|
|
177
|
+
)
|
|
178
|
+
|
|
152
179
|
const onTargetLayout = useCallback(
|
|
153
180
|
({
|
|
154
181
|
nativeEvent: {
|
|
@@ -173,12 +200,14 @@ export const useOverlaidPosition = ({
|
|
|
173
200
|
const readyToShow = Boolean(isShown && sourceRef.current)
|
|
174
201
|
useEffect(() => {
|
|
175
202
|
const handleDimensionsChange = ({ window }) => {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
203
|
+
if (Platform.OS === 'web') {
|
|
204
|
+
measureSourceOnWeb(window)
|
|
205
|
+
} else {
|
|
206
|
+
sourceRef.current?.measure((x, y, width, height) => {
|
|
207
|
+
setWindowDimensions(window)
|
|
208
|
+
setSourceLayout({ x, y, width, height })
|
|
209
|
+
})
|
|
210
|
+
}
|
|
182
211
|
}
|
|
183
212
|
|
|
184
213
|
let subscription
|
|
@@ -192,6 +221,7 @@ export const useOverlaidPosition = ({
|
|
|
192
221
|
}
|
|
193
222
|
setSourceLayout(null)
|
|
194
223
|
setTargetDimensions(null)
|
|
224
|
+
hasRemeasuredRef.current = false
|
|
195
225
|
}
|
|
196
226
|
|
|
197
227
|
if (readyToShow) {
|
|
@@ -202,19 +232,29 @@ export const useOverlaidPosition = ({
|
|
|
202
232
|
}
|
|
203
233
|
|
|
204
234
|
return unsubscribe
|
|
205
|
-
}, [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
|
+
useEffect(() => {
|
|
246
|
+
if (!isShown || !sourceRef.current || !targetDimensions || Platform.OS !== 'web') return
|
|
247
|
+
|
|
248
|
+
measureSourceOnWeb()
|
|
249
|
+
hasRemeasuredRef.current = true
|
|
250
|
+
}, [targetDimensions, isShown, measureSourceOnWeb])
|
|
206
251
|
|
|
207
252
|
useEffect(() => {
|
|
208
253
|
if (Platform.OS !== 'web') {
|
|
209
254
|
return undefined
|
|
210
255
|
}
|
|
211
256
|
|
|
212
|
-
const handleScroll = debounce(
|
|
213
|
-
sourceRef.current?.measureInWindow((x, y, width, height) => {
|
|
214
|
-
setWindowDimensions(window)
|
|
215
|
-
setSourceLayout({ x, y, width, height })
|
|
216
|
-
})
|
|
217
|
-
}, DEBOUNCE_DELAY)
|
|
257
|
+
const handleScroll = debounce(measureSourceOnWeb, DEBOUNCE_DELAY)
|
|
218
258
|
|
|
219
259
|
window.addEventListener('scroll', handleScroll)
|
|
220
260
|
|
|
@@ -222,9 +262,19 @@ export const useOverlaidPosition = ({
|
|
|
222
262
|
window.removeEventListener('scroll', handleScroll)
|
|
223
263
|
handleScroll.cancel()
|
|
224
264
|
}
|
|
225
|
-
}, [
|
|
265
|
+
}, [measureSourceOnWeb])
|
|
226
266
|
|
|
227
|
-
|
|
267
|
+
// On web, require hasRemeasuredRef to be true before declaring isReady. This ensures
|
|
268
|
+
// the dropdown is never shown with an intermediate stale position (the blink).
|
|
269
|
+
// On native, hasRemeasuredRef stays false (the web-only effect never runs), so we skip
|
|
270
|
+
// that check to preserve the original native behaviour.
|
|
271
|
+
const isReady = Boolean(
|
|
272
|
+
isShown &&
|
|
273
|
+
sourceLayout &&
|
|
274
|
+
windowDimensions &&
|
|
275
|
+
targetDimensions &&
|
|
276
|
+
(Platform.OS !== 'web' || hasRemeasuredRef.current)
|
|
277
|
+
)
|
|
228
278
|
|
|
229
279
|
const overlaidPosition = isReady
|
|
230
280
|
? getOverlaidPosition({
|
|
@@ -234,7 +284,7 @@ export const useOverlaidPosition = ({
|
|
|
234
284
|
offsets,
|
|
235
285
|
align
|
|
236
286
|
})
|
|
237
|
-
: {}
|
|
287
|
+
: { top: 0, left: 0 }
|
|
238
288
|
|
|
239
289
|
return {
|
|
240
290
|
overlaidPosition,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ComponentType } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface A11yTextProps {
|
|
4
|
+
text: string
|
|
5
|
+
heading?: boolean
|
|
6
|
+
role?: string
|
|
7
|
+
testID?: string
|
|
8
|
+
dataSet?: Record<string, any>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare const A11yText: ComponentType<A11yTextProps>
|
|
12
|
+
|
|
13
|
+
export { A11yText }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface ActionCardProps {
|
|
4
|
+
icon?: ComponentType<any>
|
|
5
|
+
title?: string
|
|
6
|
+
children?: ReactNode
|
|
7
|
+
href?: string
|
|
8
|
+
direction?: boolean
|
|
9
|
+
accessibilityRole?: string
|
|
10
|
+
tokens?: Record<string, string | number | boolean | object>
|
|
11
|
+
variant?: Record<string, string | number | boolean | undefined>
|
|
12
|
+
testID?: string
|
|
13
|
+
dataSet?: Record<string, any>
|
|
14
|
+
onPress?: () => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
declare const ActionCard: ComponentType<ActionCardProps>
|
|
18
|
+
|
|
19
|
+
export { ActionCard }
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ComponentType } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface ActivityIndicatorProps {
|
|
4
|
+
tokens?: Record<string, string | number | boolean | object>
|
|
5
|
+
variant?: Record<string, string | number | boolean | undefined>
|
|
6
|
+
testID?: string
|
|
7
|
+
dataSet?: Record<string, any>
|
|
8
|
+
accessibilityLabel?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare const ActivityIndicator: ComponentType<ActivityIndicatorProps>
|
|
12
|
+
|
|
13
|
+
export { ActivityIndicator }
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface AutocompleteItem {
|
|
4
|
+
id: string
|
|
5
|
+
label: string
|
|
6
|
+
value?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface AutocompleteProps {
|
|
10
|
+
children?: (props: Record<string, any>) => ReactNode
|
|
11
|
+
tokens?: Record<string, string | number | boolean | object>
|
|
12
|
+
copy?: 'en' | 'fr'
|
|
13
|
+
items?: AutocompleteItem[]
|
|
14
|
+
value?: string
|
|
15
|
+
onChange?: (value: string) => void
|
|
16
|
+
onSearch?: (value: string) => void
|
|
17
|
+
onSelect?: (item: AutocompleteItem) => void
|
|
18
|
+
loading?: boolean
|
|
19
|
+
loadingMessage?: string
|
|
20
|
+
emptyText?: string
|
|
21
|
+
testID?: string
|
|
22
|
+
dataSet?: Record<string, any>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare const Autocomplete: ComponentType<AutocompleteProps>
|
|
26
|
+
|
|
27
|
+
export { Autocomplete }
|
package/types/Badge.d.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
2
|
+
|
|
1
3
|
export interface BadgeProps {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
children?: ReactNode
|
|
5
|
+
variant?: BadgeVariants
|
|
6
|
+
tokens?: BadgeTokens
|
|
7
|
+
pointerEvents?: 'all' | 'none' | 'box-only' | 'box-none'
|
|
8
|
+
onLayout?: (event: any) => void
|
|
9
|
+
nativeID?: string
|
|
10
|
+
testID?: string
|
|
11
|
+
dataSet?: Record<string, any>
|
|
5
12
|
}
|
|
6
13
|
|
|
7
|
-
type BadgeVariants = {
|
|
14
|
+
export type BadgeVariants = {
|
|
8
15
|
alternative?: boolean
|
|
9
16
|
inverse?: boolean
|
|
10
17
|
outline?: boolean
|
|
@@ -26,3 +33,7 @@ export type BadgeTokens = {
|
|
|
26
33
|
paddingRight?: number
|
|
27
34
|
paddingTop?: number
|
|
28
35
|
}
|
|
36
|
+
|
|
37
|
+
declare const Badge: ComponentType<BadgeProps>
|
|
38
|
+
|
|
39
|
+
export { Badge }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface ThemeMetadata {
|
|
4
|
+
themeTokensVersion: string
|
|
5
|
+
name: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface Theme {
|
|
9
|
+
metadata: ThemeMetadata
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ThemeOptions {
|
|
13
|
+
forceAbsoluteFontSizing?: boolean
|
|
14
|
+
forceZIndex?: boolean
|
|
15
|
+
defaultViewport?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface BaseProviderProps {
|
|
19
|
+
children: ReactNode
|
|
20
|
+
defaultTheme: Theme
|
|
21
|
+
themeOptions?: ThemeOptions
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare const BaseProvider: ComponentType<BaseProviderProps>
|
|
25
|
+
|
|
26
|
+
export { BaseProvider }
|
package/types/Box.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ReactNode, ReactElement } from 'react'
|
|
1
|
+
import type { ComponentType, ReactNode, ReactElement } from 'react'
|
|
2
2
|
import { ScrollViewProps } from 'react-native'
|
|
3
3
|
import { ResponsiveSpacing, SemanticTag } from './Common'
|
|
4
4
|
|
|
5
|
-
type BoxVariants = {
|
|
5
|
+
export type BoxVariants = {
|
|
6
6
|
background?:
|
|
7
7
|
| 'lightest'
|
|
8
8
|
| 'lighter'
|
|
@@ -21,7 +21,7 @@ type BoxVariants = {
|
|
|
21
21
|
| 'featureBrand'
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
type BoxTokens = {
|
|
24
|
+
export type BoxTokens = {
|
|
25
25
|
backgroundColor?: string
|
|
26
26
|
gradient?: string
|
|
27
27
|
borderWidth?: number
|
|
@@ -49,4 +49,14 @@ export interface BoxProps {
|
|
|
49
49
|
testID?: string
|
|
50
50
|
accessibilityLabel?: string
|
|
51
51
|
accessible?: boolean
|
|
52
|
+
customGradient?: (...args: any[]) => ReactNode
|
|
53
|
+
backgroundImage?: any
|
|
54
|
+
pointerEvents?: 'all' | 'none' | 'box-only' | 'box-none'
|
|
55
|
+
onLayout?: (event: any) => void
|
|
56
|
+
nativeID?: string
|
|
57
|
+
dataSet?: Record<string, any>
|
|
52
58
|
}
|
|
59
|
+
|
|
60
|
+
declare const Box: ComponentType<BoxProps>
|
|
61
|
+
|
|
62
|
+
export { Box }
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
2
|
+
import { IconProps } from './Icon'
|
|
3
|
+
|
|
4
|
+
export interface ButtonProps {
|
|
5
|
+
children: ReactNode | ((state: ButtonPressableState) => ReactNode)
|
|
6
|
+
onPress?: () => void
|
|
7
|
+
inactive?: boolean
|
|
8
|
+
disabled?: boolean
|
|
9
|
+
heightFull?: boolean
|
|
10
|
+
iconPosition?: 'left' | 'right'
|
|
11
|
+
icon?: ComponentType<any>
|
|
12
|
+
iconProps?: IconProps
|
|
13
|
+
tokens?: Record<string, string | number | boolean | object>
|
|
14
|
+
variant?: Record<string, string | number | boolean | undefined>
|
|
15
|
+
accessibilityLabel?: string
|
|
16
|
+
accessibilityRole?: string
|
|
17
|
+
accessible?: boolean
|
|
18
|
+
testID?: string
|
|
19
|
+
dataSet?: Record<string, any>
|
|
20
|
+
href?: string
|
|
21
|
+
hrefAttrs?: Record<string, string>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type ButtonPressableState = {
|
|
25
|
+
hovered: boolean
|
|
26
|
+
focused: boolean
|
|
27
|
+
pressed: boolean
|
|
28
|
+
inactive: boolean
|
|
29
|
+
selected: boolean
|
|
30
|
+
textStyles: Record<string, any>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare const Button: ComponentType<ButtonProps>
|
|
34
|
+
|
|
35
|
+
export { Button }
|
package/types/Card.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface CardProps {
|
|
4
|
+
children?: ReactNode
|
|
5
|
+
tokens?: Record<string, string | number | boolean | object>
|
|
6
|
+
variant?: Record<string, string | number | boolean | undefined>
|
|
7
|
+
onPress?: () => void
|
|
8
|
+
id?: string
|
|
9
|
+
interactiveCard?: Record<string, any>
|
|
10
|
+
backgroundImage?: any
|
|
11
|
+
testID?: string
|
|
12
|
+
dataSet?: Record<string, any>
|
|
13
|
+
accessibilityLabel?: string
|
|
14
|
+
accessibilityRole?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
declare const Card: ComponentType<CardProps>
|
|
18
|
+
|
|
19
|
+
export { Card }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface CardGroupItem {
|
|
4
|
+
id: string
|
|
5
|
+
image?: Record<string, any>
|
|
6
|
+
dictionary?: Record<string, any>
|
|
7
|
+
[key: string]: any
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface CardGroupProps {
|
|
11
|
+
copy?: 'en' | 'fr'
|
|
12
|
+
dictionary?: Record<string, any>
|
|
13
|
+
tokens?: Record<string, string | number | boolean | object>
|
|
14
|
+
variant?: Record<string, string | number | boolean | undefined>
|
|
15
|
+
items?: CardGroupItem[]
|
|
16
|
+
children?: ReactNode
|
|
17
|
+
testID?: string
|
|
18
|
+
dataSet?: Record<string, any>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare const CardGroup: ComponentType<CardGroupProps>
|
|
22
|
+
|
|
23
|
+
export { CardGroup }
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface CarouselProps {
|
|
4
|
+
children: ReactNode
|
|
5
|
+
tokens?: Record<string, string | number | boolean | object>
|
|
6
|
+
variant?: Record<string, string | number | boolean | undefined>
|
|
7
|
+
stepTrackerVariant?: Record<string, string | number | boolean | undefined>
|
|
8
|
+
progressBarVariant?: Record<string, string | number | boolean | undefined>
|
|
9
|
+
itemLabel?: string
|
|
10
|
+
previousNextNavigationPosition?: 'inside' | 'outside' | 'edge'
|
|
11
|
+
copy?: 'en' | 'fr'
|
|
12
|
+
testID?: string
|
|
13
|
+
dataSet?: Record<string, any>
|
|
14
|
+
accessibilityLabel?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CarouselItemProps {
|
|
18
|
+
children: ReactNode
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare const Carousel: ComponentType<CarouselProps> & {
|
|
22
|
+
Item: ComponentType<CarouselItemProps>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { Carousel }
|
package/types/Checkbox.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { ComponentType } from 'react'
|
|
1
2
|
import { StyleProp, ViewStyle, TextStyle } from 'react-native'
|
|
2
3
|
|
|
3
|
-
type CheckboxTokens = {
|
|
4
|
+
export type CheckboxTokens = {
|
|
4
5
|
containerBackgroundColor?: string
|
|
5
6
|
feedbackMarginBottom?: number
|
|
6
7
|
feedbackMarginTop?: number
|
|
@@ -33,7 +34,7 @@ type CheckboxTokens = {
|
|
|
33
34
|
descriptionFontColor?: string
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
type CheckboxProps = {
|
|
37
|
+
export type CheckboxProps = {
|
|
37
38
|
checked?: boolean
|
|
38
39
|
defaultChecked?: boolean
|
|
39
40
|
error?: boolean
|
|
@@ -50,4 +51,16 @@ type CheckboxProps = {
|
|
|
50
51
|
style?: StyleProp<ViewStyle>
|
|
51
52
|
labelStyle?: StyleProp<TextStyle>
|
|
52
53
|
containerStyle?: StyleProp<ViewStyle>
|
|
54
|
+
pointerEvents?: 'all' | 'none' | 'box-only' | 'box-none'
|
|
55
|
+
onLayout?: (event: any) => void
|
|
56
|
+
nativeID?: string
|
|
57
|
+
testID?: string
|
|
58
|
+
dataSet?: Record<string, any>
|
|
59
|
+
accessibilityLabel?: string
|
|
60
|
+
accessibilityRole?: string
|
|
61
|
+
accessible?: boolean
|
|
53
62
|
}
|
|
63
|
+
|
|
64
|
+
declare const Checkbox: ComponentType<CheckboxProps>
|
|
65
|
+
|
|
66
|
+
export { Checkbox }
|