@jobber/components 4.60.3 → 4.60.5

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.
@@ -0,0 +1,403 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var classnames = require('classnames');
5
+ var ReactDOM = require('react-dom');
6
+ var styleInject_es = require('./style-inject.es-9d2f5f4e.js');
7
+ var Icon = require('./Icon-405a216c.js');
8
+ var Text = require('./Text-54334431.js');
9
+ var Typography = require('./Typography-83915c54.js');
10
+ var Button = require('./Button-db6114a6.js');
11
+ var useRefocusOnActivator = require('@jobber/hooks/useRefocusOnActivator');
12
+ var useFocusTrap = require('@jobber/hooks/useFocusTrap');
13
+ var reactPopper = require('react-popper');
14
+ var useOnKeyDown = require('@jobber/hooks/useOnKeyDown');
15
+ var Chip = require('./Chip-f26246d1.js');
16
+ require('@jobber/design');
17
+ var useAssert = require('@jobber/hooks/useAssert');
18
+
19
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
20
+
21
+ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
22
+ var classnames__default = /*#__PURE__*/_interopDefaultLegacy(classnames);
23
+ var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
24
+
25
+ var css_248z$6 = ".m1w5vdUZ6rQ- {\n z-index: 1002;\n z-index: var(--elevation-tooltip);\n max-width: var(--popover--width);\n min-width: calc((16px * 4) * 3.75);\n min-width: calc(calc(16px * 4) * 3.75);\n min-width: calc(var(--space-extravagant) * 3.75);\n box-shadow: 0px calc(16px / 16) calc(16px / 4) 0px\n rgba(0, 0, 0, 0.1),\n 0px calc(16px / 4) 12px 0px rgba(0, 0, 0, 0.05);\n box-shadow: var(--shadow-base);\n margin-top: calc(16px / 2);\n margin-top: var(--space-small);\n border: calc(16px / 16) solid rgb(225, 225, 225);\n border: var(--border-base) solid var(--color-border);\n border-radius: calc(16px / 2);\n border-radius: var(--radius-larger);\n overflow: scroll;\n overflow-y: auto;\n background: rgba(255, 255, 255, 1);\n background: var(--color-surface);\n}\n\n/* CAUTION: Tests for content visibility assume this class' presence hides the content\n* so please be careful if you change this class name, or the css rules within.\n*/\n\n.m1w5vdUZ6rQ-.Zlkv2HA096A- {\n display: none;\n visibility: hidden;\n}\n\n.YQry-Rd6zfQ- {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n position: sticky;\n gap: calc(16px / 4);\n gap: var(--space-smaller);\n bottom: 0;\n width: 100%;\n background-color: rgba(255, 255, 255, 1);\n background-color: var(--color-surface);\n}\n\n.TcpqL34s4lI- {\n padding-bottom: calc(16px / 2);\n padding-bottom: var(--space-small);\n}\n";
26
+ var styles$6 = {"content":"m1w5vdUZ6rQ-","hidden":"Zlkv2HA096A-","actions":"YQry-Rd6zfQ-","actionPadding":"TcpqL34s4lI-"};
27
+ styleInject_es.styleInject(css_248z$6);
28
+
29
+ var css_248z$5 = ".MIyb-oFZg9w- {\n position: relative;\n}\n\n.WZPK91I4aJs- {\n width: 100%;\n padding: calc(16px * 1);\n padding: var(--space-base);\n padding-right: calc((16px * 1.5) * 2.25);\n padding-right: calc(calc(16px * 1.5) * 2.25);\n padding-right: calc(var(--space-large) * 2.25);\n border: none;\n border-bottom: calc(16px / 16) solid rgb(225, 225, 225);\n border-bottom: var(--border-base) solid var(--color-border);\n font-family: \"Inter\", Helvetica, Arial, sans-serif;\n font-family: var(--typography--fontFamily-normal);\n font-size: calc((16px * 1) * 0.875);\n font-size: calc(calc(16px * 1) * 0.875);\n font-size: var(--typography--fontSize-base);\n}\n\n.WZPK91I4aJs-::-webkit-search-decoration,\n.WZPK91I4aJs-::-webkit-search-cancel-button,\n.WZPK91I4aJs-::-webkit-search-results-button,\n.WZPK91I4aJs-::-webkit-search-results-decoration {\n -webkit-appearance: none;\n}\n\n.WZPK91I4aJs-:focus {\n outline: none;\n}\n\n._7loHrVUe-Rk- {\n display: -ms-flexbox;\n display: flex;\n position: absolute;\n top: 50%;\n right: calc(16px * 1);\n right: var(--space-base);\n z-index: 1002;\n z-index: var(--elevation-tooltip);\n width: calc(16px * 1.5);\n width: var(--space-large);\n height: calc(16px * 1.5);\n height: var(--space-large);\n padding: 0;\n border: none;\n border-radius: 100%;\n border-radius: var(--radius-circle);\n background-color: rgb(244, 244, 244);\n background-color: var(--color-surface--background);\n cursor: pointer;\n -webkit-transform: translateY(-50%);\n transform: translateY(-50%);\n -ms-flex-pack: center;\n justify-content: center;\n -ms-flex-align: center;\n align-items: center;\n}\n\n._7loHrVUe-Rk-:focus {\n box-shadow: 0px 0px calc(16px / 4) calc(16px / 8)\n rgb(231, 213, 87);\n box-shadow: var(--shadow-focus);\n}\n";
30
+ var styles$5 = {"search":"MIyb-oFZg9w-","searchInput":"WZPK91I4aJs-","clearSearch":"_7loHrVUe-Rk-"};
31
+ styleInject_es.styleInject(css_248z$5);
32
+
33
+ function ComboboxContentSearch(props) {
34
+ const searchRef = React.useRef(null);
35
+ React.useEffect(() => {
36
+ if (props.open) {
37
+ setTimeout(() => {
38
+ var _a;
39
+ (_a = searchRef.current) === null || _a === void 0 ? void 0 : _a.focus();
40
+ }, 0);
41
+ }
42
+ }, [props.open]);
43
+ return (React__default["default"].createElement("div", { className: styles$5.search },
44
+ React__default["default"].createElement("input", { type: "search", ref: searchRef, className: styles$5.searchInput, placeholder: props.placeholder ? `Search ${props.placeholder}` : "Search", onChange: (event) => handleSearch(event), value: props.searchValue }),
45
+ props.searchValue && (React__default["default"].createElement("button", { className: styles$5.clearSearch, onClick: clearSearch, type: "button", "data-testid": "ATL-Combobox-Content-Search-Clear", "aria-label": "Clear search" },
46
+ React__default["default"].createElement(Icon.Icon, { name: "remove", size: "small" })))));
47
+ function clearSearch() {
48
+ var _a;
49
+ props.setSearchValue("");
50
+ (_a = searchRef.current) === null || _a === void 0 ? void 0 : _a.focus();
51
+ }
52
+ function handleSearch(event) {
53
+ props.setSearchValue(event.target.value);
54
+ }
55
+ }
56
+
57
+ var css_248z$4 = ".SgMzjOcdE-E- {\n position: relative;\n padding: calc(16px / 2) 0;\n padding: var(--space-small) 0;\n}\n\n.TwoTCjgcssc- {\n display: -ms-flexbox;\n display: flex;\n max-height: calc((16px / 2) * 33.33);\n max-height: calc(calc(16px / 2) * 33.33);\n max-height: calc(var(--space-small) * 33.33);\n margin: 0;\n padding: 0;\n overflow: scroll;\n list-style: none;\n -ms-flex-direction: column;\n flex-direction: column;\n gap: calc(16px / 4);\n gap: var(--space-smaller);\n}\n\n._8T5M7MGwCRE-,\n._4y5NXkNeIQM- {\n padding: calc(16px / 2) calc(16px * 1);\n padding: var(--space-small) var(--space-base);\n}\n\n.SgMzjOcdE-E-::after {\n content: \"\";\n display: block;\n position: absolute;\n right: 0;\n bottom: calc(16px / 2);\n bottom: var(--space-small);\n left: 0;\n height: calc(16px * 1);\n height: var(--space-base);\n background: linear-gradient(\n 180deg,\n rgba(255, 255, 255, 0) 0%,\n rgb(255, 255, 255) 100%\n );\n pointer-events: none;\n}\n\n.SgMzjOcdE-E-::before {\n content: \"\";\n display: block;\n position: absolute;\n top: calc(16px / 2);\n top: var(--space-small);\n right: 0;\n left: 0;\n height: calc(16px * 1);\n height: var(--space-base);\n background: linear-gradient(\n to bottom,\n rgba(255, 255, 255, 1),\n rgba(255, 255, 255, 0)\n );\n pointer-events: none;\n}\n";
58
+ var styles$4 = {"container":"SgMzjOcdE-E-","optionsList":"TwoTCjgcssc-","filterMessage":"_8T5M7MGwCRE-","emptyStateMessage":"_4y5NXkNeIQM-"};
59
+ styleInject_es.styleInject(css_248z$4);
60
+
61
+ var css_248z$3 = "._5QdRGmaNHvc- {\n display: -ms-flexbox;\n display: flex;\n min-height: calc((calc(16px * 3) - calc(16px / 4)));\n min-height: calc((var(--space-largest) - var(--space-smaller)));\n box-sizing: border-box;\n margin: 0 calc(16px / 2);\n margin: 0 var(--space-small);\n padding: calc(16px / 2);\n padding: var(--space-small);\n border-radius: calc(16px / 4);\n border-radius: var(--radius-large);\n color: rgb(1, 41, 57);\n color: var(--color-heading);\n font-weight: 500;\n cursor: pointer;\n transition: all 200ms;\n transition: all var(--timing-base);\n -ms-flex-pack: justify;\n justify-content: space-between;\n -ms-flex-align: center;\n align-items: center;\n}\n\n._5QdRGmaNHvc-:hover,\n._5QdRGmaNHvc-:focus {\n background-color: rgb(244, 244, 244);\n background-color: var(--color-surface--background);\n}\n\n._5QdRGmaNHvc-:focus {\n box-shadow: 0px 0px calc(16px / 4) calc(16px / 8)\n rgb(231, 213, 87);\n box-shadow: var(--shadow-focus);\n}\n";
62
+ var styles$3 = {"option":"_5QdRGmaNHvc-"};
63
+ styleInject_es.styleInject(css_248z$3);
64
+
65
+ const ComboboxContext = React__default["default"].createContext({});
66
+ function ComboboxContextProvider(props) {
67
+ return (React__default["default"].createElement(ComboboxContext.Provider, { value: {
68
+ open: props.open,
69
+ setOpen: props.setOpen,
70
+ selected: props.selected,
71
+ selectionHandler: props.selectionHandler,
72
+ handleClose: props.handleClose,
73
+ shouldScroll: props.shouldScroll,
74
+ } }, props.children));
75
+ }
76
+
77
+ function ComboboxOption(props) {
78
+ const { selected, selectionHandler } = React.useContext(ComboboxContext);
79
+ const isSelected = selected.some(selection => selection.id.toString() === props.id.toString());
80
+ return (React__default["default"].createElement("li", { key: props.id, tabIndex: -1, "data-selected": isSelected, role: "option", "aria-selected": isSelected, onClick: () => selectionHandler &&
81
+ selectionHandler({ id: props.id, label: props.label }), className: classnames__default["default"](styles$3.option) },
82
+ props.label,
83
+ isSelected && React__default["default"].createElement(Icon.Icon, { name: "checkmark", color: "blue" })));
84
+ }
85
+
86
+ function ComboboxContentList(props) {
87
+ return (React__default["default"].createElement("div", { className: styles$4.container },
88
+ !props.showEmptyState && props.options.length > 0 && (React__default["default"].createElement("ul", { className: styles$4.optionsList, role: "listbox", "aria-multiselectable": props.multiselect, ref: props.optionsListRef }, !props.showEmptyState &&
89
+ props.options.map(option => {
90
+ return (React__default["default"].createElement(ComboboxOption, { key: option.id, id: option.id, label: option.label }));
91
+ }))),
92
+ !props.showEmptyState && props.options.length === 0 && (React__default["default"].createElement("div", { className: styles$4.filterMessage },
93
+ React__default["default"].createElement(Text.Text, { variation: "subdued" },
94
+ "No results for ",
95
+ `“${props.searchValue}”`))),
96
+ props.showEmptyState && (React__default["default"].createElement("div", { className: styles$4.emptyStateMessage },
97
+ React__default["default"].createElement(Text.Text, { variation: "subdued" }, getZeroIndexStateText(props.subjectNoun))))));
98
+ }
99
+ function getZeroIndexStateText(subjectNoun) {
100
+ if (subjectNoun) {
101
+ return `You don't have any ${subjectNoun} yet`;
102
+ }
103
+ return "No options yet";
104
+ }
105
+
106
+ var css_248z$2 = ".Q-mwo-g5Fi8- {\n display: -ms-flexbox;\n display: flex;\n padding: calc(16px * 1) calc(16px / 2) 0 calc(16px * 1);\n padding: var(--space-base) var(--space-small) 0 var(--space-base);\n -ms-flex-pack: justify;\n justify-content: space-between;\n gap: calc(16px / 4);\n gap: var(--space-smaller);\n}\n";
107
+ var styles$2 = {"header":"Q-mwo-g5Fi8-"};
108
+ styleInject_es.styleInject(css_248z$2);
109
+
110
+ function ComboboxContentHeader(props) {
111
+ const hasSelected = props.selectedCount > 0;
112
+ const actionLabel = hasSelected ? "Clear" : "Select all";
113
+ const label = getLabel(hasSelected, props.selectedCount, props.subjectNoun);
114
+ const handleSelectAll = hasSelected ? props.onClearAll : props.onSelectAll;
115
+ const showAction = hasSelected || props.hasOptionsVisible;
116
+ return (React__default["default"].createElement("div", { className: styles$2.header, "data-testid": "ATL-Combobox-Header" },
117
+ React__default["default"].createElement(Typography.Typography, { textColor: "heading", fontWeight: "semiBold" }, label),
118
+ showAction && (React__default["default"].createElement(Button.Button, { size: "small", label: actionLabel, type: "tertiary", onClick: handleSelectAll }))));
119
+ }
120
+ function getLabel(hasSelected, count, subjectNoun) {
121
+ if (hasSelected) {
122
+ return `${count} selected`;
123
+ }
124
+ if (subjectNoun) {
125
+ return `Select ${subjectNoun}`;
126
+ }
127
+ return "Select";
128
+ }
129
+
130
+ function useComboboxContent(options, open, selected, searchValue) {
131
+ const { shouldScroll } = React.useContext(ComboboxContext);
132
+ const optionsListRef = React.useRef(null);
133
+ const filteredOptions = options.filter(option => option.label.toLowerCase().includes(searchValue.toLowerCase()));
134
+ React.useEffect(() => {
135
+ var _a;
136
+ if (open && shouldScroll.current && optionsListRef.current) {
137
+ const firstSelected = Array.from((_a = optionsListRef === null || optionsListRef === void 0 ? void 0 : optionsListRef.current) === null || _a === void 0 ? void 0 : _a.children).find(child => {
138
+ if (child instanceof HTMLElement) {
139
+ return child.dataset.selected === "true";
140
+ }
141
+ });
142
+ scrollToFirstSelected(firstSelected, shouldScroll);
143
+ }
144
+ }, [open, selected]);
145
+ return {
146
+ filteredOptions,
147
+ optionsListRef,
148
+ };
149
+ }
150
+ function scrollToFirstSelected(firstSelected, shouldScroll) {
151
+ if (firstSelected) {
152
+ firstSelected.scrollIntoView({
153
+ block: "nearest",
154
+ });
155
+ shouldScroll.current = false;
156
+ }
157
+ }
158
+
159
+ // eslint-disable-next-line max-statements
160
+ function useComboboxAccessibility(selectionCallback, filteredOptions, optionsListRef, open, wrapperRef) {
161
+ const { handleClose } = React.useContext(ComboboxContext);
162
+ const hasOptionsVisible = open && filteredOptions.length > 0;
163
+ const focusedIndex = React.useRef(null);
164
+ useRefocusOnActivator.useRefocusOnActivator(open);
165
+ const popperRef = useFocusTrap.useFocusTrap(open);
166
+ const { styles: popperStyles, attributes, update, } = reactPopper.usePopper(wrapperRef.current, popperRef.current, {
167
+ modifiers: [
168
+ {
169
+ name: "flip",
170
+ options: {
171
+ fallbackPlacements: ["top-start"],
172
+ },
173
+ },
174
+ ],
175
+ placement: "bottom-start",
176
+ });
177
+ React.useEffect(() => {
178
+ focusedIndex.current = null;
179
+ if (open) {
180
+ update === null || update === void 0 ? void 0 : update();
181
+ }
182
+ }, [open, filteredOptions.length]);
183
+ React.useEffect(() => {
184
+ var _a;
185
+ if (open) {
186
+ (_a = popperRef.current) === null || _a === void 0 ? void 0 : _a.addEventListener("keydown", handleContentKeydown);
187
+ }
188
+ return () => {
189
+ var _a;
190
+ (_a = popperRef.current) === null || _a === void 0 ? void 0 : _a.removeEventListener("keydown", handleContentKeydown);
191
+ };
192
+ }, [open, optionsListRef, filteredOptions]);
193
+ useOnKeyDown.useOnKeyDown(() => {
194
+ if (open) {
195
+ handleClose();
196
+ }
197
+ }, "Escape");
198
+ function handleContentKeydown(event) {
199
+ var _a;
200
+ if (!hasOptionsVisible)
201
+ return;
202
+ if (event.key === "Enter" || event.key === " ") {
203
+ const activeElementInList = (_a = optionsListRef.current) === null || _a === void 0 ? void 0 : _a.contains(document.activeElement);
204
+ if (!activeElementInList)
205
+ return;
206
+ handleKeyboardSelection(event);
207
+ }
208
+ if (event.key === "ArrowDown") {
209
+ handleKeyboardNavigation(event, 1);
210
+ }
211
+ if (event.key === "ArrowUp") {
212
+ handleKeyboardNavigation(event, -1);
213
+ }
214
+ }
215
+ function handleKeyboardNavigation(event, indexChange) {
216
+ var _a;
217
+ const newIndex = focusedIndex.current === null ? 0 : focusedIndex.current + indexChange;
218
+ if (newIndex < 0 || newIndex >= filteredOptions.length)
219
+ return;
220
+ const optionElement = (_a = optionsListRef.current) === null || _a === void 0 ? void 0 : _a.children[newIndex];
221
+ optionElement === null || optionElement === void 0 ? void 0 : optionElement.focus();
222
+ focusedIndex.current = newIndex;
223
+ event.preventDefault();
224
+ event.stopPropagation();
225
+ }
226
+ function handleKeyboardSelection(event) {
227
+ event.preventDefault();
228
+ event.stopPropagation();
229
+ if (focusedIndex.current !== null) {
230
+ selectionCallback(filteredOptions[focusedIndex.current]);
231
+ }
232
+ }
233
+ return {
234
+ popperRef,
235
+ popperStyles,
236
+ attributes,
237
+ };
238
+ }
239
+
240
+ function ComboboxContent(props) {
241
+ const optionsExist = props.options.length > 0;
242
+ const { filteredOptions, optionsListRef } = useComboboxContent(props.options, props.open, props.selected, props.searchValue);
243
+ const { popperRef, popperStyles, attributes } = useComboboxAccessibility(props.handleSelection, filteredOptions, optionsListRef, props.open, props.wrapperRef);
244
+ const template = (React__default["default"].createElement("div", Object.assign({ ref: popperRef, id: "ATL-Combobox-Content", "data-testid": "ATL-Combobox-Content", tabIndex: 0, className: classnames__default["default"](styles$6.content, { [styles$6.hidden]: !props.open }), style: popperStyles.popper }, attributes.popper),
245
+ React__default["default"].createElement(ComboboxContentSearch, { open: props.open, placeholder: props.subjectNoun, searchValue: props.searchValue, setSearchValue: props.setSearchValue }),
246
+ props.multiselect && optionsExist && (React__default["default"].createElement(ComboboxContentHeader, { hasOptionsVisible: filteredOptions.length > 0, subjectNoun: props.subjectNoun, selectedCount: props.selected.length, onClearAll: () => {
247
+ props.selectedStateSetter([]);
248
+ }, onSelectAll: () => {
249
+ props.selectedStateSetter(filteredOptions);
250
+ } })),
251
+ React__default["default"].createElement(ComboboxContentList, { multiselect: props.multiselect, showEmptyState: !optionsExist, options: filteredOptions, selected: props.selected, optionsListRef: optionsListRef, searchValue: props.searchValue, subjectNoun: props.subjectNoun }),
252
+ props.actionElements && (React__default["default"].createElement("div", { className: styles$6.actions, role: "group" }, props.actionElements.map((child, index, childrenArray) => (React__default["default"].createElement("div", { key: index, className: classnames__default["default"]({
253
+ [styles$6.actionPadding]: index === childrenArray.length - 1,
254
+ }) }, child)))))));
255
+ return ReactDOM__default["default"].createPortal(template, document.body);
256
+ }
257
+
258
+ var css_248z$1 = ".Ow9wSNnGP-g- {\n width: 100%;\n box-sizing: border-box;\n padding: 0 calc(16px / 2);\n padding: 0 var(--space-small);\n background-color: rgba(255, 255, 255, 1);\n background-color: var(--color-surface);\n}\n\n.ncp0nKHQ7d8- {\n position: relative;\n width: 100%;\n min-height: calc((calc(16px * 3) - calc(16px / 2)));\n min-height: calc((var(--space-largest) - var(--space-small)));\n padding: calc(16px / 2) 0;\n padding: var(--space-small) 0;\n border: none;\n border-radius: calc(16px / 4);\n border-radius: var(--radius-large);\n text-align: left;\n background-color: rgba(255, 255, 255, 1);\n background-color: var(--color-surface);\n cursor: pointer;\n transition: all 200ms;\n transition: all var(--timing-base);\n}\n\n.ncp0nKHQ7d8-:focus {\n box-shadow: 0px 0px calc(16px / 4) calc(16px / 8)\n rgb(231, 213, 87);\n box-shadow: var(--shadow-focus);\n background-color: rgb(244, 244, 244);\n background-color: var(--color-surface--background);\n}\n\n.ncp0nKHQ7d8-:hover {\n background-color: rgb(244, 244, 244);\n background-color: var(--color-surface--background);\n}\n\n.ncp0nKHQ7d8- span {\n margin-left: calc(16px / 2);\n margin-left: var(--space-small);\n}\n";
259
+ var styles$1 = {"actionContainer":"Ow9wSNnGP-g-","actionButton":"ncp0nKHQ7d8-"};
260
+ styleInject_es.styleInject(css_248z$1);
261
+
262
+ function ComboboxAction(props) {
263
+ return (React__default["default"].createElement("div", { className: styles$1.actionContainer },
264
+ React__default["default"].createElement("button", { className: styles$1.actionButton, onClick: props.onClick, type: "button", "aria-label": props.label },
265
+ React__default["default"].createElement(Typography.Typography, { element: "span", size: "base", textColor: "green", fontWeight: "semiBold" }, props.label))));
266
+ }
267
+
268
+ function ComboboxTrigger({ label = "Select", selected, }) {
269
+ const { handleClose, open, setOpen } = React__default["default"].useContext(ComboboxContext);
270
+ const hasSelection = selected.length;
271
+ const selectedLabel = selected.map(option => option.label).join(", ");
272
+ return (React__default["default"].createElement(Chip.Chip, { variation: hasSelection ? "base" : "subtle", label: hasSelection ? selectedLabel : "", heading: label, onClick: () => {
273
+ if (open) {
274
+ handleClose();
275
+ }
276
+ else {
277
+ setOpen(true);
278
+ }
279
+ }, role: "combobox" }, !hasSelection && (React__default["default"].createElement(Chip.Chip.Suffix, null,
280
+ React__default["default"].createElement(Icon.Icon, { name: "add", size: "small" })))));
281
+ }
282
+
283
+ var css_248z = ".W8qMZnORYxI- {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 6;\n z-index: var(--elevation-menu);\n background-color: transparent;\n}\n\n._-0dkolvLF-Y- {\n display: inline-block;\n}\n";
284
+ var styles = {"overlay":"W8qMZnORYxI-","wrapper":"_-0dkolvLF-Y-"};
285
+ styleInject_es.styleInject(css_248z);
286
+
287
+ function useMakeComboboxHandlers(setOpen, setSearchValue, selectedOptions, shouldScroll, selectedStateSetter, multiSelect, onClose) {
288
+ const handleClose = React.useCallback(() => {
289
+ setOpen(false);
290
+ setSearchValue("");
291
+ onClose && onClose();
292
+ if (selectedOptions.length > 0) {
293
+ shouldScroll.current = true;
294
+ }
295
+ }, [setOpen, setSearchValue, onClose, selectedOptions.length]);
296
+ const handleSelection = React.useCallback((selection) => {
297
+ if (multiSelect) {
298
+ handleMultiSelect(selectedStateSetter, selectedOptions, selection);
299
+ }
300
+ else {
301
+ handleSingleSelect(selectedStateSetter, selection);
302
+ }
303
+ }, [multiSelect, selectedStateSetter, selectedOptions]);
304
+ const handleSingleSelect = React.useCallback((selectCallback, selection) => {
305
+ selectCallback([selection]);
306
+ handleClose();
307
+ }, []);
308
+ return {
309
+ handleClose,
310
+ handleSelection,
311
+ };
312
+ }
313
+ function handleMultiSelect(selectCallback, selected, selection) {
314
+ if (selected.some(s => s.id === selection.id)) {
315
+ selectCallback(selected.filter(s => s.id !== selection.id));
316
+ }
317
+ else {
318
+ selectCallback([...selected, selection]);
319
+ }
320
+ }
321
+
322
+ function useCombobox(selected, onSelect, onClose, multiSelect) {
323
+ const wrapperRef = React.useRef(null);
324
+ const shouldScroll = React.useRef(false);
325
+ const [open, setOpen] = React.useState(false);
326
+ const [searchValue, setSearchValue] = React.useState("");
327
+ const { handleClose, handleSelection } = useMakeComboboxHandlers(setOpen, setSearchValue, selected, shouldScroll, onSelect, multiSelect, onClose);
328
+ return {
329
+ wrapperRef,
330
+ searchValue,
331
+ setSearchValue,
332
+ open,
333
+ setOpen,
334
+ selectedOptions: selected,
335
+ selectedStateSetter: onSelect,
336
+ shouldScroll,
337
+ handleClose,
338
+ handleSelection,
339
+ };
340
+ }
341
+
342
+ function ComboboxActivator(props) {
343
+ const { handleClose, open, setOpen } = React__default["default"].useContext(ComboboxContext);
344
+ if (props.children.type === Button.Button || props.children.type === Chip.Chip) {
345
+ return React__default["default"].cloneElement(props.children, {
346
+ role: "combobox",
347
+ onClick: () => {
348
+ if (open) {
349
+ handleClose();
350
+ }
351
+ else {
352
+ setOpen(true);
353
+ }
354
+ },
355
+ });
356
+ }
357
+ return props.children;
358
+ }
359
+
360
+ const COMBOBOX_TRIGGER_COUNT_ERROR_MESSAGE = "Combobox must have exactly one Trigger element";
361
+ function useComboboxValidation(children) {
362
+ const optionElements = getCompoundComponents(ComboboxOption, children);
363
+ const actionElements = getCompoundComponents(ComboboxAction, children);
364
+ const activatorElements = getCompoundComponents(ComboboxActivator, children);
365
+ const shouldThrowTriggerError = isInvalid(activatorElements);
366
+ useAssert.useAssert(shouldThrowTriggerError, COMBOBOX_TRIGGER_COUNT_ERROR_MESSAGE);
367
+ return {
368
+ optionElements,
369
+ triggerElement: activatorElements[0],
370
+ actionElements,
371
+ };
372
+ }
373
+ function isInvalid(activators) {
374
+ return activators.length > 1 ? true : false;
375
+ }
376
+ /**
377
+ * Return all instances child component that matches the `type` provided
378
+ */
379
+ function getCompoundComponents(type, children) {
380
+ const childrenArray = React.Children.toArray(children);
381
+ const elements = childrenArray.filter((child) => React.isValidElement(child) && child.type === type);
382
+ return elements;
383
+ }
384
+
385
+ function Combobox(props) {
386
+ const { optionElements, triggerElement, actionElements } = useComboboxValidation(props.children);
387
+ const options = React.useMemo(() => (optionElements === null || optionElements === void 0 ? void 0 : optionElements.map(option => ({
388
+ id: option.props.id,
389
+ label: option.props.label,
390
+ }))) || [], [optionElements]);
391
+ const { selectedOptions, selectedStateSetter, shouldScroll, wrapperRef, searchValue, setSearchValue, open, setOpen, handleClose, handleSelection, } = useCombobox(props.selected, props.onSelect, props.onClose, props.multiSelect);
392
+ return (React__default["default"].createElement(ComboboxContextProvider, { selected: selectedOptions, selectionHandler: handleSelection, open: open, setOpen: setOpen, handleClose: handleClose, shouldScroll: shouldScroll },
393
+ React__default["default"].createElement("div", { ref: wrapperRef, className: styles.wrapper },
394
+ open && (React__default["default"].createElement("div", { className: styles.overlay, onClick: () => handleClose(), "data-testid": "ATL-Combobox-Overlay" })),
395
+ triggerElement || (React__default["default"].createElement(ComboboxTrigger, { label: props.label, selected: props.selected })),
396
+ React__default["default"].createElement(ComboboxContent, { multiselect: props.multiSelect, searchPlaceholder: props.searchPlaceholder, subjectNoun: props.subjectNoun, selected: selectedOptions, actionElements: actionElements, selectedStateSetter: selectedStateSetter, handleSelection: handleSelection, searchValue: searchValue, setSearchValue: setSearchValue, wrapperRef: wrapperRef, open: open, setOpen: setOpen, options: options }))));
397
+ }
398
+ Combobox.Activator = ComboboxActivator;
399
+ Combobox.Action = ComboboxAction;
400
+ Combobox.Option = ComboboxOption;
401
+
402
+ exports.Combobox = Combobox;
403
+ exports.ComboboxContextProvider = ComboboxContextProvider;
@@ -25,23 +25,20 @@ var ReactDOM = require('react-dom');
25
25
  var Checkbox = require('../Checkbox-bc4bc968.js');
26
26
  var reactRouterDom = require('react-router-dom');
27
27
  var AnimatedSwitcher = require('../AnimatedSwitcher-d1e1ddcf.js');
28
- var Popover = require('../Popover-7c966790.js');
29
- var Content = require('../Content-2ca1ffe1.js');
30
- var Chip = require('../Chip-4f12eec8.js');
31
- require('@jobber/hooks/useAssert');
32
- require('../Avatar-1aa30ee1.js');
33
- require('color');
34
- var Typography = require('../Typography-83915c54.js');
35
- require('react-popper');
28
+ var Combobox = require('../Combobox-981633c1.js');
29
+ var Chip = require('../Chip-f26246d1.js');
30
+ var Icon = require('../Icon-405a216c.js');
36
31
  var debounce = require('lodash/debounce');
37
32
  var InputText = require('../InputText-ab237501.js');
38
33
  var Spinner = require('../Spinner-9d8fc7ff.js');
39
- var Icon = require('../Icon-405a216c.js');
34
+ var Typography = require('../Typography-83915c54.js');
35
+ require('../Content-2ca1ffe1.js');
36
+ require('react-popper');
40
37
  require('uuid');
41
38
  require('react-hook-form');
42
- require('../ButtonDismiss-1ccb84ec.js');
43
- require('lodash/sortBy');
44
- require('@jobber/hooks/useLiveAnnounce');
39
+ require('@jobber/hooks/useAssert');
40
+ require('../Avatar-1aa30ee1.js');
41
+ require('color');
45
42
  require('../FormField-fa9254dc.js');
46
43
  require('../InputValidation-377d6c62.js');
47
44
 
@@ -221,14 +218,15 @@ function DataListOverflowFade({ children }) {
221
218
  React__default["default"].createElement("span", { ref: rightRef, className: styles$b.overflowTrigger }))));
222
219
  }
223
220
 
224
- var css_248z$a = ".WRV-UmQmPzo- {\n display: -ms-flexbox;\n display: flex;\n position: relative;\n z-index: 0;\n z-index: var(--elevation-default);\n overflow: hidden;\n gap: calc(16px / 2);\n gap: var(--space-small);\n white-space: nowrap;\n}\n\n.liQeWCMenD0- {\n --overflow-bg: var(--data-list-item-active-color, var(--color-surface));\n\n display: -ms-flexbox;\n\n display: flex;\n position: absolute;\n top: 0;\n right: 0;\n height: 100%;\n padding-left: calc(16px * 1);\n padding-left: var(--space-base);\n background-image: linear-gradient(\n 90deg,\n transparent 0,\n rgba(255, 255, 255, 1) calc(16px * 1),\n rgba(255, 255, 255, 1) 100%\n );\n background-image: linear-gradient(\n 90deg,\n transparent 0,\n var(--overflow-bg) var(--space-base),\n var(--overflow-bg) 100%\n );\n -ms-flex-align: center;\n align-items: center;\n transition: all 200ms;\n transition: all var(--timing-base);\n}\n";
225
- var styles$a = {"tags":"WRV-UmQmPzo-","tagCount":"liQeWCMenD0-"};
221
+ var css_248z$a = "._8MYxAQYdZzM- {\n position: relative;\n}\n\n.WRV-UmQmPzo- {\n display: -ms-flexbox;\n display: flex;\n z-index: 0;\n z-index: var(--elevation-default);\n overflow: hidden;\n gap: calc(16px / 2);\n gap: var(--space-small);\n white-space: nowrap;\n}\n\n.ND-cN-mX5-c- {\n -webkit-mask-image: linear-gradient(\n to left,\n transparent,\n transparent calc(16px * 1.5),\n rgba(0, 0, 0, 1) calc(16px * 3),\n rgba(0, 0, 0, 1)\n );\n -webkit-mask-image: linear-gradient(\n to left,\n transparent,\n transparent var(--space-large),\n rgba(0, 0, 0, 1) var(--space-largest),\n rgba(0, 0, 0, 1)\n );\n mask-image: linear-gradient(\n to left,\n transparent,\n transparent calc(16px * 1.5),\n rgba(0, 0, 0, 1) calc(16px * 3),\n rgba(0, 0, 0, 1)\n );\n mask-image: linear-gradient(\n to left,\n transparent,\n transparent var(--space-large),\n rgba(0, 0, 0, 1) var(--space-largest),\n rgba(0, 0, 0, 1)\n );\n}\n\n.liQeWCMenD0- {\n display: -ms-flexbox;\n display: flex;\n position: absolute;\n top: 0;\n right: 0;\n height: 100%;\n -ms-flex-align: center;\n align-items: center;\n}\n";
222
+ var styles$a = {"tagWrapper":"_8MYxAQYdZzM-","tags":"WRV-UmQmPzo-","tagsMask":"ND-cN-mX5-c-","tagCount":"liQeWCMenD0-"};
226
223
  styleInject_es.styleInject(css_248z$a);
227
224
 
228
225
  function DataListTags({ items }) {
229
226
  const ref = React.useRef(null);
230
227
  const [visibleIndex, setVisibleIndex] = React.useState([]);
231
228
  const visibleItems = visibleIndex.filter(Boolean).length;
229
+ const shouldShowTagCount = Boolean(visibleItems);
232
230
  React.useEffect(() => {
233
231
  var _a;
234
232
  if (!window.IntersectionObserver)
@@ -243,10 +241,12 @@ function DataListTags({ items }) {
243
241
  elements === null || elements === void 0 ? void 0 : elements.forEach(element => observer.unobserve(element));
244
242
  };
245
243
  }, [items]);
246
- return (React__default["default"].createElement("div", { className: styles$a.tags, ref: ref },
247
- items.filter(Boolean).map((tag, index) => (React__default["default"].createElement("div", { key: tag, "data-tag-element": index },
248
- React__default["default"].createElement(InlineLabel.InlineLabel, null, tag)))),
249
- Boolean(visibleItems) && (React__default["default"].createElement("div", { className: styles$a.tagCount },
244
+ return (React__default["default"].createElement("div", { className: styles$a.tagWrapper, ref: ref },
245
+ React__default["default"].createElement("div", { className: classnames__default["default"](styles$a.tags, {
246
+ [styles$a.tagsMask]: shouldShowTagCount,
247
+ }) }, items.filter(Boolean).map((tag, index) => (React__default["default"].createElement("div", { key: tag, "data-tag-element": index },
248
+ React__default["default"].createElement(InlineLabel.InlineLabel, null, tag))))),
249
+ shouldShowTagCount && (React__default["default"].createElement("div", { className: styles$a.tagCount },
250
250
  React__default["default"].createElement(Text.Text, null,
251
251
  "+",
252
252
  visibleItems)))));
@@ -732,30 +732,38 @@ function DataListHeader() {
732
732
 
733
733
  function DataListSort() {
734
734
  const { sorting, headers } = useDataListContext();
735
- const divRef = React.useRef(null);
736
- const [showPopover, setShowPopover] = React.useState(false);
737
735
  if (!sorting)
738
736
  return null;
739
737
  const { sortable, state, onSort } = sorting;
740
738
  const sortByOptions = getSortByOptions();
741
- const canChangeOrder = !(state === null || state === void 0 ? void 0 : state.key);
742
- return (React__default["default"].createElement(React__default["default"].Fragment, null,
743
- React__default["default"].createElement("span", { ref: divRef },
744
- React__default["default"].createElement(Button.Button, Object.assign({}, (!state && { icon: "add", iconOnRight: true }), { label: getButtonLabel(), variation: "subtle", onClick: () => setShowPopover(!showPopover) }))),
745
- React__default["default"].createElement(Popover.Popover, { attachTo: divRef, open: showPopover, onRequestClose: () => setShowPopover(false) },
746
- React__default["default"].createElement(Content.Content, null,
747
- React__default["default"].createElement(Heading.Heading, { level: 5 }, "Sort by"),
748
- React__default["default"].createElement(Chip.Chips, { selected: (state === null || state === void 0 ? void 0 : state.key) || "none", onChange: handleKeyChange }, sortByOptions.map(({ label, value }) => (React__default["default"].createElement(Chip.Chip, { key: label, label: label, value: value })))),
749
- React__default["default"].createElement(Heading.Heading, { level: 5 }, "Ordered by"),
750
- React__default["default"].createElement(Chip.Chips, { selected: state === null || state === void 0 ? void 0 : state.order, onChange: handleSortingChange },
751
- React__default["default"].createElement(Chip.Chip, { label: "Ascending", value: "asc", disabled: canChangeOrder }),
752
- React__default["default"].createElement(Chip.Chip, { label: "Descending", value: "desc", disabled: canChangeOrder }))))));
739
+ console.log({ state });
740
+ return (React__default["default"].createElement(Combobox.Combobox, { onSelect: selection => {
741
+ handleKeyChange(selection[0].id.toString());
742
+ }, selected: [
743
+ {
744
+ id: `${state === null || state === void 0 ? void 0 : state.key},${state === null || state === void 0 ? void 0 : state.order}`,
745
+ label: (state === null || state === void 0 ? void 0 : state.order) || "",
746
+ },
747
+ ] },
748
+ React__default["default"].createElement(Combobox.Combobox.Activator, null,
749
+ React__default["default"].createElement(Chip.Chip, { heading: "Sort", label: getButtonLabel(), variation: state ? "base" : "subtle" }, !state && (React__default["default"].createElement(Chip.Chip.Suffix, null,
750
+ React__default["default"].createElement(Icon.Icon, { name: "arrowDown", size: "small" }))))),
751
+ sortByOptions.map(({ label, value }) => {
752
+ return React__default["default"].createElement(Combobox.Combobox.Option, { key: value, id: value, label: label });
753
+ })));
753
754
  function getSortByOptions() {
754
755
  const options = sortable.reduce((acc, sort) => {
755
756
  const label = headers[sort];
756
757
  if (!label)
757
758
  return acc;
758
- acc.push({ label, value: sort.toString() });
759
+ if (label === "Last activity") {
760
+ acc.push({ label: "Last Activity (Newest)", value: `${sort},desc` });
761
+ acc.push({ label: "Last Activity (Oldest)", value: `${sort},asc` });
762
+ }
763
+ else {
764
+ acc.push({ label: `${label} (A-Z)`, value: `${sort},asc` });
765
+ acc.push({ label: `${label} (Z-A)`, value: `${sort},desc` });
766
+ }
759
767
  return acc;
760
768
  }, []);
761
769
  // Inject a none option as the first option
@@ -763,24 +771,17 @@ function DataListSort() {
763
771
  return options;
764
772
  }
765
773
  function getButtonLabel() {
766
- const label = state && headers[state.key];
767
- if (!label)
768
- return "Sort by";
769
- return `Sort by: ${label}, ${state.order}`;
774
+ const selectedOption = sortByOptions.find(option => option.value === `${state === null || state === void 0 ? void 0 : state.key},${state === null || state === void 0 ? void 0 : state.order}`);
775
+ return (selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.label) || "";
770
776
  }
771
777
  function handleKeyChange(value) {
772
778
  if (value && value !== "none") {
773
- onSort({ key: value, order: (state === null || state === void 0 ? void 0 : state.order) || "asc" });
779
+ const [key, order] = value.split(",");
780
+ onSort({ key, order: order });
774
781
  return;
775
782
  }
776
783
  onSort(undefined);
777
784
  }
778
- function handleSortingChange(value) {
779
- if ((state === null || state === void 0 ? void 0 : state.key) && value) {
780
- onSort({ key: state.key, order: value });
781
- return;
782
- }
783
- }
784
785
  }
785
786
 
786
787
  function useShowHeader() {
@@ -2,12 +2,11 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var Popover = require('../Popover-7c966790.js');
6
- require('react');
7
- require('react-popper');
8
- require('@jobber/hooks/useRefocusOnActivator');
9
- require('../style-inject.es-9d2f5f4e.js');
10
- require('../ButtonDismiss-1ccb84ec.js');
5
+ var React = require('react');
6
+ var reactPopper = require('react-popper');
7
+ var useRefocusOnActivator = require('@jobber/hooks/useRefocusOnActivator');
8
+ var styleInject_es = require('../style-inject.es-9d2f5f4e.js');
9
+ var ButtonDismiss = require('../ButtonDismiss-1ccb84ec.js');
11
10
  require('../Button-db6114a6.js');
12
11
  require('classnames');
13
12
  require('react-router-dom');
@@ -15,6 +14,48 @@ require('../Icon-405a216c.js');
15
14
  require('@jobber/design');
16
15
  require('../Typography-83915c54.js');
17
16
 
17
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
18
18
 
19
+ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
19
20
 
20
- exports.Popover = Popover.Popover;
21
+ var css_248z = ":root {\n --card--base-padding: var(--space-base);\n --popover--padding: var(--space-base);\n --popover--width: calc(var(--base-unit) * 21.875);\n --dismiss--button-padding: calc(var(--base-unit) / 4);\n}\n\n.oSlK4rLUvzE- {\n display: -ms-flexbox;\n display: flex;\n position: \"relative\";\n padding: calc(16px * 1);\n padding: var(--popover--padding);\n -ms-flex-pack: justify;\n justify-content: space-between;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.XVPpPb-7e30- {\n float: right;\n padding: calc(16px / 4);\n padding: var(--dismiss--button-padding);\n text-align: right;\n}\n\n.TDuYbqXaLW0- {\n z-index: 1002;\n z-index: var(--elevation-tooltip);\n width: -webkit-max-content;\n width: max-content;\n max-width: calc(16px * 21.875);\n max-width: var(--popover--width);\n box-shadow: 0px calc(16px / 16) calc(16px / 4) 0px\n rgba(0, 0, 0, 0.1),\n 0px calc(16px / 4) 12px 0px rgba(0, 0, 0, 0.05);\n box-shadow: var(--shadow-base);\n border: calc(16px / 16) solid rgb(225, 225, 225);\n border: var(--border-base) solid var(--color-border);\n border-radius: calc(16px / 8);\n border-radius: var(--radius-base);\n font-size: calc((16px * 1) * 0.875);\n font-size: calc(calc(16px * 1) * 0.875);\n font-size: var(--typography--fontSize-base);\n line-height: normal;\n background: rgba(255, 255, 255, 1);\n background: var(--color-surface);\n}\n\n.TDuYbqXaLW0- > * {\n --public-content--padding: var(--card--base-padding);\n}\n\n.LJCpn0zf1e8- {\n visibility: hidden;\n}\n\n.LJCpn0zf1e8-,\n.LJCpn0zf1e8-::before {\n position: absolute;\n width: 16px;\n width: var(--base-unit);\n height: 16px;\n height: var(--base-unit);\n background: inherit;\n}\n\n.LJCpn0zf1e8-::before {\n content: \"\";\n visibility: visible;\n border: calc(16px / 16) solid rgb(225, 225, 225);\n border: var(--border-base) solid var(--color-border);\n -webkit-transform: rotate(45deg);\n transform: rotate(45deg);\n -webkit-transform-origin: center;\n transform-origin: center;\n -webkit-clip-path: polygon(-4px -4px, 15.3px 0, 0 15.3px);\n clip-path: polygon(-4px -4px, 15.3px 0, 0 15.3px);\n}\n\n.TDuYbqXaLW0-[data-popper-placement^=\"top\"] > .LJCpn0zf1e8- {\n bottom: -5px;\n}\n\n.TDuYbqXaLW0-[data-popper-placement^=\"top\"] > .LJCpn0zf1e8-::before {\n -webkit-transform: rotate(-135deg);\n transform: rotate(-135deg);\n}\n\n.TDuYbqXaLW0-[data-popper-placement^=\"bottom\"] > .LJCpn0zf1e8- {\n top: -7px;\n}\n\n.TDuYbqXaLW0-[data-popper-placement^=\"left\"] > .LJCpn0zf1e8- {\n right: -5px;\n}\n\n.TDuYbqXaLW0-[data-popper-placement^=\"left\"] > .LJCpn0zf1e8-::before {\n -webkit-transform: rotate(135deg);\n transform: rotate(135deg);\n}\n\n.TDuYbqXaLW0-[data-popper-placement^=\"right\"] > .LJCpn0zf1e8- {\n left: -7px;\n}\n\n.TDuYbqXaLW0-[data-popper-placement^=\"right\"] > .LJCpn0zf1e8-::before {\n -webkit-transform: rotate(-45deg);\n transform: rotate(-45deg);\n}\n";
22
+ var classes = {"header":"oSlK4rLUvzE-","dismissButton":"XVPpPb-7e30-","popover":"TDuYbqXaLW0-","arrow":"LJCpn0zf1e8-"};
23
+ styleInject_es.styleInject(css_248z);
24
+
25
+ function Popover({ onRequestClose, children, attachTo, open, preferredPlacement = "auto", }) {
26
+ const [popperElement, setPopperElement] = React.useState();
27
+ const [arrowElement, setArrowElement] = React.useState();
28
+ const { styles: popperStyles, attributes } = reactPopper.usePopper(attachTo instanceof Element ? attachTo : attachTo.current, popperElement, {
29
+ modifiers: buildModifiers(arrowElement),
30
+ placement: preferredPlacement,
31
+ });
32
+ useRefocusOnActivator.useRefocusOnActivator(open);
33
+ return (React__default["default"].createElement(React__default["default"].Fragment, null, open && (React__default["default"].createElement("div", Object.assign({ role: "dialog", ref: setPopperElement, style: popperStyles.popper, className: classes.popover }, attributes.popper),
34
+ React__default["default"].createElement("div", { className: classes.dismissButton },
35
+ React__default["default"].createElement(ButtonDismiss.ButtonDismiss, { onClick: onRequestClose, ariaLabel: "Close dialog" })),
36
+ children,
37
+ React__default["default"].createElement("div", { ref: setArrowElement, className: classes.arrow, style: popperStyles.arrow })))));
38
+ }
39
+ function buildModifiers(arrowElement) {
40
+ const modifiers = [
41
+ {
42
+ name: "arrow",
43
+ options: { element: arrowElement, padding: 10 },
44
+ },
45
+ {
46
+ name: "offset",
47
+ options: {
48
+ offset: [0, 10],
49
+ },
50
+ },
51
+ {
52
+ name: "flip",
53
+ options: {
54
+ fallbackPlacements: ["auto"],
55
+ },
56
+ },
57
+ ];
58
+ return modifiers;
59
+ }
60
+
61
+ exports.Popover = Popover;