@telus-uds/components-base 3.23.0 → 3.25.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 +25 -1
- package/lib/cjs/Button/ButtonGroup.js +9 -2
- package/lib/cjs/Card/CardBase.js +97 -17
- package/lib/cjs/Card/PressableCardBase.js +12 -8
- package/lib/cjs/Carousel/Carousel.js +35 -4
- package/lib/cjs/FlexGrid/FlexGrid.js +31 -35
- package/lib/cjs/HorizontalScroll/HorizontalScroll.js +5 -2
- package/lib/cjs/Icon/Icon.js +3 -0
- package/lib/cjs/IconButton/IconButton.js +15 -5
- package/lib/cjs/Listbox/GroupControl.js +12 -6
- package/lib/cjs/Listbox/Listbox.js +41 -7
- package/lib/cjs/Listbox/ListboxGroup.js +139 -8
- package/lib/cjs/Listbox/ListboxOverlay.js +10 -5
- package/lib/cjs/Listbox/SecondLevelHeader.js +201 -0
- package/lib/cjs/Listbox/dictionary.js +14 -0
- package/lib/cjs/Shortcuts/Shortcuts.js +169 -0
- package/lib/cjs/Shortcuts/ShortcutsItem.js +280 -0
- package/lib/cjs/Shortcuts/index.js +16 -0
- package/lib/cjs/TextInput/TextInputBase.js +2 -3
- package/lib/cjs/Tooltip/Tooltip.native.js +2 -0
- package/lib/cjs/index.js +15 -0
- package/lib/cjs/utils/index.js +9 -1
- package/lib/cjs/utils/resolveContentMaxWidth.js +30 -0
- package/lib/esm/Button/ButtonGroup.js +9 -2
- package/lib/esm/Card/CardBase.js +97 -17
- package/lib/esm/Card/PressableCardBase.js +10 -8
- package/lib/esm/Carousel/Carousel.js +37 -6
- package/lib/esm/FlexGrid/FlexGrid.js +31 -35
- package/lib/esm/HorizontalScroll/HorizontalScroll.js +6 -3
- package/lib/esm/Icon/Icon.js +3 -0
- package/lib/esm/IconButton/IconButton.js +15 -5
- package/lib/esm/Listbox/GroupControl.js +12 -6
- package/lib/esm/Listbox/Listbox.js +41 -7
- package/lib/esm/Listbox/ListboxGroup.js +141 -10
- package/lib/esm/Listbox/ListboxOverlay.js +10 -5
- package/lib/esm/Listbox/SecondLevelHeader.js +194 -0
- package/lib/esm/Listbox/dictionary.js +8 -0
- package/lib/esm/Shortcuts/Shortcuts.js +160 -0
- package/lib/esm/Shortcuts/ShortcutsItem.js +273 -0
- package/lib/esm/Shortcuts/index.js +3 -0
- package/lib/esm/TextInput/TextInputBase.js +2 -3
- package/lib/esm/Tooltip/Tooltip.native.js +2 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/utils/index.js +2 -1
- package/lib/esm/utils/resolveContentMaxWidth.js +24 -0
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Button/ButtonGroup.jsx +20 -3
- package/src/Card/CardBase.jsx +113 -14
- package/src/Card/PressableCardBase.jsx +17 -5
- package/src/Carousel/Carousel.jsx +38 -6
- package/src/FlexGrid/FlexGrid.jsx +30 -39
- package/src/HorizontalScroll/HorizontalScroll.jsx +6 -3
- package/src/Icon/Icon.jsx +3 -0
- package/src/IconButton/IconButton.jsx +12 -5
- package/src/Listbox/GroupControl.jsx +41 -33
- package/src/Listbox/Listbox.jsx +41 -2
- package/src/Listbox/ListboxGroup.jsx +158 -26
- package/src/Listbox/ListboxOverlay.jsx +18 -5
- package/src/Listbox/SecondLevelHeader.jsx +182 -0
- package/src/Listbox/dictionary.js +8 -0
- package/src/Shortcuts/Shortcuts.jsx +174 -0
- package/src/Shortcuts/ShortcutsItem.jsx +297 -0
- package/src/Shortcuts/index.js +4 -0
- package/src/TextInput/TextInputBase.jsx +2 -2
- package/src/Tooltip/Tooltip.native.jsx +2 -1
- package/src/index.js +1 -0
- package/src/utils/index.js +1 -0
- package/src/utils/resolveContentMaxWidth.js +28 -0
- package/types/Listbox.d.ts +24 -0
- package/types/Shortcuts.d.ts +136 -0
- package/types/Status.d.ts +42 -0
- package/types/index.d.ts +15 -0
|
@@ -5,6 +5,7 @@ import { useThemeTokens } from '../ThemeProvider'
|
|
|
5
5
|
import Icon from '../Icon'
|
|
6
6
|
import Spacer from '../Spacer'
|
|
7
7
|
import { useListboxContext } from './ListboxContext'
|
|
8
|
+
import { variantProp } from '../utils'
|
|
8
9
|
|
|
9
10
|
const styles = StyleSheet.create({
|
|
10
11
|
container: {
|
|
@@ -47,39 +48,45 @@ const selectContainerStyles = (tokens) => ({
|
|
|
47
48
|
borderBottomColor: tokens.groupBorderBottomColor
|
|
48
49
|
})
|
|
49
50
|
|
|
50
|
-
const GroupControl = React.forwardRef(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
51
|
+
const GroupControl = React.forwardRef(
|
|
52
|
+
({ expanded, pressed, hover, focus, label, id, variant = {} }, ref) => {
|
|
53
|
+
const { selectedId, setSelectedId } = useListboxContext()
|
|
54
|
+
const isSecondLevel = variant?.secondLevel === true
|
|
55
|
+
const tokens = useThemeTokens(
|
|
56
|
+
'Listbox',
|
|
57
|
+
variant,
|
|
58
|
+
{},
|
|
59
|
+
{
|
|
60
|
+
expanded,
|
|
61
|
+
pressed,
|
|
62
|
+
hover,
|
|
63
|
+
current: selectedId === id && id !== undefined,
|
|
64
|
+
focus,
|
|
65
|
+
secondLevel: isSecondLevel
|
|
66
|
+
}
|
|
67
|
+
)
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
69
|
+
const displayIcon = isSecondLevel ? tokens.secondLevelParentIcon : tokens.groupIcon
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<View
|
|
73
|
+
onPress={() => setSelectedId(id)}
|
|
74
|
+
style={[styles.container, selectContainerStyles(tokens)]}
|
|
75
|
+
ref={ref}
|
|
76
|
+
>
|
|
77
|
+
<Text style={selectTextStyles(tokens)}>{label}</Text>
|
|
78
|
+
<Spacer space={1} direction="row" />
|
|
79
|
+
{displayIcon && (
|
|
80
|
+
<Icon
|
|
81
|
+
icon={displayIcon}
|
|
82
|
+
tokens={{ color: tokens.groupColor }}
|
|
83
|
+
variant={{ size: 'micro' }}
|
|
84
|
+
/>
|
|
85
|
+
)}
|
|
86
|
+
</View>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
)
|
|
83
90
|
|
|
84
91
|
GroupControl.displayName = 'GroupControl'
|
|
85
92
|
|
|
@@ -89,7 +96,8 @@ GroupControl.propTypes = {
|
|
|
89
96
|
pressed: PropTypes.bool,
|
|
90
97
|
hover: PropTypes.bool,
|
|
91
98
|
focus: PropTypes.bool,
|
|
92
|
-
label: PropTypes.string
|
|
99
|
+
label: PropTypes.string,
|
|
100
|
+
variant: variantProp.propType
|
|
93
101
|
}
|
|
94
102
|
|
|
95
103
|
export default GroupControl
|
package/src/Listbox/Listbox.jsx
CHANGED
|
@@ -8,11 +8,14 @@ import ListboxGroup from './ListboxGroup'
|
|
|
8
8
|
import ListboxItem from './ListboxItem'
|
|
9
9
|
import { ListboxContext } from './ListboxContext'
|
|
10
10
|
import DropdownOverlay from './ListboxOverlay'
|
|
11
|
+
import defaultDictionary from './dictionary'
|
|
11
12
|
|
|
12
13
|
const styles = StyleSheet.create({
|
|
13
14
|
container: {
|
|
14
15
|
padding: 0,
|
|
15
|
-
margin: 0
|
|
16
|
+
margin: 0,
|
|
17
|
+
position: 'relative',
|
|
18
|
+
overflow: 'visible'
|
|
16
19
|
}
|
|
17
20
|
})
|
|
18
21
|
|
|
@@ -41,6 +44,8 @@ const Listbox = React.forwardRef(
|
|
|
41
44
|
LinkRouter,
|
|
42
45
|
itemRouterProps,
|
|
43
46
|
onClose,
|
|
47
|
+
copy = 'en',
|
|
48
|
+
dictionary = defaultDictionary,
|
|
44
49
|
variant,
|
|
45
50
|
tokens,
|
|
46
51
|
testID
|
|
@@ -49,6 +54,7 @@ const Listbox = React.forwardRef(
|
|
|
49
54
|
) => {
|
|
50
55
|
const initialOpen = getInitialOpen(items, defaultSelectedId)
|
|
51
56
|
const [selectedId, setSelectedId] = React.useState(defaultSelectedId)
|
|
57
|
+
const [activeSecondLevelGroup, setActiveSecondLevelGroup] = React.useState(null)
|
|
52
58
|
const listboxTokens = useThemeTokens('Listbox', tokens, variant)
|
|
53
59
|
|
|
54
60
|
// We need to keep track of each item's ref in order to be able to
|
|
@@ -102,8 +108,15 @@ const Listbox = React.forwardRef(
|
|
|
102
108
|
return () => {}
|
|
103
109
|
}, [onClose, handleKeydown])
|
|
104
110
|
|
|
111
|
+
const contextValue = {
|
|
112
|
+
selectedId,
|
|
113
|
+
setSelectedId,
|
|
114
|
+
activeSecondLevelGroup,
|
|
115
|
+
setActiveSecondLevelGroup
|
|
116
|
+
}
|
|
117
|
+
|
|
105
118
|
return (
|
|
106
|
-
<ListboxContext.Provider value={
|
|
119
|
+
<ListboxContext.Provider value={contextValue}>
|
|
107
120
|
<ExpandCollapse initialOpen={initialOpen} maxOpen={1} ref={ref}>
|
|
108
121
|
{(expandProps) => (
|
|
109
122
|
<View
|
|
@@ -121,6 +134,10 @@ const Listbox = React.forwardRef(
|
|
|
121
134
|
return currentItemRef
|
|
122
135
|
}
|
|
123
136
|
|
|
137
|
+
if (!nestedItems && activeSecondLevelGroup) {
|
|
138
|
+
return null
|
|
139
|
+
}
|
|
140
|
+
|
|
124
141
|
return nestedItems ? (
|
|
125
142
|
<ListboxGroup
|
|
126
143
|
{...item}
|
|
@@ -131,6 +148,11 @@ const Listbox = React.forwardRef(
|
|
|
131
148
|
nextItemRef={itemRefs.current[index + 1] ?? null}
|
|
132
149
|
ref={index === 0 ? firstItemRef : itemRef}
|
|
133
150
|
key={itemId}
|
|
151
|
+
copy={copy}
|
|
152
|
+
dictionary={dictionary}
|
|
153
|
+
variant={variant}
|
|
154
|
+
tokens={tokens}
|
|
155
|
+
onClose={onClose}
|
|
134
156
|
/>
|
|
135
157
|
) : (
|
|
136
158
|
<ListboxItem
|
|
@@ -142,6 +164,8 @@ const Listbox = React.forwardRef(
|
|
|
142
164
|
prevItemRef={itemRefs.current[index - 1] ?? null}
|
|
143
165
|
nextItemRef={itemRefs.current[index + 1] ?? null}
|
|
144
166
|
ref={index === 0 ? firstItemRef : itemRef}
|
|
167
|
+
variant={variant}
|
|
168
|
+
tokens={tokens}
|
|
145
169
|
/>
|
|
146
170
|
)
|
|
147
171
|
})}
|
|
@@ -183,6 +207,21 @@ Listbox.propTypes = {
|
|
|
183
207
|
* Test ID for testing
|
|
184
208
|
*/
|
|
185
209
|
testID: PropTypes.string,
|
|
210
|
+
/**
|
|
211
|
+
* Select English or French copy
|
|
212
|
+
*/
|
|
213
|
+
copy: PropTypes.oneOf(['en', 'fr']),
|
|
214
|
+
/**
|
|
215
|
+
* Override the default dictionary, by passing the complete dictionary object for `en` and `fr`
|
|
216
|
+
*/
|
|
217
|
+
dictionary: PropTypes.shape({
|
|
218
|
+
en: PropTypes.shape({
|
|
219
|
+
closeMenu: PropTypes.string.isRequired
|
|
220
|
+
}),
|
|
221
|
+
fr: PropTypes.shape({
|
|
222
|
+
closeMenu: PropTypes.string.isRequired
|
|
223
|
+
})
|
|
224
|
+
}),
|
|
186
225
|
/**
|
|
187
226
|
* Listbox variant
|
|
188
227
|
*/
|
|
@@ -2,21 +2,36 @@
|
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import PropTypes from 'prop-types'
|
|
4
4
|
import { View, StyleSheet, Platform } from 'react-native'
|
|
5
|
-
import { withLinkRouter } from '../utils'
|
|
5
|
+
import { withLinkRouter, variantProp, copyPropTypes } from '../utils'
|
|
6
|
+
import { useThemeTokens } from '../ThemeProvider'
|
|
6
7
|
import ExpandCollapse from '../ExpandCollapse'
|
|
7
8
|
import ListboxItem from './ListboxItem'
|
|
8
9
|
import { useListboxContext } from './ListboxContext'
|
|
9
10
|
import GroupControl from './GroupControl'
|
|
11
|
+
import SecondLevelHeader from './SecondLevelHeader'
|
|
12
|
+
import defaultDictionary from './dictionary'
|
|
10
13
|
|
|
11
14
|
const styles = StyleSheet.create({
|
|
12
15
|
groupWrapper: {
|
|
13
16
|
margin: 0,
|
|
14
17
|
padding: 0,
|
|
15
|
-
overflow: '
|
|
18
|
+
overflow: 'visible'
|
|
16
19
|
},
|
|
17
20
|
list: {
|
|
18
21
|
margin: 0,
|
|
19
22
|
padding: 0
|
|
23
|
+
},
|
|
24
|
+
secondLevelContainer: {
|
|
25
|
+
margin: 0,
|
|
26
|
+
padding: 0,
|
|
27
|
+
width: '100%',
|
|
28
|
+
display: 'flex',
|
|
29
|
+
flexDirection: 'column'
|
|
30
|
+
},
|
|
31
|
+
secondLevelList: {
|
|
32
|
+
margin: 0,
|
|
33
|
+
padding: 0,
|
|
34
|
+
width: '100%'
|
|
20
35
|
}
|
|
21
36
|
})
|
|
22
37
|
|
|
@@ -27,6 +42,10 @@ const getAccessibilityRole = () =>
|
|
|
27
42
|
web: 'listitem'
|
|
28
43
|
})
|
|
29
44
|
|
|
45
|
+
const selectSecondLevelContainerStyles = ({ secondLevelHeaderBackgroundColor }) => ({
|
|
46
|
+
backgroundColor: secondLevelHeaderBackgroundColor
|
|
47
|
+
})
|
|
48
|
+
|
|
30
49
|
const ListboxGroup = React.forwardRef(
|
|
31
50
|
(
|
|
32
51
|
{
|
|
@@ -38,11 +57,86 @@ const ListboxGroup = React.forwardRef(
|
|
|
38
57
|
expandProps,
|
|
39
58
|
onLastItemBlur,
|
|
40
59
|
nextItemRef,
|
|
41
|
-
prevItemRef
|
|
60
|
+
prevItemRef,
|
|
61
|
+
copy = 'en',
|
|
62
|
+
dictionary = defaultDictionary,
|
|
63
|
+
variant = {},
|
|
64
|
+
tokens = {},
|
|
65
|
+
onClose
|
|
42
66
|
},
|
|
43
67
|
ref
|
|
44
68
|
) => {
|
|
45
|
-
const { selectedId } = useListboxContext()
|
|
69
|
+
const { selectedId, activeSecondLevelGroup, setActiveSecondLevelGroup } = useListboxContext()
|
|
70
|
+
const [secondLevelOpen, setSecondLevelOpen] = React.useState(false)
|
|
71
|
+
const isSecondLevel = variant?.secondLevel === true
|
|
72
|
+
const listboxTokens = useThemeTokens('Listbox', variant, tokens)
|
|
73
|
+
const groupId = id ?? label
|
|
74
|
+
|
|
75
|
+
const handleGroupClick = React.useCallback(() => {
|
|
76
|
+
if (isSecondLevel) {
|
|
77
|
+
setSecondLevelOpen(true)
|
|
78
|
+
setActiveSecondLevelGroup(groupId)
|
|
79
|
+
}
|
|
80
|
+
}, [isSecondLevel, groupId, setActiveSecondLevelGroup])
|
|
81
|
+
|
|
82
|
+
const handleBackClick = React.useCallback(() => {
|
|
83
|
+
setSecondLevelOpen(false)
|
|
84
|
+
setActiveSecondLevelGroup(null)
|
|
85
|
+
}, [setActiveSecondLevelGroup])
|
|
86
|
+
|
|
87
|
+
const handleCloseClick = React.useCallback(() => {
|
|
88
|
+
setSecondLevelOpen(false)
|
|
89
|
+
setActiveSecondLevelGroup(null)
|
|
90
|
+
if (onClose) {
|
|
91
|
+
onClose()
|
|
92
|
+
}
|
|
93
|
+
}, [setActiveSecondLevelGroup, onClose])
|
|
94
|
+
|
|
95
|
+
if (isSecondLevel && activeSecondLevelGroup && activeSecondLevelGroup !== groupId) {
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (isSecondLevel && secondLevelOpen) {
|
|
100
|
+
return (
|
|
101
|
+
<View
|
|
102
|
+
style={[styles.secondLevelContainer, selectSecondLevelContainerStyles(listboxTokens)]}
|
|
103
|
+
>
|
|
104
|
+
<SecondLevelHeader
|
|
105
|
+
label={label}
|
|
106
|
+
onBack={handleBackClick}
|
|
107
|
+
onClose={handleCloseClick}
|
|
108
|
+
copy={copy}
|
|
109
|
+
dictionary={dictionary}
|
|
110
|
+
variant={variant}
|
|
111
|
+
tokens={tokens}
|
|
112
|
+
/>
|
|
113
|
+
<View style={styles.secondLevelList}>
|
|
114
|
+
{items &&
|
|
115
|
+
items.map((item, index) => {
|
|
116
|
+
return (
|
|
117
|
+
<ListboxItem
|
|
118
|
+
key={item.label}
|
|
119
|
+
id={item.id ?? item.label}
|
|
120
|
+
{...item}
|
|
121
|
+
selected={
|
|
122
|
+
(item.id && item.id === selectedId) ||
|
|
123
|
+
(item.label && item.label === selectedId)
|
|
124
|
+
}
|
|
125
|
+
isChild={false}
|
|
126
|
+
LinkRouter={LinkRouter}
|
|
127
|
+
linkRouterProps={linkRouterProps}
|
|
128
|
+
variant={variant}
|
|
129
|
+
tokens={tokens}
|
|
130
|
+
{...(index === 0 && { prevItemRef })}
|
|
131
|
+
{...(index === items.length - 1 && { nextItemRef })}
|
|
132
|
+
{...(index === items.length - 1 && { onBlur: onLastItemBlur })}
|
|
133
|
+
/>
|
|
134
|
+
)
|
|
135
|
+
})}
|
|
136
|
+
</View>
|
|
137
|
+
</View>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
46
140
|
|
|
47
141
|
// TODO: implement keyboard navigation via refs for grouped items separately here
|
|
48
142
|
return (
|
|
@@ -63,9 +157,11 @@ const ListboxGroup = React.forwardRef(
|
|
|
63
157
|
// TODO refactor
|
|
64
158
|
// eslint-disable-next-line react/no-unstable-nested-components
|
|
65
159
|
control={(controlProps) => (
|
|
66
|
-
<GroupControl id={id ?? label} {...controlProps} label={label} />
|
|
160
|
+
<GroupControl id={id ?? label} {...controlProps} label={label} variant={variant} />
|
|
67
161
|
)}
|
|
68
162
|
{...expandProps}
|
|
163
|
+
{...(isSecondLevel && { open: false })}
|
|
164
|
+
{...(isSecondLevel && { onPress: handleGroupClick })}
|
|
69
165
|
tokens={{
|
|
70
166
|
contentPaddingLeft: 0,
|
|
71
167
|
contentPaddingRight: 0,
|
|
@@ -79,26 +175,31 @@ const ListboxGroup = React.forwardRef(
|
|
|
79
175
|
}}
|
|
80
176
|
controlRef={ref}
|
|
81
177
|
>
|
|
82
|
-
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
178
|
+
{!isSecondLevel && (
|
|
179
|
+
<View style={styles.list}>
|
|
180
|
+
{items.map((item, index) => {
|
|
181
|
+
return (
|
|
182
|
+
<ListboxItem
|
|
183
|
+
key={item.label}
|
|
184
|
+
id={item.id ?? item.label}
|
|
185
|
+
{...item}
|
|
186
|
+
selected={
|
|
187
|
+
(item.id && item.id === selectedId) ||
|
|
188
|
+
(item.label && item.label === selectedId)
|
|
189
|
+
}
|
|
190
|
+
isChild
|
|
191
|
+
LinkRouter={LinkRouter}
|
|
192
|
+
linkRouterProps={linkRouterProps}
|
|
193
|
+
variant={variant}
|
|
194
|
+
tokens={tokens}
|
|
195
|
+
{...(index === 0 && { prevItemRef })}
|
|
196
|
+
{...(index === items.length - 1 && { nextItemRef })}
|
|
197
|
+
{...(index === items.length - 1 && { onBlur: onLastItemBlur })}
|
|
198
|
+
/>
|
|
199
|
+
)
|
|
200
|
+
})}
|
|
201
|
+
</View>
|
|
202
|
+
)}
|
|
102
203
|
</ExpandCollapse.Panel>
|
|
103
204
|
</View>
|
|
104
205
|
)
|
|
@@ -108,6 +209,10 @@ ListboxGroup.displayName = 'ListboxGroup'
|
|
|
108
209
|
|
|
109
210
|
ListboxGroup.propTypes = {
|
|
110
211
|
...withLinkRouter.propTypes,
|
|
212
|
+
/**
|
|
213
|
+
* Unique identifier for the group
|
|
214
|
+
*/
|
|
215
|
+
id: PropTypes.string,
|
|
111
216
|
label: PropTypes.string,
|
|
112
217
|
items: PropTypes.arrayOf(
|
|
113
218
|
PropTypes.shape({
|
|
@@ -122,7 +227,34 @@ ListboxGroup.propTypes = {
|
|
|
122
227
|
/**
|
|
123
228
|
* Use this callback to redirect the focus after it leaves the last item of the group.
|
|
124
229
|
*/
|
|
125
|
-
onLastItemBlur: PropTypes.func
|
|
230
|
+
onLastItemBlur: PropTypes.func,
|
|
231
|
+
/**
|
|
232
|
+
* Select English or French copy
|
|
233
|
+
*/
|
|
234
|
+
copy: copyPropTypes,
|
|
235
|
+
/**
|
|
236
|
+
* Override the default dictionary, by passing the complete dictionary object for `en` and `fr`
|
|
237
|
+
*/
|
|
238
|
+
dictionary: PropTypes.shape({
|
|
239
|
+
en: PropTypes.shape({
|
|
240
|
+
closeMenu: PropTypes.string.isRequired
|
|
241
|
+
}),
|
|
242
|
+
fr: PropTypes.shape({
|
|
243
|
+
closeMenu: PropTypes.string.isRequired
|
|
244
|
+
})
|
|
245
|
+
}),
|
|
246
|
+
/**
|
|
247
|
+
* Variant configuration for secondLevel behavior
|
|
248
|
+
*/
|
|
249
|
+
variant: variantProp.propType,
|
|
250
|
+
/**
|
|
251
|
+
* Custom tokens
|
|
252
|
+
*/
|
|
253
|
+
tokens: PropTypes.object,
|
|
254
|
+
/**
|
|
255
|
+
* Callback when the menu is closed
|
|
256
|
+
*/
|
|
257
|
+
onClose: PropTypes.func
|
|
126
258
|
}
|
|
127
259
|
|
|
128
260
|
export default ListboxGroup
|
|
@@ -23,10 +23,21 @@ const paddingHorizontal = 0
|
|
|
23
23
|
|
|
24
24
|
const DropdownOverlay = React.forwardRef(
|
|
25
25
|
(
|
|
26
|
-
{
|
|
26
|
+
{
|
|
27
|
+
children,
|
|
28
|
+
isReady = false,
|
|
29
|
+
overlaidPosition,
|
|
30
|
+
maxWidth,
|
|
31
|
+
minWidth,
|
|
32
|
+
onLayout,
|
|
33
|
+
tokens,
|
|
34
|
+
testID,
|
|
35
|
+
variant
|
|
36
|
+
},
|
|
37
|
+
|
|
27
38
|
ref
|
|
28
39
|
) => {
|
|
29
|
-
const systemTokens = useThemeTokens('Listbox',
|
|
40
|
+
const systemTokens = useThemeTokens('Listbox', variant, tokens)
|
|
30
41
|
|
|
31
42
|
return (
|
|
32
43
|
<View
|
|
@@ -43,11 +54,12 @@ const DropdownOverlay = React.forwardRef(
|
|
|
43
54
|
<Card
|
|
44
55
|
tokens={{
|
|
45
56
|
shadow: systemTokens.shadow,
|
|
57
|
+
borderRadius: systemTokens.borderRadius,
|
|
58
|
+
...(Platform.OS === 'web' && { overflowY: 'hidden' }),
|
|
46
59
|
paddingBottom: paddingVertical,
|
|
47
60
|
paddingTop: paddingVertical,
|
|
48
61
|
paddingLeft: paddingHorizontal,
|
|
49
|
-
paddingRight: paddingHorizontal
|
|
50
|
-
...tokens
|
|
62
|
+
paddingRight: paddingHorizontal
|
|
51
63
|
}}
|
|
52
64
|
>
|
|
53
65
|
{children}
|
|
@@ -80,7 +92,8 @@ DropdownOverlay.propTypes = {
|
|
|
80
92
|
minWidth: PropTypes.number,
|
|
81
93
|
onLayout: PropTypes.func,
|
|
82
94
|
tokens: PropTypes.object,
|
|
83
|
-
testID: PropTypes.string
|
|
95
|
+
testID: PropTypes.string,
|
|
96
|
+
variant: PropTypes.object
|
|
84
97
|
}
|
|
85
98
|
|
|
86
99
|
export default Platform.OS === 'web' ? withPortal(DropdownOverlay) : DropdownOverlay
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import { View, StyleSheet, Text, Pressable } from 'react-native'
|
|
4
|
+
import { useThemeTokens } from '../ThemeProvider'
|
|
5
|
+
import { useCopy, variantProp, copyPropTypes } from '../utils'
|
|
6
|
+
import Icon from '../Icon'
|
|
7
|
+
import IconButton from '../IconButton'
|
|
8
|
+
import Divider from '../Divider'
|
|
9
|
+
import defaultDictionary from './dictionary'
|
|
10
|
+
|
|
11
|
+
const styles = StyleSheet.create({
|
|
12
|
+
headerContainer: {
|
|
13
|
+
width: '100%'
|
|
14
|
+
},
|
|
15
|
+
headerContent: {
|
|
16
|
+
flexDirection: 'row',
|
|
17
|
+
alignItems: 'center',
|
|
18
|
+
width: '100%'
|
|
19
|
+
},
|
|
20
|
+
leftSection: {
|
|
21
|
+
flexDirection: 'row',
|
|
22
|
+
alignItems: 'center',
|
|
23
|
+
flex: 1
|
|
24
|
+
},
|
|
25
|
+
backIcon: {
|
|
26
|
+
marginRight: 8,
|
|
27
|
+
flexShrink: 0
|
|
28
|
+
},
|
|
29
|
+
labelText: {
|
|
30
|
+
flex: 1
|
|
31
|
+
},
|
|
32
|
+
closeButton: {
|
|
33
|
+
flexShrink: 0
|
|
34
|
+
},
|
|
35
|
+
dividerContainer: {
|
|
36
|
+
width: '100%'
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const selectHeaderContainerStyles = ({ secondLevelHeaderBackgroundColor }) => ({
|
|
41
|
+
backgroundColor: secondLevelHeaderBackgroundColor
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const selectHeaderContentStyles = ({
|
|
45
|
+
secondLevelHeaderPaddingTop,
|
|
46
|
+
secondLevelHeaderPaddingBottom,
|
|
47
|
+
secondLevelHeaderPaddingLeft,
|
|
48
|
+
secondLevelHeaderPaddingRight
|
|
49
|
+
}) => ({
|
|
50
|
+
paddingTop: secondLevelHeaderPaddingTop,
|
|
51
|
+
paddingBottom: secondLevelHeaderPaddingBottom,
|
|
52
|
+
paddingLeft: secondLevelHeaderPaddingLeft,
|
|
53
|
+
paddingRight: secondLevelHeaderPaddingRight
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const selectLabelTextStyles = ({
|
|
57
|
+
secondLevelBackLinkFontName,
|
|
58
|
+
secondLevelBackLinkFontWeight,
|
|
59
|
+
secondLevelBackLinkFontSize,
|
|
60
|
+
secondLevelBackLinkColor
|
|
61
|
+
}) => ({
|
|
62
|
+
fontFamily: `${secondLevelBackLinkFontName}${secondLevelBackLinkFontWeight}normal`,
|
|
63
|
+
fontSize: secondLevelBackLinkFontSize,
|
|
64
|
+
color: secondLevelBackLinkColor
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* SecondLevelHeader component for Listbox secondLevel variant.
|
|
69
|
+
* Displays a header with back button icon, title text, and close button (IconButton),
|
|
70
|
+
* separated from content by a Divider.
|
|
71
|
+
*/
|
|
72
|
+
const SecondLevelHeader = React.forwardRef(
|
|
73
|
+
(
|
|
74
|
+
{
|
|
75
|
+
label,
|
|
76
|
+
onBack,
|
|
77
|
+
onClose,
|
|
78
|
+
copy = 'en',
|
|
79
|
+
dictionary = defaultDictionary,
|
|
80
|
+
tokens: tokensProp = {},
|
|
81
|
+
variant = {}
|
|
82
|
+
},
|
|
83
|
+
ref
|
|
84
|
+
) => {
|
|
85
|
+
const tokens = useThemeTokens('Listbox', variant, tokensProp)
|
|
86
|
+
const getCopy = useCopy({ dictionary, copy })
|
|
87
|
+
|
|
88
|
+
const {
|
|
89
|
+
secondLevelBackIcon,
|
|
90
|
+
secondLevelBackIconColor,
|
|
91
|
+
secondLevelCloseIcon,
|
|
92
|
+
secondLevelCloseIconSize,
|
|
93
|
+
secondLevelCloseButtonBorderWidth,
|
|
94
|
+
secondLevelCloseButtonPadding,
|
|
95
|
+
secondLevelDividerColor,
|
|
96
|
+
secondLevelDividerWidth
|
|
97
|
+
} = tokens
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<View style={[styles.headerContainer, selectHeaderContainerStyles(tokens)]} ref={ref}>
|
|
101
|
+
<View style={[styles.headerContent, selectHeaderContentStyles(tokens)]}>
|
|
102
|
+
<Pressable onPress={onBack} style={styles.leftSection}>
|
|
103
|
+
<View style={styles.backIcon}>
|
|
104
|
+
<Icon
|
|
105
|
+
icon={secondLevelBackIcon}
|
|
106
|
+
tokens={{
|
|
107
|
+
color: secondLevelBackIconColor
|
|
108
|
+
}}
|
|
109
|
+
variant={{ size: 'micro' }}
|
|
110
|
+
/>
|
|
111
|
+
</View>
|
|
112
|
+
<Text numberOfLines={1} style={[styles.labelText, selectLabelTextStyles(tokens)]}>
|
|
113
|
+
{label}
|
|
114
|
+
</Text>
|
|
115
|
+
</Pressable>
|
|
116
|
+
<View style={styles.closeButton}>
|
|
117
|
+
<IconButton
|
|
118
|
+
icon={secondLevelCloseIcon}
|
|
119
|
+
onPress={onClose}
|
|
120
|
+
accessibilityLabel={getCopy('closeMenu')}
|
|
121
|
+
tokens={{
|
|
122
|
+
iconSize: secondLevelCloseIconSize,
|
|
123
|
+
borderWidth: secondLevelCloseButtonBorderWidth,
|
|
124
|
+
padding: secondLevelCloseButtonPadding
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
127
|
+
</View>
|
|
128
|
+
</View>
|
|
129
|
+
<View style={styles.dividerContainer}>
|
|
130
|
+
<Divider
|
|
131
|
+
tokens={{
|
|
132
|
+
color: secondLevelDividerColor,
|
|
133
|
+
width: secondLevelDividerWidth
|
|
134
|
+
}}
|
|
135
|
+
/>
|
|
136
|
+
</View>
|
|
137
|
+
</View>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
SecondLevelHeader.displayName = 'SecondLevelHeader'
|
|
143
|
+
|
|
144
|
+
SecondLevelHeader.propTypes = {
|
|
145
|
+
/**
|
|
146
|
+
* The label text to display (typically the parent item label)
|
|
147
|
+
*/
|
|
148
|
+
label: PropTypes.string.isRequired,
|
|
149
|
+
/**
|
|
150
|
+
* Callback when back button is clicked
|
|
151
|
+
*/
|
|
152
|
+
onBack: PropTypes.func.isRequired,
|
|
153
|
+
/**
|
|
154
|
+
* Callback when close button is clicked
|
|
155
|
+
*/
|
|
156
|
+
onClose: PropTypes.func.isRequired,
|
|
157
|
+
/**
|
|
158
|
+
* Select English or French copy
|
|
159
|
+
*/
|
|
160
|
+
copy: copyPropTypes,
|
|
161
|
+
/**
|
|
162
|
+
* Override the default dictionary, by passing the complete dictionary object for `en` and `fr`
|
|
163
|
+
*/
|
|
164
|
+
dictionary: PropTypes.shape({
|
|
165
|
+
en: PropTypes.shape({
|
|
166
|
+
closeMenu: PropTypes.string.isRequired
|
|
167
|
+
}),
|
|
168
|
+
fr: PropTypes.shape({
|
|
169
|
+
closeMenu: PropTypes.string.isRequired
|
|
170
|
+
})
|
|
171
|
+
}),
|
|
172
|
+
/**
|
|
173
|
+
* Custom tokens to override theme tokens
|
|
174
|
+
*/
|
|
175
|
+
tokens: PropTypes.object,
|
|
176
|
+
/**
|
|
177
|
+
* Variant configuration
|
|
178
|
+
*/
|
|
179
|
+
variant: variantProp.propType
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export default SecondLevelHeader
|