@telus-uds/components-base 1.24.2 → 2.0.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 -2
- package/component-docs.json +55 -1
- package/lib/Button/ButtonBase.js +2 -1
- package/lib/Carousel/Carousel.js +9 -1
- package/lib/Carousel/CarouselStepTracker/CarouselStepTracker.js +11 -2
- package/lib/MultiSelectFilter/ModalOverlay.js +136 -0
- package/lib/MultiSelectFilter/MultiSelectFilter.js +64 -26
- package/lib/Typography/Typography.js +27 -2
- package/lib-module/Button/ButtonBase.js +2 -1
- package/lib-module/Carousel/Carousel.js +9 -1
- package/lib-module/Carousel/CarouselStepTracker/CarouselStepTracker.js +10 -2
- package/lib-module/MultiSelectFilter/ModalOverlay.js +112 -0
- package/lib-module/MultiSelectFilter/MultiSelectFilter.js +65 -27
- package/lib-module/Typography/Typography.js +26 -2
- package/package.json +2 -2
- package/src/Button/ButtonBase.jsx +1 -1
- package/src/Carousel/Carousel.jsx +6 -1
- package/src/Carousel/CarouselStepTracker/CarouselStepTracker.jsx +7 -1
- package/src/MultiSelectFilter/ModalOverlay.jsx +86 -0
- package/src/MultiSelectFilter/MultiSelectFilter.jsx +73 -49
- package/src/Typography/Typography.jsx +26 -2
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import View from "react-native-web/dist/exports/View";
|
|
4
|
+
import StyleSheet from "react-native-web/dist/exports/StyleSheet";
|
|
5
|
+
import { Portal } from '@gorhom/portal';
|
|
6
|
+
import { copyPropTypes, getTokensPropType, selectTokens, useCopy, variantProp } from '../utils';
|
|
7
|
+
import { useViewport } from '../ViewportProvider';
|
|
8
|
+
import { useThemeTokens } from '../ThemeProvider';
|
|
9
|
+
import dictionary from './dictionary';
|
|
10
|
+
import Card from '../Card';
|
|
11
|
+
import IconButton from '../IconButton';
|
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
+
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
15
|
+
const staticStyles = StyleSheet.create({
|
|
16
|
+
positioner: {
|
|
17
|
+
flex: 1,
|
|
18
|
+
// Grow to maxWidth when possible, shrink when not possible
|
|
19
|
+
position: 'absolute',
|
|
20
|
+
height: 330,
|
|
21
|
+
paddingTop: 5,
|
|
22
|
+
zIndex: 10000 // Position on top of all the other overlays, including backdrops and modals
|
|
23
|
+
|
|
24
|
+
},
|
|
25
|
+
closeButtonContainer: {
|
|
26
|
+
position: 'absolute',
|
|
27
|
+
top: 0,
|
|
28
|
+
right: 0,
|
|
29
|
+
zIndex: 1
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const selectCloseButtonContainerStyles = _ref => {
|
|
34
|
+
let {
|
|
35
|
+
paddingRight,
|
|
36
|
+
paddingTop
|
|
37
|
+
} = _ref;
|
|
38
|
+
return {
|
|
39
|
+
paddingRight,
|
|
40
|
+
paddingTop
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const selectPaddingContainerStyles = _ref2 => {
|
|
45
|
+
let {
|
|
46
|
+
paddingTop,
|
|
47
|
+
paddingLeft,
|
|
48
|
+
paddingRight
|
|
49
|
+
} = _ref2;
|
|
50
|
+
return {
|
|
51
|
+
paddingBottom: 35,
|
|
52
|
+
paddingTop,
|
|
53
|
+
paddingLeft,
|
|
54
|
+
paddingRight
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const ModalOverlay = /*#__PURE__*/forwardRef((_ref3, ref) => {
|
|
59
|
+
let {
|
|
60
|
+
children,
|
|
61
|
+
tokens,
|
|
62
|
+
variant,
|
|
63
|
+
copy,
|
|
64
|
+
onClose
|
|
65
|
+
} = _ref3;
|
|
66
|
+
const viewport = useViewport();
|
|
67
|
+
const themeTokens = useThemeTokens('Modal', tokens, variant, {
|
|
68
|
+
viewport,
|
|
69
|
+
maxWidth: false
|
|
70
|
+
});
|
|
71
|
+
const {
|
|
72
|
+
closeIcon: CloseIconComponent,
|
|
73
|
+
maxWidth
|
|
74
|
+
} = themeTokens;
|
|
75
|
+
const getCopy = useCopy({
|
|
76
|
+
dictionary,
|
|
77
|
+
copy
|
|
78
|
+
});
|
|
79
|
+
const closeLabel = getCopy('closeButton');
|
|
80
|
+
return /*#__PURE__*/_jsx(_Fragment, {
|
|
81
|
+
children: /*#__PURE__*/_jsx(Portal, {
|
|
82
|
+
ref: ref,
|
|
83
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
84
|
+
style: [{
|
|
85
|
+
minWidth: maxWidth
|
|
86
|
+
}, staticStyles.positioner],
|
|
87
|
+
children: /*#__PURE__*/_jsxs(Card, {
|
|
88
|
+
tokens: selectPaddingContainerStyles(themeTokens),
|
|
89
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
90
|
+
style: [staticStyles.closeButtonContainer, selectCloseButtonContainerStyles(themeTokens)],
|
|
91
|
+
children: /*#__PURE__*/_jsx(IconButton, {
|
|
92
|
+
onPress: onClose,
|
|
93
|
+
icon: CloseIconComponent,
|
|
94
|
+
accessibilityRole: "button",
|
|
95
|
+
accessibilityLabel: closeLabel,
|
|
96
|
+
tokens: selectTokens('IconButton', themeTokens, 'close')
|
|
97
|
+
})
|
|
98
|
+
}), children]
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
ModalOverlay.displayName = 'ModalOverlay';
|
|
105
|
+
ModalOverlay.propTypes = {
|
|
106
|
+
children: PropTypes.node.isRequired,
|
|
107
|
+
variant: variantProp.propType,
|
|
108
|
+
tokens: getTokensPropType('Modal'),
|
|
109
|
+
copy: copyPropTypes,
|
|
110
|
+
onClose: PropTypes.func
|
|
111
|
+
};
|
|
112
|
+
export default ModalOverlay;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { forwardRef, useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import { useThemeTokensCallback } from '../ThemeProvider';
|
|
3
|
+
import { useThemeTokens, useThemeTokensCallback } from '../ThemeProvider';
|
|
4
4
|
import { containUniqueFields, getTokensPropType, getPressHandlersWithArgs, selectTokens, useCopy, useMultipleInputValues, variantProp } from '../utils';
|
|
5
5
|
import dictionary from './dictionary';
|
|
6
6
|
import Box from '../Box';
|
|
@@ -8,21 +8,47 @@ import { Button, ButtonDropdown } from '../Button';
|
|
|
8
8
|
import { CheckboxGroup } from '../Checkbox';
|
|
9
9
|
import Divider from '../Divider';
|
|
10
10
|
import FlexGrid from '../FlexGrid';
|
|
11
|
-
import Modal from '../Modal';
|
|
12
11
|
import Spacer from '../Spacer';
|
|
13
12
|
import StackView from '../StackView';
|
|
14
13
|
import Typography from '../Typography';
|
|
15
14
|
import { TextButton } from '../Link';
|
|
15
|
+
import ModalOverlay from './ModalOverlay';
|
|
16
16
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
17
|
-
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
17
|
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
19
|
const {
|
|
20
20
|
Col,
|
|
21
21
|
Row
|
|
22
22
|
} = FlexGrid;
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
const selectSubTitleTokens = _ref => {
|
|
25
|
+
let {
|
|
26
|
+
subtitleColor
|
|
27
|
+
} = _ref;
|
|
28
|
+
return {
|
|
29
|
+
color: subtitleColor
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const selectDividerToknes = _ref2 => {
|
|
34
|
+
let {
|
|
35
|
+
dividerColor,
|
|
36
|
+
width,
|
|
37
|
+
decorative = true,
|
|
38
|
+
weight = 'thin'
|
|
39
|
+
} = _ref2;
|
|
40
|
+
return {
|
|
41
|
+
color: dividerColor,
|
|
42
|
+
width,
|
|
43
|
+
decorative,
|
|
44
|
+
weight
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const MultiSelectFilter = /*#__PURE__*/forwardRef((_ref3, ref) => {
|
|
24
49
|
let {
|
|
25
50
|
label,
|
|
51
|
+
subtitle,
|
|
26
52
|
id = label,
|
|
27
53
|
variant,
|
|
28
54
|
tokens,
|
|
@@ -36,7 +62,7 @@ const MultiSelectFilter = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
36
62
|
inactive = false,
|
|
37
63
|
rowLimit = 12,
|
|
38
64
|
...rest
|
|
39
|
-
} =
|
|
65
|
+
} = _ref3;
|
|
40
66
|
const {
|
|
41
67
|
currentValues,
|
|
42
68
|
setValues
|
|
@@ -47,6 +73,7 @@ const MultiSelectFilter = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
47
73
|
onChange,
|
|
48
74
|
readOnly
|
|
49
75
|
});
|
|
76
|
+
const themeTokens = useThemeTokens('ButtonDropdown', tokens, variant);
|
|
50
77
|
const getItemTokens = useThemeTokensCallback('ButtonDropdown', tokens, variant);
|
|
51
78
|
|
|
52
79
|
const getButtonTokens = buttonState => selectTokens('Button', getItemTokens(buttonState));
|
|
@@ -74,7 +101,7 @@ const MultiSelectFilter = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
74
101
|
|
|
75
102
|
const handleChange = event => {
|
|
76
103
|
if (pressHandlers.onPress) pressHandlers === null || pressHandlers === void 0 ? void 0 : pressHandlers.onPress(event);
|
|
77
|
-
setIsOpen(
|
|
104
|
+
setIsOpen(!isOpen);
|
|
78
105
|
};
|
|
79
106
|
|
|
80
107
|
const onApply = e => {
|
|
@@ -83,12 +110,20 @@ const MultiSelectFilter = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
83
110
|
};
|
|
84
111
|
|
|
85
112
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
86
|
-
children: [/*#__PURE__*/
|
|
87
|
-
|
|
88
|
-
|
|
113
|
+
children: [/*#__PURE__*/_jsx(ButtonDropdown, {
|
|
114
|
+
ref: ref,
|
|
115
|
+
...pressHandlers,
|
|
116
|
+
value: isOpen,
|
|
117
|
+
selected: isSelected,
|
|
118
|
+
label: label,
|
|
119
|
+
onChange: handleChange,
|
|
120
|
+
tokens: getButtonTokens,
|
|
121
|
+
inactive: inactive
|
|
122
|
+
}, id), isOpen && /*#__PURE__*/_jsxs(ModalOverlay, {
|
|
89
123
|
variant: {
|
|
90
124
|
width: colSize > 1 ? 'size576' : 's'
|
|
91
125
|
},
|
|
126
|
+
onClose: () => setIsOpen(false),
|
|
92
127
|
children: [/*#__PURE__*/_jsx(Row, {
|
|
93
128
|
children: /*#__PURE__*/_jsx(Typography, {
|
|
94
129
|
variant: {
|
|
@@ -96,10 +131,20 @@ const MultiSelectFilter = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
96
131
|
},
|
|
97
132
|
children: getCopy('filterByLabel').replace(/%\{filterCategory\}/g, label.toLowerCase())
|
|
98
133
|
})
|
|
134
|
+
}), subtitle && /*#__PURE__*/_jsxs(_Fragment, {
|
|
135
|
+
children: [/*#__PURE__*/_jsx(Spacer, {
|
|
136
|
+
space: 5
|
|
137
|
+
}), /*#__PURE__*/_jsx(Row, {
|
|
138
|
+
children: /*#__PURE__*/_jsx(Typography, {
|
|
139
|
+
variant: {
|
|
140
|
+
size: 'h5'
|
|
141
|
+
},
|
|
142
|
+
tokens: selectSubTitleTokens(themeTokens),
|
|
143
|
+
children: subtitle
|
|
144
|
+
})
|
|
145
|
+
})]
|
|
99
146
|
}), /*#__PURE__*/_jsx(Spacer, {
|
|
100
147
|
space: 4
|
|
101
|
-
}), /*#__PURE__*/_jsx(Spacer, {
|
|
102
|
-
space: 1
|
|
103
148
|
}), /*#__PURE__*/_jsx(Box, {
|
|
104
149
|
scroll: true,
|
|
105
150
|
children: /*#__PURE__*/_jsx(Row, {
|
|
@@ -116,12 +161,9 @@ const MultiSelectFilter = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
116
161
|
}, i))
|
|
117
162
|
})
|
|
118
163
|
}), /*#__PURE__*/_jsx(Divider, {
|
|
119
|
-
variant: {
|
|
120
|
-
width: 'full'
|
|
121
|
-
|
|
122
|
-
decorative: true,
|
|
123
|
-
weight: 'thin'
|
|
124
|
-
},
|
|
164
|
+
variant: selectDividerToknes({ ...themeTokens,
|
|
165
|
+
width: 'full'
|
|
166
|
+
}),
|
|
125
167
|
space: 4
|
|
126
168
|
}), /*#__PURE__*/_jsx(Row, {
|
|
127
169
|
children: /*#__PURE__*/_jsxs(StackView, {
|
|
@@ -145,16 +187,7 @@ const MultiSelectFilter = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
145
187
|
})]
|
|
146
188
|
})
|
|
147
189
|
})]
|
|
148
|
-
})
|
|
149
|
-
ref: ref,
|
|
150
|
-
...pressHandlers,
|
|
151
|
-
value: isOpen,
|
|
152
|
-
selected: isSelected,
|
|
153
|
-
label: label,
|
|
154
|
-
onChange: handleChange,
|
|
155
|
-
tokens: getButtonTokens,
|
|
156
|
-
inactive: inactive
|
|
157
|
-
}, id)]
|
|
190
|
+
})]
|
|
158
191
|
});
|
|
159
192
|
});
|
|
160
193
|
MultiSelectFilter.displayName = 'MultiSelectFilter';
|
|
@@ -164,6 +197,11 @@ MultiSelectFilter.propTypes = {
|
|
|
164
197
|
*/
|
|
165
198
|
label: PropTypes.string.isRequired,
|
|
166
199
|
|
|
200
|
+
/**
|
|
201
|
+
* The text for the subtitle
|
|
202
|
+
*/
|
|
203
|
+
subtitle: PropTypes.string,
|
|
204
|
+
|
|
167
205
|
/**
|
|
168
206
|
* An optional unique string may be provided to identify the ButtonDropdown.
|
|
169
207
|
* If not provided, the label is used.
|
|
@@ -71,17 +71,41 @@ const Typography = /*#__PURE__*/forwardRef((_ref2, ref) => {
|
|
|
71
71
|
...getA11yPropsFromHtmlTag(tag, accessibilityRole),
|
|
72
72
|
...selectContainerProps(rest)
|
|
73
73
|
};
|
|
74
|
+
|
|
75
|
+
const resetTagStyling = child => {
|
|
76
|
+
if (typeof child === 'object' && ((child === null || child === void 0 ? void 0 : child.type) === 'sub' || (child === null || child === void 0 ? void 0 : child.type) === 'sup')) {
|
|
77
|
+
var _child$props;
|
|
78
|
+
|
|
79
|
+
const sanitizedChild = /*#__PURE__*/React.cloneElement(child, {
|
|
80
|
+
style: { ...(child === null || child === void 0 ? void 0 : (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.style),
|
|
81
|
+
lineHeight: 0
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return sanitizedChild;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return child;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const sanitizeChildren = () => {
|
|
91
|
+
if (Array.isArray(children)) {
|
|
92
|
+
return children.map(resetTagStyling);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return resetTagStyling(children);
|
|
96
|
+
};
|
|
97
|
+
|
|
74
98
|
return block ? /*#__PURE__*/_jsx(View, {
|
|
75
99
|
ref: ref,
|
|
76
100
|
...containerProps,
|
|
77
101
|
children: /*#__PURE__*/_jsx(Text, { ...resolvedTextProps,
|
|
78
|
-
children: children
|
|
102
|
+
children: sanitizeChildren(children)
|
|
79
103
|
})
|
|
80
104
|
}) : /*#__PURE__*/_jsx(Text, {
|
|
81
105
|
ref: ref,
|
|
82
106
|
...containerProps,
|
|
83
107
|
...resolvedTextProps,
|
|
84
|
-
children: children
|
|
108
|
+
children: sanitizeChildren(children)
|
|
85
109
|
});
|
|
86
110
|
});
|
|
87
111
|
Typography.displayName = 'Typography';
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"@floating-ui/react-native": "^0.8.1",
|
|
12
12
|
"@gorhom/portal": "^1.0.14",
|
|
13
13
|
"@telus-uds/system-constants": "^1.2.0",
|
|
14
|
-
"@telus-uds/system-theme-tokens": "^2.
|
|
14
|
+
"@telus-uds/system-theme-tokens": "^2.10.0",
|
|
15
15
|
"airbnb-prop-types": "^2.16.0",
|
|
16
16
|
"lodash.debounce": "^4.0.8",
|
|
17
17
|
"lodash.merge": "^4.6.2",
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"standard-engine": {
|
|
73
73
|
"skip": true
|
|
74
74
|
},
|
|
75
|
-
"version": "
|
|
75
|
+
"version": "2.0.0"
|
|
76
76
|
}
|
|
@@ -174,7 +174,7 @@ const ButtonBase = forwardRef(
|
|
|
174
174
|
ref
|
|
175
175
|
) => {
|
|
176
176
|
const { onPress, ...rest } = clickProps.toPressProps(rawRest)
|
|
177
|
-
const extraButtonState = { inactive, selected }
|
|
177
|
+
const extraButtonState = { inactive, selected, iconPosition }
|
|
178
178
|
const resolveButtonTokens = (pressableState) =>
|
|
179
179
|
resolvePressableTokens(tokens, pressableState, extraButtonState)
|
|
180
180
|
|
|
@@ -145,6 +145,7 @@ const Carousel = React.forwardRef(
|
|
|
145
145
|
children,
|
|
146
146
|
itemLabel = 'item',
|
|
147
147
|
previousNextNavigationPosition = 'inside',
|
|
148
|
+
stepTrackerVariant,
|
|
148
149
|
previousNextIconSize = 'default',
|
|
149
150
|
minDistanceToCapture = 5,
|
|
150
151
|
minDistanceForAction = 0.2,
|
|
@@ -160,7 +161,7 @@ const Carousel = React.forwardRef(
|
|
|
160
161
|
panelNavigation = thumbnails ? (
|
|
161
162
|
<CarouselThumbnailNavigation thumbnails={thumbnails} />
|
|
162
163
|
) : (
|
|
163
|
-
<CarouselStepTracker />
|
|
164
|
+
<CarouselStepTracker variant={stepTrackerVariant} />
|
|
164
165
|
),
|
|
165
166
|
tag = 'ul',
|
|
166
167
|
accessibilityRole,
|
|
@@ -563,6 +564,10 @@ Carousel.propTypes = {
|
|
|
563
564
|
...selectedSystemPropTypes,
|
|
564
565
|
tokens: getTokensPropType('Carousel'),
|
|
565
566
|
variant: variantProp.propType,
|
|
567
|
+
/**
|
|
568
|
+
* Prop related to StepTracker Variants
|
|
569
|
+
*/
|
|
570
|
+
stepTrackerVariant: variantProp.propType,
|
|
566
571
|
/**
|
|
567
572
|
* Slides to render in Carousel. Wrap individual slides in `Carousel.Item`
|
|
568
573
|
*/
|
|
@@ -2,8 +2,9 @@ import React from 'react'
|
|
|
2
2
|
import { useCarousel } from '../CarouselContext'
|
|
3
3
|
import StepTracker from '../../StepTracker'
|
|
4
4
|
import StackView from '../../StackView'
|
|
5
|
+
import { variantProp } from '../../utils'
|
|
5
6
|
|
|
6
|
-
const CarouselStepTracker = () => {
|
|
7
|
+
const CarouselStepTracker = ({ variant }) => {
|
|
7
8
|
const { activeIndex, totalItems, getCopyWithPlaceholders, themeTokens } = useCarousel()
|
|
8
9
|
const stackViewTokens = {
|
|
9
10
|
justifyContent: 'center'
|
|
@@ -28,9 +29,14 @@ const CarouselStepTracker = () => {
|
|
|
28
29
|
stepTrackerLabel: getCopyWithPlaceholders('stepTrackerLabel')
|
|
29
30
|
}}
|
|
30
31
|
tokens={stepTrackerTokens}
|
|
32
|
+
variant={variant}
|
|
31
33
|
/>
|
|
32
34
|
</StackView>
|
|
33
35
|
)
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
CarouselStepTracker.propTypes = {
|
|
39
|
+
variant: variantProp.propType
|
|
40
|
+
}
|
|
41
|
+
|
|
36
42
|
export default CarouselStepTracker
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import { View, StyleSheet } from 'react-native-web'
|
|
4
|
+
import { Portal } from '@gorhom/portal'
|
|
5
|
+
import { copyPropTypes, getTokensPropType, selectTokens, useCopy, variantProp } from '../utils'
|
|
6
|
+
import { useViewport } from '../ViewportProvider'
|
|
7
|
+
import { useThemeTokens } from '../ThemeProvider'
|
|
8
|
+
import dictionary from './dictionary'
|
|
9
|
+
|
|
10
|
+
import Card from '../Card'
|
|
11
|
+
import IconButton from '../IconButton'
|
|
12
|
+
|
|
13
|
+
const staticStyles = StyleSheet.create({
|
|
14
|
+
positioner: {
|
|
15
|
+
flex: 1, // Grow to maxWidth when possible, shrink when not possible
|
|
16
|
+
position: 'absolute',
|
|
17
|
+
height: 330,
|
|
18
|
+
paddingTop: 5,
|
|
19
|
+
zIndex: 10000 // Position on top of all the other overlays, including backdrops and modals
|
|
20
|
+
},
|
|
21
|
+
closeButtonContainer: {
|
|
22
|
+
position: 'absolute',
|
|
23
|
+
top: 0,
|
|
24
|
+
right: 0,
|
|
25
|
+
zIndex: 1
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const selectCloseButtonContainerStyles = ({ paddingRight, paddingTop }) => ({
|
|
30
|
+
paddingRight,
|
|
31
|
+
paddingTop
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const selectPaddingContainerStyles = ({ paddingTop, paddingLeft, paddingRight }) => ({
|
|
35
|
+
paddingBottom: 35,
|
|
36
|
+
paddingTop,
|
|
37
|
+
paddingLeft,
|
|
38
|
+
paddingRight
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const ModalOverlay = forwardRef(({ children, tokens, variant, copy, onClose }, ref) => {
|
|
42
|
+
const viewport = useViewport()
|
|
43
|
+
const themeTokens = useThemeTokens('Modal', tokens, variant, { viewport, maxWidth: false })
|
|
44
|
+
|
|
45
|
+
const { closeIcon: CloseIconComponent, maxWidth } = themeTokens
|
|
46
|
+
|
|
47
|
+
const getCopy = useCopy({ dictionary, copy })
|
|
48
|
+
const closeLabel = getCopy('closeButton')
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<>
|
|
52
|
+
<Portal ref={ref}>
|
|
53
|
+
<View style={[{ minWidth: maxWidth }, staticStyles.positioner]}>
|
|
54
|
+
<Card tokens={selectPaddingContainerStyles(themeTokens)}>
|
|
55
|
+
<View
|
|
56
|
+
style={[
|
|
57
|
+
staticStyles.closeButtonContainer,
|
|
58
|
+
selectCloseButtonContainerStyles(themeTokens)
|
|
59
|
+
]}
|
|
60
|
+
>
|
|
61
|
+
<IconButton
|
|
62
|
+
onPress={onClose}
|
|
63
|
+
icon={CloseIconComponent}
|
|
64
|
+
accessibilityRole="button"
|
|
65
|
+
accessibilityLabel={closeLabel}
|
|
66
|
+
tokens={selectTokens('IconButton', themeTokens, 'close')}
|
|
67
|
+
/>
|
|
68
|
+
</View>
|
|
69
|
+
{children}
|
|
70
|
+
</Card>
|
|
71
|
+
</View>
|
|
72
|
+
</Portal>
|
|
73
|
+
</>
|
|
74
|
+
)
|
|
75
|
+
})
|
|
76
|
+
ModalOverlay.displayName = 'ModalOverlay'
|
|
77
|
+
|
|
78
|
+
ModalOverlay.propTypes = {
|
|
79
|
+
children: PropTypes.node.isRequired,
|
|
80
|
+
variant: variantProp.propType,
|
|
81
|
+
tokens: getTokensPropType('Modal'),
|
|
82
|
+
copy: copyPropTypes,
|
|
83
|
+
onClose: PropTypes.func
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default ModalOverlay
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { forwardRef, useState } from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
|
|
4
|
-
import { useThemeTokensCallback } from '../ThemeProvider'
|
|
4
|
+
import { useThemeTokens, useThemeTokensCallback } from '../ThemeProvider'
|
|
5
5
|
import {
|
|
6
6
|
containUniqueFields,
|
|
7
7
|
getTokensPropType,
|
|
@@ -18,18 +18,30 @@ import { Button, ButtonDropdown } from '../Button'
|
|
|
18
18
|
import { CheckboxGroup } from '../Checkbox'
|
|
19
19
|
import Divider from '../Divider'
|
|
20
20
|
import FlexGrid from '../FlexGrid'
|
|
21
|
-
import Modal from '../Modal'
|
|
22
21
|
import Spacer from '../Spacer'
|
|
23
22
|
import StackView from '../StackView'
|
|
24
23
|
import Typography from '../Typography'
|
|
25
24
|
import { TextButton } from '../Link'
|
|
25
|
+
import ModalOverlay from './ModalOverlay'
|
|
26
26
|
|
|
27
27
|
const { Col, Row } = FlexGrid
|
|
28
28
|
|
|
29
|
+
const selectSubTitleTokens = ({ subtitleColor }) => ({
|
|
30
|
+
color: subtitleColor
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const selectDividerToknes = ({ dividerColor, width, decorative = true, weight = 'thin' }) => ({
|
|
34
|
+
color: dividerColor,
|
|
35
|
+
width,
|
|
36
|
+
decorative,
|
|
37
|
+
weight
|
|
38
|
+
})
|
|
39
|
+
|
|
29
40
|
const MultiSelectFilter = forwardRef(
|
|
30
41
|
(
|
|
31
42
|
{
|
|
32
43
|
label,
|
|
44
|
+
subtitle,
|
|
33
45
|
id = label,
|
|
34
46
|
variant,
|
|
35
47
|
tokens,
|
|
@@ -54,6 +66,7 @@ const MultiSelectFilter = forwardRef(
|
|
|
54
66
|
readOnly
|
|
55
67
|
})
|
|
56
68
|
|
|
69
|
+
const themeTokens = useThemeTokens('ButtonDropdown', tokens, variant)
|
|
57
70
|
const getItemTokens = useThemeTokensCallback('ButtonDropdown', tokens, variant)
|
|
58
71
|
const getButtonTokens = (buttonState) => selectTokens('Button', getItemTokens(buttonState))
|
|
59
72
|
const getCopy = useCopy({ dictionary, copy })
|
|
@@ -74,7 +87,7 @@ const MultiSelectFilter = forwardRef(
|
|
|
74
87
|
|
|
75
88
|
const handleChange = (event) => {
|
|
76
89
|
if (pressHandlers.onPress) pressHandlers?.onPress(event)
|
|
77
|
-
setIsOpen(
|
|
90
|
+
setIsOpen(!isOpen)
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
const onApply = (e) => {
|
|
@@ -84,52 +97,6 @@ const MultiSelectFilter = forwardRef(
|
|
|
84
97
|
|
|
85
98
|
return (
|
|
86
99
|
<>
|
|
87
|
-
<Modal
|
|
88
|
-
isOpen={isOpen}
|
|
89
|
-
onClose={() => setIsOpen(false)}
|
|
90
|
-
variant={{ width: colSize > 1 ? 'size576' : 's' }}
|
|
91
|
-
>
|
|
92
|
-
<Row>
|
|
93
|
-
<Typography variant={{ size: 'h4' }}>
|
|
94
|
-
{getCopy('filterByLabel').replace(/%\{filterCategory\}/g, label.toLowerCase())}
|
|
95
|
-
</Typography>
|
|
96
|
-
</Row>
|
|
97
|
-
<Spacer space={4} />
|
|
98
|
-
<Spacer space={1} />
|
|
99
|
-
<Box scroll={true}>
|
|
100
|
-
<Row distribute="between">
|
|
101
|
-
{[...Array(colSize).keys()].map((i) => (
|
|
102
|
-
<Col xs={12 / colSize} key={i}>
|
|
103
|
-
<CheckboxGroup
|
|
104
|
-
items={items.slice(i * rowLimit, (i + 1) * rowLimit)}
|
|
105
|
-
checkedIds={checkedIds}
|
|
106
|
-
onChange={(e) => setCheckedIds(e, i)}
|
|
107
|
-
/>
|
|
108
|
-
<Spacer size={4} />
|
|
109
|
-
</Col>
|
|
110
|
-
))}
|
|
111
|
-
</Row>
|
|
112
|
-
</Box>
|
|
113
|
-
<Divider
|
|
114
|
-
variant={{ width: 'full', color: 'E3E6E8', decorative: true, weight: 'thin' }}
|
|
115
|
-
space={4}
|
|
116
|
-
/>
|
|
117
|
-
<Row>
|
|
118
|
-
<StackView direction="row" space={3} tokens={{ alignItems: 'center' }}>
|
|
119
|
-
<Button
|
|
120
|
-
onPress={() => onApply(checkedIds)}
|
|
121
|
-
variant={{ size: 'small', priority: 'high' }}
|
|
122
|
-
>
|
|
123
|
-
{getCopy('applyButtonLabel')}
|
|
124
|
-
</Button>
|
|
125
|
-
<Box>
|
|
126
|
-
<TextButton onPress={() => setCheckedIds([])}>
|
|
127
|
-
{getCopy('clearButtonLabel')}
|
|
128
|
-
</TextButton>
|
|
129
|
-
</Box>
|
|
130
|
-
</StackView>
|
|
131
|
-
</Row>
|
|
132
|
-
</Modal>
|
|
133
100
|
<ButtonDropdown
|
|
134
101
|
ref={ref}
|
|
135
102
|
key={id}
|
|
@@ -141,6 +108,59 @@ const MultiSelectFilter = forwardRef(
|
|
|
141
108
|
tokens={getButtonTokens}
|
|
142
109
|
inactive={inactive}
|
|
143
110
|
/>
|
|
111
|
+
{isOpen && (
|
|
112
|
+
<ModalOverlay
|
|
113
|
+
variant={{ width: colSize > 1 ? 'size576' : 's' }}
|
|
114
|
+
onClose={() => setIsOpen(false)}
|
|
115
|
+
>
|
|
116
|
+
<Row>
|
|
117
|
+
<Typography variant={{ size: 'h4' }}>
|
|
118
|
+
{getCopy('filterByLabel').replace(/%\{filterCategory\}/g, label.toLowerCase())}
|
|
119
|
+
</Typography>
|
|
120
|
+
</Row>
|
|
121
|
+
{subtitle && (
|
|
122
|
+
<>
|
|
123
|
+
<Spacer space={5} />
|
|
124
|
+
<Row>
|
|
125
|
+
<Typography variant={{ size: 'h5' }} tokens={selectSubTitleTokens(themeTokens)}>
|
|
126
|
+
{subtitle}
|
|
127
|
+
</Typography>
|
|
128
|
+
</Row>
|
|
129
|
+
</>
|
|
130
|
+
)}
|
|
131
|
+
<Spacer space={4} />
|
|
132
|
+
<Box scroll={true}>
|
|
133
|
+
<Row distribute="between">
|
|
134
|
+
{[...Array(colSize).keys()].map((i) => (
|
|
135
|
+
<Col xs={12 / colSize} key={i}>
|
|
136
|
+
<CheckboxGroup
|
|
137
|
+
items={items.slice(i * rowLimit, (i + 1) * rowLimit)}
|
|
138
|
+
checkedIds={checkedIds}
|
|
139
|
+
onChange={(e) => setCheckedIds(e, i)}
|
|
140
|
+
/>
|
|
141
|
+
<Spacer size={4} />
|
|
142
|
+
</Col>
|
|
143
|
+
))}
|
|
144
|
+
</Row>
|
|
145
|
+
</Box>
|
|
146
|
+
<Divider variant={selectDividerToknes({ ...themeTokens, width: 'full' })} space={4} />
|
|
147
|
+
<Row>
|
|
148
|
+
<StackView direction="row" space={3} tokens={{ alignItems: 'center' }}>
|
|
149
|
+
<Button
|
|
150
|
+
onPress={() => onApply(checkedIds)}
|
|
151
|
+
variant={{ size: 'small', priority: 'high' }}
|
|
152
|
+
>
|
|
153
|
+
{getCopy('applyButtonLabel')}
|
|
154
|
+
</Button>
|
|
155
|
+
<Box>
|
|
156
|
+
<TextButton onPress={() => setCheckedIds([])}>
|
|
157
|
+
{getCopy('clearButtonLabel')}
|
|
158
|
+
</TextButton>
|
|
159
|
+
</Box>
|
|
160
|
+
</StackView>
|
|
161
|
+
</Row>
|
|
162
|
+
</ModalOverlay>
|
|
163
|
+
)}
|
|
144
164
|
</>
|
|
145
165
|
)
|
|
146
166
|
}
|
|
@@ -152,6 +172,10 @@ MultiSelectFilter.propTypes = {
|
|
|
152
172
|
* The text displayed to the user in a ButtonDropdown.
|
|
153
173
|
*/
|
|
154
174
|
label: PropTypes.string.isRequired,
|
|
175
|
+
/**
|
|
176
|
+
* The text for the subtitle
|
|
177
|
+
*/
|
|
178
|
+
subtitle: PropTypes.string,
|
|
155
179
|
/**
|
|
156
180
|
* An optional unique string may be provided to identify the ButtonDropdown.
|
|
157
181
|
* If not provided, the label is used.
|