@telus-uds/components-base 1.72.0 → 1.74.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 +35 -2
- package/lib/Box/Box.js +17 -6
- package/lib/ExpandCollapse/Panel.js +1 -1
- package/lib/FlexGrid/Col/Col.js +42 -19
- package/lib/FlexGrid/FlexGrid.js +40 -17
- package/lib/FlexGrid/Row/Row.js +45 -22
- package/lib/Footnote/Footnote.js +328 -0
- package/lib/Footnote/FootnoteLink.js +108 -0
- package/lib/Footnote/dictionary.js +19 -0
- package/lib/Footnote/index.js +12 -0
- package/lib/Listbox/ListboxGroup.js +7 -1
- package/lib/MultiSelectFilter/MultiSelectFilter.js +1 -0
- package/lib/Notification/Notification.js +13 -5
- package/lib/OrderedList/ItemBase.js +7 -1
- package/lib/Responsive/Responsive.js +32 -14
- package/lib/Responsive/ResponsiveProp.js +46 -0
- package/lib/Responsive/ResponsiveWithMediaQueryStyleSheet.js +75 -0
- package/lib/ThemeProvider/ThemeProvider.js +5 -2
- package/lib/ThemeProvider/index.js +9 -1
- package/lib/ThemeProvider/useResponsiveThemeTokens.js +89 -0
- package/lib/Typography/Typography.js +50 -22
- package/lib/index.js +8 -0
- package/lib/server.js +40 -0
- package/lib/utils/ssr-media-query/utils/create-media-query-styles.js +39 -6
- package/lib-module/Box/Box.js +17 -6
- package/lib-module/ExpandCollapse/Panel.js +1 -1
- package/lib-module/FlexGrid/Col/Col.js +42 -19
- package/lib-module/FlexGrid/FlexGrid.js +40 -17
- package/lib-module/FlexGrid/Row/Row.js +45 -22
- package/lib-module/Footnote/Footnote.js +319 -0
- package/lib-module/Footnote/FootnoteLink.js +101 -0
- package/lib-module/Footnote/dictionary.js +12 -0
- package/lib-module/Footnote/index.js +4 -0
- package/lib-module/Listbox/ListboxGroup.js +7 -1
- package/lib-module/MultiSelectFilter/MultiSelectFilter.js +1 -0
- package/lib-module/Notification/Notification.js +13 -5
- package/lib-module/OrderedList/ItemBase.js +7 -1
- package/lib-module/Responsive/Responsive.js +32 -15
- package/lib-module/Responsive/ResponsiveProp.js +39 -0
- package/lib-module/Responsive/ResponsiveWithMediaQueryStyleSheet.js +67 -0
- package/lib-module/ThemeProvider/ThemeProvider.js +5 -2
- package/lib-module/ThemeProvider/index.js +1 -0
- package/lib-module/ThemeProvider/useResponsiveThemeTokens.js +81 -0
- package/lib-module/Typography/Typography.js +52 -24
- package/lib-module/index.js +1 -0
- package/lib-module/server.js +4 -0
- package/lib-module/utils/ssr-media-query/utils/create-media-query-styles.js +36 -6
- package/package.json +13 -2
- package/src/Box/Box.jsx +35 -17
- package/src/ExpandCollapse/Panel.jsx +1 -1
- package/src/FlexGrid/Col/Col.jsx +42 -13
- package/src/FlexGrid/FlexGrid.jsx +40 -11
- package/src/FlexGrid/Row/Row.jsx +40 -16
- package/src/Footnote/Footnote.jsx +316 -0
- package/src/Footnote/FootnoteLink.jsx +95 -0
- package/src/Footnote/dictionary.js +12 -0
- package/src/Footnote/index.js +6 -0
- package/src/Listbox/ListboxGroup.jsx +9 -2
- package/src/MultiSelectFilter/MultiSelectFilter.jsx +2 -0
- package/src/Notification/Notification.jsx +15 -3
- package/src/OrderedList/ItemBase.jsx +14 -2
- package/src/Responsive/Responsive.jsx +31 -12
- package/src/Responsive/ResponsiveProp.jsx +33 -0
- package/src/Responsive/ResponsiveWithMediaQueryStyleSheet.jsx +60 -0
- package/src/ThemeProvider/ThemeProvider.jsx +5 -2
- package/src/ThemeProvider/index.js +1 -0
- package/src/ThemeProvider/useResponsiveThemeTokens.js +85 -0
- package/src/Typography/Typography.jsx +77 -24
- package/src/index.js +1 -0
- package/src/server.js +4 -0
- package/src/utils/ssr-media-query/utils/create-media-query-styles.js +21 -6
package/src/Box/Box.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { forwardRef } from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import { View, ScrollView } from 'react-native'
|
|
3
|
+
import { View, ScrollView, Platform } from 'react-native'
|
|
4
4
|
import { useThemeTokens } from '../ThemeProvider'
|
|
5
5
|
import {
|
|
6
6
|
a11yProps,
|
|
@@ -23,17 +23,20 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
|
|
|
23
23
|
* @typedef {import('../utils/props/spacingProps.js').SpacingOptions} SpacingOptions
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
const selectBoxStyles = (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
const selectBoxStyles = (
|
|
27
|
+
{
|
|
28
|
+
backgroundColor,
|
|
29
|
+
gradient,
|
|
30
|
+
borderWidth,
|
|
31
|
+
borderColor,
|
|
32
|
+
borderTopLeftRadius,
|
|
33
|
+
borderTopRightRadius,
|
|
34
|
+
borderBottomLeftRadius,
|
|
35
|
+
borderBottomRightRadius,
|
|
36
|
+
...rest
|
|
37
|
+
},
|
|
38
|
+
customGradient
|
|
39
|
+
) => {
|
|
37
40
|
const styles = {
|
|
38
41
|
backgroundColor,
|
|
39
42
|
borderWidth,
|
|
@@ -48,7 +51,12 @@ const selectBoxStyles = ({
|
|
|
48
51
|
angle,
|
|
49
52
|
stops: [stopOne, stopTwo]
|
|
50
53
|
} = gradient
|
|
51
|
-
|
|
54
|
+
|
|
55
|
+
if (Platform.OS === 'web') {
|
|
56
|
+
styles.backgroundImage = `linear-gradient(${angle}deg, ${stopOne.color}, 75%, ${stopTwo.color})`
|
|
57
|
+
} else if (customGradient && Platform.OS !== 'web') {
|
|
58
|
+
styles.colors = [stopOne.color, stopTwo.color]
|
|
59
|
+
}
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
const paddings = ['paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom']
|
|
@@ -145,6 +153,7 @@ const Box = forwardRef(
|
|
|
145
153
|
accessibilityRole,
|
|
146
154
|
testID,
|
|
147
155
|
dataSet,
|
|
156
|
+
customGradient,
|
|
148
157
|
...rest
|
|
149
158
|
},
|
|
150
159
|
ref
|
|
@@ -162,21 +171,26 @@ const Box = forwardRef(
|
|
|
162
171
|
paddingRight: useSpacingScale(right),
|
|
163
172
|
paddingTop: useSpacingScale(top),
|
|
164
173
|
paddingBottom: useSpacingScale(bottom),
|
|
165
|
-
...selectBoxStyles(themeTokens)
|
|
174
|
+
...selectBoxStyles(themeTokens, customGradient)
|
|
166
175
|
}
|
|
167
176
|
|
|
177
|
+
const childrenToRender =
|
|
178
|
+
typeof customGradient === 'function'
|
|
179
|
+
? customGradient(styles.colors, styles)(children)
|
|
180
|
+
: children
|
|
181
|
+
|
|
168
182
|
if (scroll) {
|
|
169
183
|
const scrollProps = typeof scroll === 'object' ? scroll : {}
|
|
170
184
|
scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle]
|
|
171
185
|
return (
|
|
172
186
|
<ScrollView {...scrollProps} {...props} testID={testID} dataSet={dataSet} ref={ref}>
|
|
173
|
-
{
|
|
187
|
+
{childrenToRender}
|
|
174
188
|
</ScrollView>
|
|
175
189
|
)
|
|
176
190
|
}
|
|
177
191
|
return (
|
|
178
192
|
<View {...props} style={styles} testID={testID} dataSet={dataSet} ref={ref}>
|
|
179
|
-
{
|
|
193
|
+
{childrenToRender}
|
|
180
194
|
</View>
|
|
181
195
|
)
|
|
182
196
|
}
|
|
@@ -266,7 +280,11 @@ Box.propTypes = {
|
|
|
266
280
|
/**
|
|
267
281
|
* Box accepts any content as children.
|
|
268
282
|
*/
|
|
269
|
-
children: PropTypes.node.isRequired
|
|
283
|
+
children: PropTypes.node.isRequired,
|
|
284
|
+
/**
|
|
285
|
+
Use this prop if need to add a custom gradient for mobile
|
|
286
|
+
*/
|
|
287
|
+
customGradient: PropTypes.func
|
|
270
288
|
}
|
|
271
289
|
|
|
272
290
|
export default Box
|
|
@@ -168,7 +168,7 @@ const ExpandCollapsePanel = forwardRef(
|
|
|
168
168
|
})
|
|
169
169
|
}}
|
|
170
170
|
>
|
|
171
|
-
<View style={selectContainerStyles(themeTokens)}>{
|
|
171
|
+
<View style={selectContainerStyles(themeTokens)}>{children}</View>
|
|
172
172
|
</View>
|
|
173
173
|
</Animated.View>
|
|
174
174
|
</View>
|
package/src/FlexGrid/Col/Col.jsx
CHANGED
|
@@ -5,6 +5,8 @@ import { Platform } from 'react-native'
|
|
|
5
5
|
import GutterContext from '../providers/GutterContext'
|
|
6
6
|
import applyInheritance from '../helpers'
|
|
7
7
|
import { responsiveProps, BaseView, StyleSheet, createMediaQueryStyles } from '../../utils'
|
|
8
|
+
import { useViewport } from '../../ViewportProvider'
|
|
9
|
+
import { useTheme } from '../../ThemeProvider'
|
|
8
10
|
|
|
9
11
|
const Col = forwardRef(
|
|
10
12
|
(
|
|
@@ -27,6 +29,10 @@ const Col = forwardRef(
|
|
|
27
29
|
ref
|
|
28
30
|
) => {
|
|
29
31
|
const gutter = useContext(GutterContext)
|
|
32
|
+
const viewport = useViewport()
|
|
33
|
+
const {
|
|
34
|
+
themeOptions: { enableMediaQueryStyleSheet }
|
|
35
|
+
} = useTheme()
|
|
30
36
|
const hiddenLevels = applyInheritance([xs, sm, md, lg, xl])
|
|
31
37
|
|
|
32
38
|
const getHorizontalAlignLevel = () => {
|
|
@@ -104,7 +110,16 @@ const Col = forwardRef(
|
|
|
104
110
|
xl: offsetsWithIheritance[4]
|
|
105
111
|
}
|
|
106
112
|
|
|
107
|
-
const
|
|
113
|
+
const staticStyles = {
|
|
114
|
+
flexGrow: 1,
|
|
115
|
+
flexShrink: 0,
|
|
116
|
+
flexBasis: 'auto',
|
|
117
|
+
maxWidth: '100%',
|
|
118
|
+
paddingLeft: gutter ? 16 : 0,
|
|
119
|
+
paddingRight: gutter ? 16 : 0
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const stylesByViewport = {
|
|
108
123
|
xs: {
|
|
109
124
|
display: hiddenLevels[0] === 0 ? 'none' : shown,
|
|
110
125
|
textAlign: horizontalAlignLevel[0],
|
|
@@ -135,21 +150,35 @@ const Col = forwardRef(
|
|
|
135
150
|
...calculateWidth(sizes.xl),
|
|
136
151
|
...calculateOffset(offsets.xl)
|
|
137
152
|
}
|
|
138
|
-
}
|
|
153
|
+
}
|
|
139
154
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
155
|
+
let colStyles
|
|
156
|
+
let mediaIds
|
|
157
|
+
|
|
158
|
+
if (enableMediaQueryStyleSheet) {
|
|
159
|
+
const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
|
|
160
|
+
const { ids, styles } = StyleSheet.create({
|
|
161
|
+
col: {
|
|
162
|
+
...staticStyles,
|
|
163
|
+
...mediaQueryStyles
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
colStyles = styles.col
|
|
167
|
+
mediaIds = ids.col
|
|
168
|
+
} else {
|
|
169
|
+
colStyles = {
|
|
170
|
+
...staticStyles,
|
|
171
|
+
...stylesByViewport[viewport]
|
|
149
172
|
}
|
|
150
|
-
}
|
|
173
|
+
}
|
|
174
|
+
|
|
151
175
|
return (
|
|
152
|
-
<BaseView
|
|
176
|
+
<BaseView
|
|
177
|
+
ref={ref}
|
|
178
|
+
{...viewProps}
|
|
179
|
+
style={colStyles}
|
|
180
|
+
dataSet={mediaIds && { media: mediaIds }}
|
|
181
|
+
>
|
|
153
182
|
{children}
|
|
154
183
|
</BaseView>
|
|
155
184
|
)
|
|
@@ -16,6 +16,8 @@ import Row from './Row'
|
|
|
16
16
|
import Col from './Col'
|
|
17
17
|
import GutterContext from './providers/GutterContext'
|
|
18
18
|
import applyInheritance from './helpers'
|
|
19
|
+
import { useTheme } from '../ThemeProvider'
|
|
20
|
+
import { useViewport } from '../ViewportProvider'
|
|
19
21
|
|
|
20
22
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
21
23
|
|
|
@@ -42,7 +44,15 @@ const FlexGrid = forwardRef(
|
|
|
42
44
|
ref
|
|
43
45
|
) => {
|
|
44
46
|
const reverseLevel = applyInheritance([xsReverse, smReverse, mdReverse, lgReverse, xlReverse])
|
|
45
|
-
const
|
|
47
|
+
const viewport = useViewport()
|
|
48
|
+
const {
|
|
49
|
+
themeOptions: { enableMediaQueryStyleSheet }
|
|
50
|
+
} = useTheme()
|
|
51
|
+
|
|
52
|
+
let flexgridStyles
|
|
53
|
+
let mediaIds
|
|
54
|
+
|
|
55
|
+
const stylesByViewport = {
|
|
46
56
|
xs: {
|
|
47
57
|
flexDirection: reverseLevel[0] ? 'column-reverse' : 'column'
|
|
48
58
|
},
|
|
@@ -62,17 +72,31 @@ const FlexGrid = forwardRef(
|
|
|
62
72
|
maxWidth: limitWidth && viewports.map.get('xl'),
|
|
63
73
|
flexDirection: reverseLevel[4] ? 'column-reverse' : 'column'
|
|
64
74
|
}
|
|
65
|
-
}
|
|
75
|
+
}
|
|
66
76
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
const staticStyles = {
|
|
78
|
+
flexWrap: 'wrap',
|
|
79
|
+
width: outsideGutter ? '100%' : 'auto',
|
|
80
|
+
marginVertical: 0,
|
|
81
|
+
marginHorizontal: outsideGutter ? 'auto' : -16
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (enableMediaQueryStyleSheet) {
|
|
85
|
+
const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
|
|
86
|
+
const { ids, styles } = StyleSheet.create({
|
|
87
|
+
flexgrid: {
|
|
88
|
+
...staticStyles,
|
|
89
|
+
...mediaQueryStyles
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
flexgridStyles = styles.flexgrid
|
|
93
|
+
mediaIds = ids.flexgrid
|
|
94
|
+
} else {
|
|
95
|
+
flexgridStyles = {
|
|
96
|
+
...staticStyles,
|
|
97
|
+
...stylesByViewport[viewport]
|
|
74
98
|
}
|
|
75
|
-
}
|
|
99
|
+
}
|
|
76
100
|
|
|
77
101
|
const props = {
|
|
78
102
|
accessibilityRole,
|
|
@@ -82,7 +106,12 @@ const FlexGrid = forwardRef(
|
|
|
82
106
|
|
|
83
107
|
return (
|
|
84
108
|
<GutterContext.Provider value={gutter}>
|
|
85
|
-
<BaseView
|
|
109
|
+
<BaseView
|
|
110
|
+
ref={ref}
|
|
111
|
+
{...props}
|
|
112
|
+
style={flexgridStyles}
|
|
113
|
+
dataSet={mediaIds && { media: mediaIds }}
|
|
114
|
+
>
|
|
86
115
|
{children}
|
|
87
116
|
</BaseView>
|
|
88
117
|
</GutterContext.Provider>
|
package/src/FlexGrid/Row/Row.jsx
CHANGED
|
@@ -3,6 +3,8 @@ import PropTypes from 'prop-types'
|
|
|
3
3
|
|
|
4
4
|
import applyInheritance from '../helpers'
|
|
5
5
|
import { BaseView, StyleSheet, createMediaQueryStyles } from '../../utils'
|
|
6
|
+
import { useTheme } from '../../ThemeProvider'
|
|
7
|
+
import { useViewport } from '../../ViewportProvider'
|
|
6
8
|
|
|
7
9
|
const horizontalAlignStyles = (horizontalAlign) => {
|
|
8
10
|
switch (horizontalAlign) {
|
|
@@ -66,8 +68,23 @@ const Row = forwardRef(
|
|
|
66
68
|
},
|
|
67
69
|
ref
|
|
68
70
|
) => {
|
|
71
|
+
const {
|
|
72
|
+
themeOptions: { enableMediaQueryStyleSheet }
|
|
73
|
+
} = useTheme()
|
|
74
|
+
const viewport = useViewport()
|
|
75
|
+
const staticStyles = {
|
|
76
|
+
width: '100%',
|
|
77
|
+
marginVertical: 0,
|
|
78
|
+
marginHorizontal: 'auto',
|
|
79
|
+
flexGrow: 0,
|
|
80
|
+
flexShrink: 1,
|
|
81
|
+
flexBasis: 'auto',
|
|
82
|
+
...horizontalAlignStyles(horizontalAlign),
|
|
83
|
+
...verticalAlignStyles(verticalAlign),
|
|
84
|
+
...distributeStyles(distribute)
|
|
85
|
+
}
|
|
69
86
|
const reverseLevel = applyInheritance([xsReverse, smReverse, mdReverse, lgReverse, xlReverse])
|
|
70
|
-
const
|
|
87
|
+
const stylesByViewport = {
|
|
71
88
|
xs: {
|
|
72
89
|
flexDirection: reverseLevel[0] ? 'row-reverse' : 'row',
|
|
73
90
|
flexWrap: reverseLevel[0] ? 'wrap-reverse' : 'wrap'
|
|
@@ -88,23 +105,30 @@ const Row = forwardRef(
|
|
|
88
105
|
flexDirection: reverseLevel[4] ? 'row-reverse' : 'row',
|
|
89
106
|
flexWrap: reverseLevel[4] ? 'wrap-reverse' : 'wrap'
|
|
90
107
|
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let rowStyles
|
|
111
|
+
let mediaIds
|
|
112
|
+
|
|
113
|
+
if (enableMediaQueryStyleSheet) {
|
|
114
|
+
const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
|
|
115
|
+
const { ids, styles } = StyleSheet.create({
|
|
116
|
+
row: {
|
|
117
|
+
...staticStyles,
|
|
118
|
+
...mediaQueryStyles
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
rowStyles = styles.row
|
|
122
|
+
mediaIds = ids.row
|
|
123
|
+
} else {
|
|
124
|
+
rowStyles = {
|
|
125
|
+
...staticStyles,
|
|
126
|
+
...stylesByViewport[viewport]
|
|
104
127
|
}
|
|
105
|
-
}
|
|
128
|
+
}
|
|
129
|
+
|
|
106
130
|
return (
|
|
107
|
-
<BaseView ref={ref} {...rest} style={
|
|
131
|
+
<BaseView ref={ref} {...rest} style={rowStyles} dataSet={mediaIds && { media: mediaIds }}>
|
|
108
132
|
{children}
|
|
109
133
|
</BaseView>
|
|
110
134
|
)
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import React, { useEffect, useState, useCallback } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import {
|
|
4
|
+
StyleSheet,
|
|
5
|
+
Text,
|
|
6
|
+
Dimensions,
|
|
7
|
+
TouchableWithoutFeedback,
|
|
8
|
+
Modal,
|
|
9
|
+
View,
|
|
10
|
+
SafeAreaView,
|
|
11
|
+
ScrollView
|
|
12
|
+
} from 'react-native'
|
|
13
|
+
|
|
14
|
+
import defaultDictionary from './dictionary'
|
|
15
|
+
import { getTokensPropType, htmlAttrs, selectSystemProps, useCopy, viewProps } from '../utils'
|
|
16
|
+
import { useViewport } from '../ViewportProvider'
|
|
17
|
+
import { useTheme, useThemeTokens } from '../ThemeProvider'
|
|
18
|
+
import Typography from '../Typography'
|
|
19
|
+
import Icon from '../Icon'
|
|
20
|
+
import OrderedList from '../OrderedList'
|
|
21
|
+
|
|
22
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs, viewProps])
|
|
23
|
+
|
|
24
|
+
const selectFootnoteStyle = ({
|
|
25
|
+
screenHeight,
|
|
26
|
+
footnoteBackground,
|
|
27
|
+
isVisible,
|
|
28
|
+
footnoteBorderTopSizeMd,
|
|
29
|
+
footnoteBorderColorMd
|
|
30
|
+
}) => ({
|
|
31
|
+
left: 0,
|
|
32
|
+
backgroundColor: footnoteBackground,
|
|
33
|
+
display: isVisible ? 'flex' : 'none',
|
|
34
|
+
height: screenHeight,
|
|
35
|
+
borderTopColor: footnoteBorderColorMd,
|
|
36
|
+
borderTopWidth: footnoteBorderTopSizeMd
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const selectFootnoteHeaderContentStyle = ({
|
|
40
|
+
footnoteHeaderPaddingTop,
|
|
41
|
+
footnoteHeaderPaddingBottom,
|
|
42
|
+
footnoteHeaderPaddingRight,
|
|
43
|
+
footnoteHeaderPaddingLeft,
|
|
44
|
+
headerMargin
|
|
45
|
+
}) => ({
|
|
46
|
+
paddingTop: footnoteHeaderPaddingTop,
|
|
47
|
+
paddingBottom: footnoteHeaderPaddingBottom,
|
|
48
|
+
paddingRight: footnoteHeaderPaddingRight,
|
|
49
|
+
paddingLeft: footnoteHeaderPaddingLeft,
|
|
50
|
+
marginRight: headerMargin
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const selectFootnoteCloseButtonStyle = ({
|
|
54
|
+
closeButtonBorderSize,
|
|
55
|
+
closeButtonBorderColor,
|
|
56
|
+
closeButtonHeight,
|
|
57
|
+
closeButtonBackgroundColor,
|
|
58
|
+
closeButtonMarginTop,
|
|
59
|
+
closeButtonMarginRight,
|
|
60
|
+
closeButtonMarginBottom,
|
|
61
|
+
closeButtonMarginLeft,
|
|
62
|
+
closeButtonWidth
|
|
63
|
+
}) => ({
|
|
64
|
+
backgroundColor: closeButtonBackgroundColor,
|
|
65
|
+
borderWidth: closeButtonBorderSize,
|
|
66
|
+
borderColor: closeButtonBorderColor,
|
|
67
|
+
height: closeButtonHeight,
|
|
68
|
+
marginTop: closeButtonMarginTop,
|
|
69
|
+
marginRight: closeButtonMarginRight,
|
|
70
|
+
marginBottom: closeButtonMarginBottom,
|
|
71
|
+
marginLeft: closeButtonMarginLeft,
|
|
72
|
+
width: closeButtonWidth
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const selectFootnoteBodyStyle = ({
|
|
76
|
+
maxWidth,
|
|
77
|
+
footnoteBodyBackground,
|
|
78
|
+
footnoteBodyPaddingTop,
|
|
79
|
+
footnoteBodyPaddingRight,
|
|
80
|
+
footnoteBodyPaddingBottom,
|
|
81
|
+
footnoteBodyPaddingLeft,
|
|
82
|
+
screenWidth
|
|
83
|
+
}) => ({
|
|
84
|
+
maxWidth,
|
|
85
|
+
backgroundColor: footnoteBodyBackground,
|
|
86
|
+
paddingTop: footnoteBodyPaddingTop,
|
|
87
|
+
paddingRight: footnoteBodyPaddingRight * 2,
|
|
88
|
+
paddingBottom: footnoteBodyPaddingBottom,
|
|
89
|
+
paddingLeft: footnoteBodyPaddingLeft,
|
|
90
|
+
width: screenWidth
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const selectCustomContentFontStyle = ({
|
|
94
|
+
listItemColor,
|
|
95
|
+
listItemFontSize,
|
|
96
|
+
listItemLineHeight,
|
|
97
|
+
listItemPaddingLeft,
|
|
98
|
+
listItemFontName,
|
|
99
|
+
listItemFontWeight
|
|
100
|
+
}) => ({
|
|
101
|
+
fontSize: listItemFontSize,
|
|
102
|
+
lineHeight: listItemLineHeight * listItemFontSize,
|
|
103
|
+
paddingLeft: listItemPaddingLeft,
|
|
104
|
+
color: listItemColor,
|
|
105
|
+
fontName: listItemFontName,
|
|
106
|
+
fontWeight: listItemFontWeight
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Use `Footnote` to display a single legal content.
|
|
111
|
+
*
|
|
112
|
+
* ## Usage Criteria
|
|
113
|
+
*
|
|
114
|
+
* - Use `Footnote` to display a single legal statement
|
|
115
|
+
* - Display on top of all UI, including other sticky elements such as Cart Summary
|
|
116
|
+
* - Dismiss by clicking on the close button, clicking anywhere outside of the `Footnote`
|
|
117
|
+
* - Use copy to set language, ‘en’ for English or ‘fr’ for French
|
|
118
|
+
*
|
|
119
|
+
* ## Accessibility requirements
|
|
120
|
+
*
|
|
121
|
+
* - Only one instance of `Footnote` should display at a time
|
|
122
|
+
* - Place `Footnote` as the last element in the body or main
|
|
123
|
+
* - When `Footnote` is open, the inert prop must be set on all children of body excluding the Footnote
|
|
124
|
+
* - When `Footnote` is closed, focus must return to the initiating element
|
|
125
|
+
*/
|
|
126
|
+
const Footnote = ({
|
|
127
|
+
copy = 'en',
|
|
128
|
+
number = undefined,
|
|
129
|
+
content = undefined,
|
|
130
|
+
onClose,
|
|
131
|
+
isOpen = false,
|
|
132
|
+
tokens,
|
|
133
|
+
variant = {},
|
|
134
|
+
dictionary = defaultDictionary,
|
|
135
|
+
...rest
|
|
136
|
+
}) => {
|
|
137
|
+
const viewport = useViewport()
|
|
138
|
+
|
|
139
|
+
const themeTokens = useThemeTokens('Footnote', tokens, variant, { viewport })
|
|
140
|
+
const themeOptions = useTheme()
|
|
141
|
+
|
|
142
|
+
const getCopy = useCopy({ dictionary, copy })
|
|
143
|
+
|
|
144
|
+
const [isVisible, setIsVisible] = useState(false)
|
|
145
|
+
|
|
146
|
+
const screenHeight = Dimensions.get('screen').height
|
|
147
|
+
const screenWidth = Dimensions.get('screen').width
|
|
148
|
+
|
|
149
|
+
const getFootnoteBodyContent = useCallback(() => {
|
|
150
|
+
if (!number || !content) {
|
|
151
|
+
return null
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (React.isValidElement(content)) {
|
|
155
|
+
return <View style={selectCustomContentFontStyle(themeTokens)}>{content}</View>
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
// TODO: Extract the OrderedList.Item from the array when the issue #4361 is fixed
|
|
160
|
+
<OrderedList start={number}>
|
|
161
|
+
{[
|
|
162
|
+
<OrderedList.Item key={number}>
|
|
163
|
+
<Text style={selectCustomContentFontStyle(themeTokens)}>{content}</Text>
|
|
164
|
+
</OrderedList.Item>
|
|
165
|
+
]}
|
|
166
|
+
</OrderedList>
|
|
167
|
+
)
|
|
168
|
+
}, [content, number, themeTokens])
|
|
169
|
+
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
if (isOpen) {
|
|
172
|
+
setIsVisible(true)
|
|
173
|
+
}
|
|
174
|
+
}, [isOpen])
|
|
175
|
+
|
|
176
|
+
const closeFootnote = useCallback(
|
|
177
|
+
(event, options) => {
|
|
178
|
+
onClose(event, options)
|
|
179
|
+
setIsVisible(false)
|
|
180
|
+
},
|
|
181
|
+
[onClose]
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
const handleClose = (event) => closeFootnote(event, { returnFocus: true })
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<View {...selectProps(rest)}>
|
|
188
|
+
<Modal visible={isVisible} animationType="slide">
|
|
189
|
+
<SafeAreaView style={staticStyles.container}>
|
|
190
|
+
<ScrollView
|
|
191
|
+
style={selectFootnoteStyle({
|
|
192
|
+
screenHeight,
|
|
193
|
+
isVisible,
|
|
194
|
+
...themeTokens
|
|
195
|
+
})}
|
|
196
|
+
>
|
|
197
|
+
<View style={staticStyles.content}>
|
|
198
|
+
<View
|
|
199
|
+
style={[selectFootnoteHeaderContentStyle(themeTokens), staticStyles.headerContent]}
|
|
200
|
+
>
|
|
201
|
+
<Typography
|
|
202
|
+
tokens={{
|
|
203
|
+
fontSize: themeTokens?.headerFontSize,
|
|
204
|
+
lineHeight: themeTokens?.headerLineHeight
|
|
205
|
+
}}
|
|
206
|
+
variant={{ size: 'h4' }}
|
|
207
|
+
>
|
|
208
|
+
{getCopy('heading')}
|
|
209
|
+
</Typography>
|
|
210
|
+
<TouchableWithoutFeedback
|
|
211
|
+
onPress={handleClose}
|
|
212
|
+
accessibilityLabel={getCopy('close')}
|
|
213
|
+
>
|
|
214
|
+
<View
|
|
215
|
+
style={[selectFootnoteCloseButtonStyle(themeTokens), staticStyles.closeButton]}
|
|
216
|
+
>
|
|
217
|
+
<Icon
|
|
218
|
+
icon={themeTokens?.closeIcon}
|
|
219
|
+
tokens={{ size: themeTokens?.closeButtonIconSize }}
|
|
220
|
+
/>
|
|
221
|
+
</View>
|
|
222
|
+
</TouchableWithoutFeedback>
|
|
223
|
+
</View>
|
|
224
|
+
<View
|
|
225
|
+
style={selectFootnoteBodyStyle({
|
|
226
|
+
maxWidth: themeOptions.contentMaxWidth,
|
|
227
|
+
screenWidth,
|
|
228
|
+
...themeTokens
|
|
229
|
+
})}
|
|
230
|
+
>
|
|
231
|
+
{getFootnoteBodyContent()}
|
|
232
|
+
</View>
|
|
233
|
+
</View>
|
|
234
|
+
</ScrollView>
|
|
235
|
+
</SafeAreaView>
|
|
236
|
+
</Modal>
|
|
237
|
+
</View>
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const copyShape = PropTypes.shape({
|
|
242
|
+
close: PropTypes.string.isRequired,
|
|
243
|
+
heading: PropTypes.string.isRequired
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
// If a language dictionary entry is provided, it must contain every key
|
|
247
|
+
const dictionaryContentShape = PropTypes.shape({
|
|
248
|
+
a11yLabel: PropTypes.string.isRequired,
|
|
249
|
+
close: PropTypes.string.isRequired,
|
|
250
|
+
heading: PropTypes.string.isRequired
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
Footnote.propTypes = {
|
|
254
|
+
...selectedSystemPropTypes,
|
|
255
|
+
tokens: getTokensPropType('Footnote'),
|
|
256
|
+
/**
|
|
257
|
+
* The content.
|
|
258
|
+
*/
|
|
259
|
+
content: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
|
|
260
|
+
/**
|
|
261
|
+
* Use the `copy` prop to either select provided English or French copy by passing 'en' or 'fr' respectively.
|
|
262
|
+
* To provide your own, pass a JSON object with the keys `heading` and `close`.
|
|
263
|
+
*/
|
|
264
|
+
copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), copyShape]),
|
|
265
|
+
/**
|
|
266
|
+
* A boolean flag used hide or show the `Footnote`. Set to `true` to open the `Footnote`.
|
|
267
|
+
*/
|
|
268
|
+
isOpen: PropTypes.bool,
|
|
269
|
+
/**
|
|
270
|
+
* The number, must match the number of the `FootnoteLink` that initiated the `Footnote`.
|
|
271
|
+
*/
|
|
272
|
+
number: PropTypes.number,
|
|
273
|
+
/**
|
|
274
|
+
* A callback function to handle the closing of the footnote.
|
|
275
|
+
*
|
|
276
|
+
* @param {SyntheticEvent} event The React `SyntheticEvent`
|
|
277
|
+
* @param {Object} options Custom options
|
|
278
|
+
* @param {boolean} options.returnFocus Should the `Footnote` return focus on close
|
|
279
|
+
*/
|
|
280
|
+
onClose: PropTypes.func.isRequired,
|
|
281
|
+
/**
|
|
282
|
+
* Override the default dictionary, by passing the complete dictionary object for `en` and `fr`
|
|
283
|
+
*/
|
|
284
|
+
dictionary: PropTypes.shape({
|
|
285
|
+
en: dictionaryContentShape,
|
|
286
|
+
fr: dictionaryContentShape
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export default Footnote
|
|
291
|
+
|
|
292
|
+
const staticStyles = StyleSheet.create({
|
|
293
|
+
container: {
|
|
294
|
+
flex: 1
|
|
295
|
+
},
|
|
296
|
+
content: {
|
|
297
|
+
marginLeft: 'auto',
|
|
298
|
+
marginRight: 'auto',
|
|
299
|
+
left: 0,
|
|
300
|
+
right: 0,
|
|
301
|
+
maxWidth: 1200,
|
|
302
|
+
paddingBottom: 100
|
|
303
|
+
},
|
|
304
|
+
headerContent: {
|
|
305
|
+
alignItems: 'center',
|
|
306
|
+
display: 'flex',
|
|
307
|
+
flexDirection: 'row',
|
|
308
|
+
justifyContent: 'space-between'
|
|
309
|
+
},
|
|
310
|
+
closeButton: {
|
|
311
|
+
alignItems: 'center',
|
|
312
|
+
borderRadius: 50,
|
|
313
|
+
display: 'flex',
|
|
314
|
+
justifyContent: 'center'
|
|
315
|
+
}
|
|
316
|
+
})
|