@khanacademy/wonder-blocks-dropdown 2.3.19
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/LICENSE +21 -0
- package/dist/es/index.js +3403 -0
- package/dist/index.js +3966 -0
- package/dist/index.js.flow +2 -0
- package/docs.md +12 -0
- package/package.json +44 -0
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +4054 -0
- package/src/__tests__/generated-snapshot.test.js +1612 -0
- package/src/__tests__/index.test.js +23 -0
- package/src/components/__mocks__/dropdown-core-virtualized.js +40 -0
- package/src/components/__tests__/__snapshots__/action-item.test.js.snap +63 -0
- package/src/components/__tests__/action-item.test.js +43 -0
- package/src/components/__tests__/action-menu.test.js +544 -0
- package/src/components/__tests__/dropdown-core-virtualized.test.js +119 -0
- package/src/components/__tests__/dropdown-core.test.js +659 -0
- package/src/components/__tests__/multi-select.test.js +982 -0
- package/src/components/__tests__/search-text-input.test.js +144 -0
- package/src/components/__tests__/single-select.test.js +588 -0
- package/src/components/action-item.js +270 -0
- package/src/components/action-menu-opener-core.js +203 -0
- package/src/components/action-menu.js +300 -0
- package/src/components/action-menu.md +338 -0
- package/src/components/check.js +59 -0
- package/src/components/checkbox.js +111 -0
- package/src/components/dropdown-core-virtualized-item.js +62 -0
- package/src/components/dropdown-core-virtualized.js +246 -0
- package/src/components/dropdown-core.js +770 -0
- package/src/components/dropdown-opener.js +101 -0
- package/src/components/multi-select.js +597 -0
- package/src/components/multi-select.md +718 -0
- package/src/components/multi-select.stories.js +111 -0
- package/src/components/option-item.js +239 -0
- package/src/components/search-text-input.js +227 -0
- package/src/components/select-opener.js +297 -0
- package/src/components/separator-item.js +50 -0
- package/src/components/single-select.js +418 -0
- package/src/components/single-select.md +520 -0
- package/src/components/single-select.stories.js +107 -0
- package/src/index.js +20 -0
- package/src/util/constants.js +50 -0
- package/src/util/types.js +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Khan Academy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/es/index.js
ADDED
|
@@ -0,0 +1,3403 @@
|
|
|
1
|
+
import { createElement, Fragment, Component, cloneElement, Children, createRef } from 'react';
|
|
2
|
+
import { StyleSheet, css } from 'aphrodite';
|
|
3
|
+
import { Link } from 'react-router-dom';
|
|
4
|
+
import { any } from 'prop-types';
|
|
5
|
+
import Color, { mix, fade, SemanticColor } from '@khanacademy/wonder-blocks-color';
|
|
6
|
+
import Spacing from '@khanacademy/wonder-blocks-spacing';
|
|
7
|
+
import { LabelMedium, styles as styles$a, LabelLarge } from '@khanacademy/wonder-blocks-typography';
|
|
8
|
+
import { getClickableBehavior, isClientSideUrl, ClickableBehavior } from '@khanacademy/wonder-blocks-clickable';
|
|
9
|
+
import { addStyle, View, getElementIntersection } from '@khanacademy/wonder-blocks-core';
|
|
10
|
+
import Icon, { icons } from '@khanacademy/wonder-blocks-icon';
|
|
11
|
+
import ReactDOM from 'react-dom';
|
|
12
|
+
import { Popper } from 'react-popper';
|
|
13
|
+
import { VariableSizeList } from 'react-window';
|
|
14
|
+
import { maybeGetPortalMountedModalHostElement } from '@khanacademy/wonder-blocks-modal';
|
|
15
|
+
import { withActionScheduler } from '@khanacademy/wonder-blocks-timing';
|
|
16
|
+
import PopperJS from 'popper.js';
|
|
17
|
+
import IconButton from '@khanacademy/wonder-blocks-icon-button';
|
|
18
|
+
import { Strut } from '@khanacademy/wonder-blocks-layout';
|
|
19
|
+
|
|
20
|
+
function _classCallCheck(instance, Constructor) {
|
|
21
|
+
if (!(instance instanceof Constructor)) {
|
|
22
|
+
throw new TypeError("Cannot call a class as a function");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function _defineProperties(target, props) {
|
|
27
|
+
for (var i = 0; i < props.length; i++) {
|
|
28
|
+
var descriptor = props[i];
|
|
29
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
30
|
+
descriptor.configurable = true;
|
|
31
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
32
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function _createClass(Constructor, protoProps, staticProps) {
|
|
37
|
+
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
38
|
+
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
39
|
+
return Constructor;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function _defineProperty(obj, key, value) {
|
|
43
|
+
if (key in obj) {
|
|
44
|
+
Object.defineProperty(obj, key, {
|
|
45
|
+
value: value,
|
|
46
|
+
enumerable: true,
|
|
47
|
+
configurable: true,
|
|
48
|
+
writable: true
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
obj[key] = value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return obj;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function _extends() {
|
|
58
|
+
_extends = Object.assign || function (target) {
|
|
59
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
60
|
+
var source = arguments[i];
|
|
61
|
+
|
|
62
|
+
for (var key in source) {
|
|
63
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
64
|
+
target[key] = source[key];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return target;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return _extends.apply(this, arguments);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function ownKeys(object, enumerableOnly) {
|
|
76
|
+
var keys = Object.keys(object);
|
|
77
|
+
|
|
78
|
+
if (Object.getOwnPropertySymbols) {
|
|
79
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
80
|
+
if (enumerableOnly) symbols = symbols.filter(function (sym) {
|
|
81
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
82
|
+
});
|
|
83
|
+
keys.push.apply(keys, symbols);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return keys;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function _objectSpread2(target) {
|
|
90
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
91
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
92
|
+
|
|
93
|
+
if (i % 2) {
|
|
94
|
+
ownKeys(Object(source), true).forEach(function (key) {
|
|
95
|
+
_defineProperty(target, key, source[key]);
|
|
96
|
+
});
|
|
97
|
+
} else if (Object.getOwnPropertyDescriptors) {
|
|
98
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
99
|
+
} else {
|
|
100
|
+
ownKeys(Object(source)).forEach(function (key) {
|
|
101
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return target;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function _inherits(subClass, superClass) {
|
|
110
|
+
if (typeof superClass !== "function" && superClass !== null) {
|
|
111
|
+
throw new TypeError("Super expression must either be null or a function");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
subClass.prototype = Object.create(superClass && superClass.prototype, {
|
|
115
|
+
constructor: {
|
|
116
|
+
value: subClass,
|
|
117
|
+
writable: true,
|
|
118
|
+
configurable: true
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
if (superClass) _setPrototypeOf(subClass, superClass);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function _getPrototypeOf(o) {
|
|
125
|
+
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
|
|
126
|
+
return o.__proto__ || Object.getPrototypeOf(o);
|
|
127
|
+
};
|
|
128
|
+
return _getPrototypeOf(o);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function _setPrototypeOf(o, p) {
|
|
132
|
+
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
|
|
133
|
+
o.__proto__ = p;
|
|
134
|
+
return o;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return _setPrototypeOf(o, p);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function _isNativeReflectConstruct() {
|
|
141
|
+
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
|
|
142
|
+
if (Reflect.construct.sham) return false;
|
|
143
|
+
if (typeof Proxy === "function") return true;
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
|
|
147
|
+
return true;
|
|
148
|
+
} catch (e) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function _objectWithoutPropertiesLoose(source, excluded) {
|
|
154
|
+
if (source == null) return {};
|
|
155
|
+
var target = {};
|
|
156
|
+
var sourceKeys = Object.keys(source);
|
|
157
|
+
var key, i;
|
|
158
|
+
|
|
159
|
+
for (i = 0; i < sourceKeys.length; i++) {
|
|
160
|
+
key = sourceKeys[i];
|
|
161
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
162
|
+
target[key] = source[key];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return target;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function _objectWithoutProperties(source, excluded) {
|
|
169
|
+
if (source == null) return {};
|
|
170
|
+
|
|
171
|
+
var target = _objectWithoutPropertiesLoose(source, excluded);
|
|
172
|
+
|
|
173
|
+
var key, i;
|
|
174
|
+
|
|
175
|
+
if (Object.getOwnPropertySymbols) {
|
|
176
|
+
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
|
|
177
|
+
|
|
178
|
+
for (i = 0; i < sourceSymbolKeys.length; i++) {
|
|
179
|
+
key = sourceSymbolKeys[i];
|
|
180
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
181
|
+
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
|
|
182
|
+
target[key] = source[key];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return target;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function _assertThisInitialized(self) {
|
|
190
|
+
if (self === void 0) {
|
|
191
|
+
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return self;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function _possibleConstructorReturn(self, call) {
|
|
198
|
+
if (call && (typeof call === "object" || typeof call === "function")) {
|
|
199
|
+
return call;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return _assertThisInitialized(self);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function _createSuper(Derived) {
|
|
206
|
+
var hasNativeReflectConstruct = _isNativeReflectConstruct();
|
|
207
|
+
|
|
208
|
+
return function _createSuperInternal() {
|
|
209
|
+
var Super = _getPrototypeOf(Derived),
|
|
210
|
+
result;
|
|
211
|
+
|
|
212
|
+
if (hasNativeReflectConstruct) {
|
|
213
|
+
var NewTarget = _getPrototypeOf(this).constructor;
|
|
214
|
+
|
|
215
|
+
result = Reflect.construct(Super, arguments, NewTarget);
|
|
216
|
+
} else {
|
|
217
|
+
result = Super.apply(this, arguments);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return _possibleConstructorReturn(this, result);
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function _toConsumableArray(arr) {
|
|
225
|
+
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function _arrayWithoutHoles(arr) {
|
|
229
|
+
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function _iterableToArray(iter) {
|
|
233
|
+
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function _unsupportedIterableToArray(o, minLen) {
|
|
237
|
+
if (!o) return;
|
|
238
|
+
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
239
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
240
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
241
|
+
if (n === "Map" || n === "Set") return Array.from(o);
|
|
242
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function _arrayLikeToArray(arr, len) {
|
|
246
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
247
|
+
|
|
248
|
+
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
|
249
|
+
|
|
250
|
+
return arr2;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function _nonIterableSpread() {
|
|
254
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function _createForOfIteratorHelper(o, allowArrayLike) {
|
|
258
|
+
var it;
|
|
259
|
+
|
|
260
|
+
if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
|
|
261
|
+
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
|
|
262
|
+
if (it) o = it;
|
|
263
|
+
var i = 0;
|
|
264
|
+
|
|
265
|
+
var F = function () {};
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
s: F,
|
|
269
|
+
n: function () {
|
|
270
|
+
if (i >= o.length) return {
|
|
271
|
+
done: true
|
|
272
|
+
};
|
|
273
|
+
return {
|
|
274
|
+
done: false,
|
|
275
|
+
value: o[i++]
|
|
276
|
+
};
|
|
277
|
+
},
|
|
278
|
+
e: function (e) {
|
|
279
|
+
throw e;
|
|
280
|
+
},
|
|
281
|
+
f: F
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
var normalCompletion = true,
|
|
289
|
+
didErr = false,
|
|
290
|
+
err;
|
|
291
|
+
return {
|
|
292
|
+
s: function () {
|
|
293
|
+
it = o[Symbol.iterator]();
|
|
294
|
+
},
|
|
295
|
+
n: function () {
|
|
296
|
+
var step = it.next();
|
|
297
|
+
normalCompletion = step.done;
|
|
298
|
+
return step;
|
|
299
|
+
},
|
|
300
|
+
e: function (e) {
|
|
301
|
+
didErr = true;
|
|
302
|
+
err = e;
|
|
303
|
+
},
|
|
304
|
+
f: function () {
|
|
305
|
+
try {
|
|
306
|
+
if (!normalCompletion && it.return != null) it.return();
|
|
307
|
+
} finally {
|
|
308
|
+
if (didErr) throw err;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
var keyCodes = {
|
|
315
|
+
tab: 9,
|
|
316
|
+
enter: 13,
|
|
317
|
+
escape: 27,
|
|
318
|
+
space: 32,
|
|
319
|
+
up: 38,
|
|
320
|
+
down: 40
|
|
321
|
+
};
|
|
322
|
+
var selectDropdownStyle = {
|
|
323
|
+
marginTop: Spacing.xSmall_8,
|
|
324
|
+
marginBottom: Spacing.xSmall_8
|
|
325
|
+
}; // Filterable dropdown has minimum dimensions requested from Design.
|
|
326
|
+
// Note that these can be overridden by the provided style if needed.
|
|
327
|
+
|
|
328
|
+
var filterableDropdownStyle = {
|
|
329
|
+
minHeight: 100,
|
|
330
|
+
maxHeight: 384
|
|
331
|
+
};
|
|
332
|
+
var searchInputStyle = {
|
|
333
|
+
margin: Spacing.xSmall_8,
|
|
334
|
+
marginTop: Spacing.xxxSmall_4
|
|
335
|
+
}; // The default item height
|
|
336
|
+
|
|
337
|
+
var DROPDOWN_ITEM_HEIGHT = 40;
|
|
338
|
+
var SEPARATOR_ITEM_HEIGHT = 9;
|
|
339
|
+
var SEARCH_ITEM_HEIGHT = DROPDOWN_ITEM_HEIGHT + searchInputStyle.margin + searchInputStyle.marginTop; // The default labels that will be used by different components
|
|
340
|
+
|
|
341
|
+
var defaultLabels = {
|
|
342
|
+
clearSearch: "Clear search",
|
|
343
|
+
filter: "Filter",
|
|
344
|
+
noResults: "No results",
|
|
345
|
+
selectNoneLabel: "Select none",
|
|
346
|
+
selectAllLabel: function selectAllLabel(numOptions) {
|
|
347
|
+
return "Select all (".concat(numOptions, ")");
|
|
348
|
+
},
|
|
349
|
+
noneSelected: "0 items",
|
|
350
|
+
someSelected: function someSelected(numSelectedValues) {
|
|
351
|
+
return "".concat(numSelectedValues, " items");
|
|
352
|
+
},
|
|
353
|
+
allSelected: "All items"
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
var blue = Color.blue,
|
|
357
|
+
white = Color.white,
|
|
358
|
+
offBlack = Color.offBlack,
|
|
359
|
+
offBlack32 = Color.offBlack32;
|
|
360
|
+
var StyledAnchor = addStyle("a");
|
|
361
|
+
var StyledButton = addStyle("button");
|
|
362
|
+
var StyledLink = addStyle(Link);
|
|
363
|
+
/**
|
|
364
|
+
* The action item trigger actions, such as navigating to a different page or
|
|
365
|
+
* opening a modal. Supply the href and/or onClick props. Used as a child of
|
|
366
|
+
* ActionMenu.
|
|
367
|
+
*/
|
|
368
|
+
|
|
369
|
+
var ActionItem = /*#__PURE__*/function (_React$Component) {
|
|
370
|
+
_inherits(ActionItem, _React$Component);
|
|
371
|
+
|
|
372
|
+
var _super = _createSuper(ActionItem);
|
|
373
|
+
|
|
374
|
+
function ActionItem() {
|
|
375
|
+
_classCallCheck(this, ActionItem);
|
|
376
|
+
|
|
377
|
+
return _super.apply(this, arguments);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
_createClass(ActionItem, [{
|
|
381
|
+
key: "render",
|
|
382
|
+
value: function render() {
|
|
383
|
+
var _this$props = this.props,
|
|
384
|
+
skipClientNav = _this$props.skipClientNav,
|
|
385
|
+
disabled = _this$props.disabled,
|
|
386
|
+
href = _this$props.href,
|
|
387
|
+
target = _this$props.target,
|
|
388
|
+
indent = _this$props.indent,
|
|
389
|
+
label = _this$props.label,
|
|
390
|
+
onClick = _this$props.onClick,
|
|
391
|
+
role = _this$props.role,
|
|
392
|
+
style = _this$props.style,
|
|
393
|
+
testId = _this$props.testId;
|
|
394
|
+
var router = this.context.router;
|
|
395
|
+
var ClickableBehavior = getClickableBehavior(href, skipClientNav, router);
|
|
396
|
+
return /*#__PURE__*/createElement(ClickableBehavior, {
|
|
397
|
+
disabled: disabled,
|
|
398
|
+
onClick: onClick,
|
|
399
|
+
href: href,
|
|
400
|
+
role: role,
|
|
401
|
+
target: target
|
|
402
|
+
}, function (state, childrenProps) {
|
|
403
|
+
var pressed = state.pressed,
|
|
404
|
+
hovered = state.hovered,
|
|
405
|
+
focused = state.focused;
|
|
406
|
+
var defaultStyle = [styles.shared, disabled && styles.disabled, !disabled && (pressed ? styles.active : (hovered || focused) && styles.focus), // pass optional styles from react-window (if applies)
|
|
407
|
+
style];
|
|
408
|
+
|
|
409
|
+
var props = _objectSpread2({
|
|
410
|
+
"data-test-id": testId,
|
|
411
|
+
disabled: disabled,
|
|
412
|
+
role: role,
|
|
413
|
+
style: [defaultStyle]
|
|
414
|
+
}, childrenProps);
|
|
415
|
+
|
|
416
|
+
var children = /*#__PURE__*/createElement(Fragment, null, /*#__PURE__*/createElement(LabelMedium, {
|
|
417
|
+
style: [indent && styles.indent, styles.label]
|
|
418
|
+
}, label));
|
|
419
|
+
|
|
420
|
+
if (href && !disabled) {
|
|
421
|
+
return router && !skipClientNav && isClientSideUrl(href) ? /*#__PURE__*/createElement(StyledLink, _extends({}, props, {
|
|
422
|
+
to: href
|
|
423
|
+
}), children) : /*#__PURE__*/createElement(StyledAnchor, _extends({}, props, {
|
|
424
|
+
href: href,
|
|
425
|
+
target: target
|
|
426
|
+
}), children);
|
|
427
|
+
} else {
|
|
428
|
+
return /*#__PURE__*/createElement(StyledButton, _extends({
|
|
429
|
+
type: "button"
|
|
430
|
+
}, props, {
|
|
431
|
+
disabled: disabled
|
|
432
|
+
}), children);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}], [{
|
|
437
|
+
key: "isClassOf",
|
|
438
|
+
value: function isClassOf(instance) {
|
|
439
|
+
return instance && instance.type && instance.type.__IS_ACTION_ITEM__;
|
|
440
|
+
}
|
|
441
|
+
}]);
|
|
442
|
+
|
|
443
|
+
return ActionItem;
|
|
444
|
+
}(Component);
|
|
445
|
+
|
|
446
|
+
_defineProperty(ActionItem, "contextTypes", {
|
|
447
|
+
router: any
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
_defineProperty(ActionItem, "defaultProps", {
|
|
451
|
+
disabled: false,
|
|
452
|
+
indent: false,
|
|
453
|
+
role: "menuitem"
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
_defineProperty(ActionItem, "__IS_ACTION_ITEM__", true);
|
|
457
|
+
var styles = StyleSheet.create({
|
|
458
|
+
shared: {
|
|
459
|
+
background: white,
|
|
460
|
+
color: offBlack,
|
|
461
|
+
textDecoration: "none",
|
|
462
|
+
border: "none",
|
|
463
|
+
outline: "none",
|
|
464
|
+
flexDirection: "row",
|
|
465
|
+
alignItems: "center",
|
|
466
|
+
display: "flex",
|
|
467
|
+
height: DROPDOWN_ITEM_HEIGHT,
|
|
468
|
+
minHeight: DROPDOWN_ITEM_HEIGHT,
|
|
469
|
+
paddingLeft: Spacing.medium_16,
|
|
470
|
+
paddingRight: Spacing.medium_16,
|
|
471
|
+
// This removes the 300ms click delay on mobile browsers by indicating that
|
|
472
|
+
// "double-tap to zoom" shouldn't be used on this element.
|
|
473
|
+
touchAction: "manipulation"
|
|
474
|
+
},
|
|
475
|
+
label: {
|
|
476
|
+
whiteSpace: "nowrap",
|
|
477
|
+
userSelect: "none"
|
|
478
|
+
},
|
|
479
|
+
indent: {
|
|
480
|
+
marginLeft: Spacing.medium_16
|
|
481
|
+
},
|
|
482
|
+
// hover and focus states
|
|
483
|
+
focus: {
|
|
484
|
+
color: white,
|
|
485
|
+
background: blue
|
|
486
|
+
},
|
|
487
|
+
// active and pressed states
|
|
488
|
+
active: {
|
|
489
|
+
color: mix(fade(blue, 0.32), white),
|
|
490
|
+
background: mix(offBlack32, blue)
|
|
491
|
+
},
|
|
492
|
+
// disabled state
|
|
493
|
+
disabled: {
|
|
494
|
+
color: offBlack32,
|
|
495
|
+
cursor: "default"
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
var offBlack$1 = Color.offBlack,
|
|
500
|
+
offBlack32$1 = Color.offBlack32,
|
|
501
|
+
white$1 = Color.white;
|
|
502
|
+
/**
|
|
503
|
+
* Props describing the state of the OptionItem, shared by the checkbox
|
|
504
|
+
* component,
|
|
505
|
+
*/
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* The check component used by OptionItem.
|
|
509
|
+
*/
|
|
510
|
+
function Check(props) {
|
|
511
|
+
var disabled = props.disabled,
|
|
512
|
+
selected = props.selected,
|
|
513
|
+
pressed = props.pressed,
|
|
514
|
+
hovered = props.hovered,
|
|
515
|
+
focused = props.focused;
|
|
516
|
+
return /*#__PURE__*/createElement(Icon, {
|
|
517
|
+
icon: icons.check,
|
|
518
|
+
size: "small",
|
|
519
|
+
color: disabled ? offBlack32$1 : pressed || hovered || focused ? white$1 : offBlack$1,
|
|
520
|
+
style: [styles$1.bounds, !selected && styles$1.hide]
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
var styles$1 = StyleSheet.create({
|
|
524
|
+
bounds: {
|
|
525
|
+
// Semantically, this are the constants for a small-sized icon
|
|
526
|
+
minHeight: 16,
|
|
527
|
+
minWidth: 16
|
|
528
|
+
},
|
|
529
|
+
hide: {
|
|
530
|
+
visibility: "hidden"
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// NOTE(sophie): This is a smaller check specifically for use in checkboxes.
|
|
535
|
+
// Please don't copy it automatically and check with designers before using.
|
|
536
|
+
// If the intended icon is a check without a checkbox, you should be using
|
|
537
|
+
// icons.check from the Wonder Blocks Icon package.
|
|
538
|
+
var checkboxCheck = {
|
|
539
|
+
small: "M11.263 4.324a1 1 0 1 1 1.474 1.352l-5.5 6a1 1 0 0 1-1.505-.036l-2.5-3a1 1 0 1 1 1.536-1.28L6.536 9.48l4.727-5.157z"
|
|
540
|
+
};
|
|
541
|
+
var blue$1 = Color.blue,
|
|
542
|
+
white$2 = Color.white,
|
|
543
|
+
offBlack16 = Color.offBlack16,
|
|
544
|
+
offBlack32$2 = Color.offBlack32,
|
|
545
|
+
offBlack50 = Color.offBlack50,
|
|
546
|
+
offWhite = Color.offWhite;
|
|
547
|
+
/**
|
|
548
|
+
* Props describing the state of the OptionItem, shared by the check
|
|
549
|
+
* component,
|
|
550
|
+
*/
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* The checkbox component used by OptionItem.
|
|
554
|
+
*/
|
|
555
|
+
function Checkbox(props) {
|
|
556
|
+
var disabled = props.disabled,
|
|
557
|
+
selected = props.selected,
|
|
558
|
+
pressed = props.pressed,
|
|
559
|
+
hovered = props.hovered,
|
|
560
|
+
focused = props.focused;
|
|
561
|
+
var activeBlue = mix(offBlack32$2, blue$1);
|
|
562
|
+
var clickInteraction = pressed || hovered || focused;
|
|
563
|
+
var bgColor = disabled ? offWhite : selected && !clickInteraction ? blue$1 : white$2;
|
|
564
|
+
var checkColor = disabled ? offBlack32$2 : clickInteraction ? pressed ? activeBlue : blue$1 : white$2;
|
|
565
|
+
return /*#__PURE__*/createElement(View, {
|
|
566
|
+
style: [styles$2.checkbox, (clickInteraction || selected && !disabled) && styles$2.noBorder, disabled && styles$2.disabledCheckbox, {
|
|
567
|
+
backgroundColor: bgColor
|
|
568
|
+
}]
|
|
569
|
+
}, selected && /*#__PURE__*/createElement(Icon, {
|
|
570
|
+
icon: checkboxCheck,
|
|
571
|
+
size: "small",
|
|
572
|
+
color: checkColor,
|
|
573
|
+
style: [disabled && selected && styles$2.disabledCheckFormatting]
|
|
574
|
+
}));
|
|
575
|
+
}
|
|
576
|
+
var styles$2 = StyleSheet.create({
|
|
577
|
+
checkbox: {
|
|
578
|
+
// Semantically, this are the constants for a small-sized icon
|
|
579
|
+
minHeight: 16,
|
|
580
|
+
minWidth: 16,
|
|
581
|
+
borderRadius: 3,
|
|
582
|
+
borderWidth: 1,
|
|
583
|
+
borderStyle: "solid",
|
|
584
|
+
borderColor: offBlack50
|
|
585
|
+
},
|
|
586
|
+
noBorder: {
|
|
587
|
+
borderWidth: 0
|
|
588
|
+
},
|
|
589
|
+
disabledCheckbox: {
|
|
590
|
+
borderColor: offBlack16,
|
|
591
|
+
backgroundColor: offWhite
|
|
592
|
+
},
|
|
593
|
+
// The border of 1px on the selected, disabled checkbox pushes the check out
|
|
594
|
+
// of place. Move it back.
|
|
595
|
+
disabledCheckFormatting: {
|
|
596
|
+
position: "absolute",
|
|
597
|
+
top: -1,
|
|
598
|
+
left: -1
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* For option items that can be selected in a dropdown, selection denoted either
|
|
604
|
+
* with a check ✔️ or a checkbox ☑️. Use as children in SingleSelect or
|
|
605
|
+
* MultiSelect.
|
|
606
|
+
*/
|
|
607
|
+
var OptionItem = /*#__PURE__*/function (_React$Component) {
|
|
608
|
+
_inherits(OptionItem, _React$Component);
|
|
609
|
+
|
|
610
|
+
var _super = _createSuper(OptionItem);
|
|
611
|
+
|
|
612
|
+
function OptionItem() {
|
|
613
|
+
var _this;
|
|
614
|
+
|
|
615
|
+
_classCallCheck(this, OptionItem);
|
|
616
|
+
|
|
617
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
618
|
+
args[_key] = arguments[_key];
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
_this = _super.call.apply(_super, [this].concat(args));
|
|
622
|
+
|
|
623
|
+
_defineProperty(_assertThisInitialized(_this), "handleClick", function () {
|
|
624
|
+
var _this$props = _this.props,
|
|
625
|
+
onClick = _this$props.onClick,
|
|
626
|
+
onToggle = _this$props.onToggle,
|
|
627
|
+
value = _this$props.value;
|
|
628
|
+
onToggle(value);
|
|
629
|
+
|
|
630
|
+
if (onClick) {
|
|
631
|
+
onClick();
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
return _this;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
_createClass(OptionItem, [{
|
|
639
|
+
key: "getCheckComponent",
|
|
640
|
+
value: function getCheckComponent() {
|
|
641
|
+
if (this.props.variant === "check") {
|
|
642
|
+
return Check;
|
|
643
|
+
} else {
|
|
644
|
+
return Checkbox;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}, {
|
|
648
|
+
key: "render",
|
|
649
|
+
value: function render() {
|
|
650
|
+
var _this$props2 = this.props,
|
|
651
|
+
disabled = _this$props2.disabled,
|
|
652
|
+
label = _this$props2.label,
|
|
653
|
+
role = _this$props2.role,
|
|
654
|
+
selected = _this$props2.selected,
|
|
655
|
+
testId = _this$props2.testId,
|
|
656
|
+
style = _this$props2.style,
|
|
657
|
+
value = _this$props2.value,
|
|
658
|
+
onClick = _this$props2.onClick,
|
|
659
|
+
onToggle = _this$props2.onToggle,
|
|
660
|
+
variant = _this$props2.variant,
|
|
661
|
+
sharedProps = _objectWithoutProperties(_this$props2, ["disabled", "label", "role", "selected", "testId", "style", "value", "onClick", "onToggle", "variant"]);
|
|
662
|
+
|
|
663
|
+
var ClickableBehavior = getClickableBehavior();
|
|
664
|
+
var CheckComponent = this.getCheckComponent();
|
|
665
|
+
return /*#__PURE__*/createElement(ClickableBehavior, {
|
|
666
|
+
disabled: disabled,
|
|
667
|
+
onClick: this.handleClick,
|
|
668
|
+
role: role
|
|
669
|
+
}, function (state, childrenProps) {
|
|
670
|
+
var pressed = state.pressed,
|
|
671
|
+
hovered = state.hovered,
|
|
672
|
+
focused = state.focused;
|
|
673
|
+
var defaultStyle = [styles$3.itemContainer, pressed ? styles$3.active : (hovered || focused) && styles$3.focus, disabled && styles$3.disabled, // pass optional styles from react-window (if applies)
|
|
674
|
+
style];
|
|
675
|
+
return /*#__PURE__*/createElement(View, _extends({}, sharedProps, {
|
|
676
|
+
testId: testId,
|
|
677
|
+
style: defaultStyle,
|
|
678
|
+
"aria-selected": selected ? "true" : "false",
|
|
679
|
+
role: role
|
|
680
|
+
}, childrenProps), /*#__PURE__*/createElement(CheckComponent, {
|
|
681
|
+
disabled: disabled,
|
|
682
|
+
selected: selected,
|
|
683
|
+
pressed: pressed,
|
|
684
|
+
hovered: hovered,
|
|
685
|
+
focused: focused
|
|
686
|
+
}), /*#__PURE__*/createElement(LabelMedium, {
|
|
687
|
+
style: styles$3.label
|
|
688
|
+
}, label));
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
}], [{
|
|
692
|
+
key: "isClassOf",
|
|
693
|
+
value: function isClassOf(instance) {
|
|
694
|
+
return instance && instance.type && instance.type.__IS_OPTION_ITEM__;
|
|
695
|
+
}
|
|
696
|
+
}]);
|
|
697
|
+
|
|
698
|
+
return OptionItem;
|
|
699
|
+
}(Component);
|
|
700
|
+
|
|
701
|
+
_defineProperty(OptionItem, "contextTypes", {
|
|
702
|
+
router: any
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
_defineProperty(OptionItem, "defaultProps", {
|
|
706
|
+
disabled: false,
|
|
707
|
+
onToggle: function onToggle() {
|
|
708
|
+
return void 0;
|
|
709
|
+
},
|
|
710
|
+
role: "option",
|
|
711
|
+
selected: false
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
_defineProperty(OptionItem, "__IS_OPTION_ITEM__", true);
|
|
715
|
+
var blue$2 = Color.blue,
|
|
716
|
+
white$3 = Color.white,
|
|
717
|
+
offBlack$2 = Color.offBlack,
|
|
718
|
+
offBlack32$3 = Color.offBlack32;
|
|
719
|
+
var styles$3 = StyleSheet.create({
|
|
720
|
+
itemContainer: {
|
|
721
|
+
flexDirection: "row",
|
|
722
|
+
backgroundColor: white$3,
|
|
723
|
+
color: offBlack$2,
|
|
724
|
+
alignItems: "center",
|
|
725
|
+
height: DROPDOWN_ITEM_HEIGHT,
|
|
726
|
+
minHeight: DROPDOWN_ITEM_HEIGHT,
|
|
727
|
+
border: 0,
|
|
728
|
+
outline: 0,
|
|
729
|
+
paddingLeft: Spacing.xSmall_8,
|
|
730
|
+
paddingRight: Spacing.medium_16,
|
|
731
|
+
whiteSpace: "nowrap",
|
|
732
|
+
cursor: "default"
|
|
733
|
+
},
|
|
734
|
+
focus: {
|
|
735
|
+
color: white$3,
|
|
736
|
+
background: blue$2
|
|
737
|
+
},
|
|
738
|
+
active: {
|
|
739
|
+
color: mix(fade(blue$2, 0.32), white$3),
|
|
740
|
+
background: mix(offBlack32$3, blue$2)
|
|
741
|
+
},
|
|
742
|
+
disabled: {
|
|
743
|
+
color: offBlack32$3,
|
|
744
|
+
background: white$3
|
|
745
|
+
},
|
|
746
|
+
label: {
|
|
747
|
+
whiteSpace: "nowrap",
|
|
748
|
+
userSelect: "none",
|
|
749
|
+
marginLeft: Spacing.xSmall_8,
|
|
750
|
+
// added to truncate strings that are longer than expected
|
|
751
|
+
overflow: "hidden",
|
|
752
|
+
textOverflow: "ellipsis"
|
|
753
|
+
},
|
|
754
|
+
hide: {
|
|
755
|
+
visibility: "hidden"
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* A separator used in a dropdown menu.
|
|
761
|
+
*/
|
|
762
|
+
var SeparatorItem = /*#__PURE__*/function (_React$Component) {
|
|
763
|
+
_inherits(SeparatorItem, _React$Component);
|
|
764
|
+
|
|
765
|
+
var _super = _createSuper(SeparatorItem);
|
|
766
|
+
|
|
767
|
+
function SeparatorItem() {
|
|
768
|
+
_classCallCheck(this, SeparatorItem);
|
|
769
|
+
|
|
770
|
+
return _super.apply(this, arguments);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
_createClass(SeparatorItem, [{
|
|
774
|
+
key: "render",
|
|
775
|
+
value: function render() {
|
|
776
|
+
return (
|
|
777
|
+
/*#__PURE__*/
|
|
778
|
+
// pass optional styles from react-window (if applies)
|
|
779
|
+
createElement(View, {
|
|
780
|
+
style: [styles$4.separator, this.props.style],
|
|
781
|
+
"aria-hidden": "true"
|
|
782
|
+
})
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
}], [{
|
|
786
|
+
key: "isClassOf",
|
|
787
|
+
value: function isClassOf(instance) {
|
|
788
|
+
return instance && instance.type && instance.type.__IS_SEPARATOR_ITEM__;
|
|
789
|
+
}
|
|
790
|
+
}]);
|
|
791
|
+
|
|
792
|
+
return SeparatorItem;
|
|
793
|
+
}(Component);
|
|
794
|
+
|
|
795
|
+
_defineProperty(SeparatorItem, "__IS_SEPARATOR_ITEM__", true);
|
|
796
|
+
var styles$4 = StyleSheet.create({
|
|
797
|
+
separator: {
|
|
798
|
+
boxShadow: "0 -1px ".concat(Color.offBlack16),
|
|
799
|
+
height: 1,
|
|
800
|
+
minHeight: 1,
|
|
801
|
+
marginTop: Spacing.xxxSmall_4,
|
|
802
|
+
marginBottom: Spacing.xxxSmall_4
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
var DropdownOpener = /*#__PURE__*/function (_React$Component) {
|
|
807
|
+
_inherits(DropdownOpener, _React$Component);
|
|
808
|
+
|
|
809
|
+
var _super = _createSuper(DropdownOpener);
|
|
810
|
+
|
|
811
|
+
function DropdownOpener() {
|
|
812
|
+
var _this;
|
|
813
|
+
|
|
814
|
+
_classCallCheck(this, DropdownOpener);
|
|
815
|
+
|
|
816
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
817
|
+
args[_key] = arguments[_key];
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
_this = _super.call.apply(_super, [this].concat(args));
|
|
821
|
+
|
|
822
|
+
_defineProperty(_assertThisInitialized(_this), "getTestIdFromProps", function (childrenProps) {
|
|
823
|
+
return childrenProps.testId || childrenProps["data-test-id"];
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
return _this;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
_createClass(DropdownOpener, [{
|
|
830
|
+
key: "renderAnchorChildren",
|
|
831
|
+
value: function renderAnchorChildren(eventState, clickableChildrenProps) {
|
|
832
|
+
var _this$props = this.props,
|
|
833
|
+
disabled = _this$props.disabled,
|
|
834
|
+
testId = _this$props.testId,
|
|
835
|
+
text = _this$props.text;
|
|
836
|
+
var renderedChildren = this.props.children(_objectSpread2(_objectSpread2({}, eventState), {}, {
|
|
837
|
+
text: text
|
|
838
|
+
}));
|
|
839
|
+
var childrenProps = renderedChildren.props;
|
|
840
|
+
var childrenTestId = this.getTestIdFromProps(childrenProps);
|
|
841
|
+
return cloneElement(renderedChildren, _objectSpread2(_objectSpread2({}, clickableChildrenProps), {}, {
|
|
842
|
+
disabled: disabled,
|
|
843
|
+
onClick: childrenProps.onClick ? function (e) {
|
|
844
|
+
// This is done to avoid overriding a
|
|
845
|
+
// custom onClick handler inside the
|
|
846
|
+
// children node
|
|
847
|
+
childrenProps.onClick(e);
|
|
848
|
+
clickableChildrenProps.onClick(e);
|
|
849
|
+
} : clickableChildrenProps.onClick,
|
|
850
|
+
// try to get the testId from the child element
|
|
851
|
+
// If it's not set, try to fallback to the parent's testId
|
|
852
|
+
"data-test-id": childrenTestId || testId
|
|
853
|
+
}));
|
|
854
|
+
}
|
|
855
|
+
}, {
|
|
856
|
+
key: "render",
|
|
857
|
+
value: function render() {
|
|
858
|
+
var _this2 = this;
|
|
859
|
+
|
|
860
|
+
return /*#__PURE__*/createElement(ClickableBehavior, {
|
|
861
|
+
onClick: this.props.onClick,
|
|
862
|
+
disabled: this.props.disabled
|
|
863
|
+
}, function (eventState, handlers) {
|
|
864
|
+
return _this2.renderAnchorChildren(eventState, handlers);
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
}]);
|
|
868
|
+
|
|
869
|
+
return DropdownOpener;
|
|
870
|
+
}(Component);
|
|
871
|
+
|
|
872
|
+
_defineProperty(DropdownOpener, "defaultProps", {
|
|
873
|
+
disabled: false
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
var EmptySizes = Object.freeze({
|
|
877
|
+
top: 0,
|
|
878
|
+
left: 0,
|
|
879
|
+
bottom: 0,
|
|
880
|
+
right: 0
|
|
881
|
+
});
|
|
882
|
+
/**
|
|
883
|
+
* Get the margin, padding, and border edges for a given element.
|
|
884
|
+
*/
|
|
885
|
+
|
|
886
|
+
function getEdges(element, withoutEdges) {
|
|
887
|
+
if (!withoutEdges && element instanceof Element) {
|
|
888
|
+
var style = element.currentStyle || window.getComputedStyle(element);
|
|
889
|
+
return {
|
|
890
|
+
margin: {
|
|
891
|
+
left: parseFloat(style.marginLeft),
|
|
892
|
+
top: parseFloat(style.marginTop),
|
|
893
|
+
right: parseFloat(style.marginRight),
|
|
894
|
+
bottom: parseFloat(style.marginBottom)
|
|
895
|
+
},
|
|
896
|
+
padding: {
|
|
897
|
+
left: parseFloat(style.paddingLeft),
|
|
898
|
+
top: parseFloat(style.paddingTop),
|
|
899
|
+
right: parseFloat(style.paddingRight),
|
|
900
|
+
bottom: parseFloat(style.paddingBottom)
|
|
901
|
+
},
|
|
902
|
+
border: {
|
|
903
|
+
left: parseFloat(style.borderLeftWidth),
|
|
904
|
+
top: parseFloat(style.borderTopWidth),
|
|
905
|
+
right: parseFloat(style.borderRightWidth),
|
|
906
|
+
bottom: parseFloat(style.borderBottomWidth)
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
return {
|
|
912
|
+
margin: EmptySizes,
|
|
913
|
+
padding: EmptySizes,
|
|
914
|
+
border: EmptySizes
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
function getBounds(element) {
|
|
919
|
+
var withoutEdges = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
920
|
+
var elementRect = element.getBoundingClientRect();
|
|
921
|
+
var edges = getEdges(element, withoutEdges);
|
|
922
|
+
return {
|
|
923
|
+
left: elementRect.left + edges.margin.left + edges.padding.left + edges.border.left,
|
|
924
|
+
top: elementRect.top + edges.margin.top + edges.padding.top + edges.border.top,
|
|
925
|
+
right: elementRect.right - edges.margin.right - edges.padding.right - edges.border.right,
|
|
926
|
+
bottom: elementRect.bottom - edges.margin.bottom - edges.padding.bottom - edges.border.bottom
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function getAxisIntersection(intersectingRect, boundsRect, axis) {
|
|
931
|
+
var start = function start(rect) {
|
|
932
|
+
return axis === "horizontal" ? rect.left : rect.top;
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
var end = function end(rect) {
|
|
936
|
+
return axis === "horizontal" ? rect.right : rect.bottom;
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
if (end(intersectingRect) <= start(boundsRect)) {
|
|
940
|
+
return "before";
|
|
941
|
+
} else if (start(intersectingRect) >= end(boundsRect)) {
|
|
942
|
+
return "after";
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
return "within";
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Determine how one rectangle intersects another.
|
|
950
|
+
*
|
|
951
|
+
* The intersection should be interpreted as whether the first rectangle is
|
|
952
|
+
* within the second.
|
|
953
|
+
*/
|
|
954
|
+
|
|
955
|
+
function getIntersection(intersectingRect, boundsRect) {
|
|
956
|
+
var horizontal = getAxisIntersection(intersectingRect, boundsRect, "horizontal");
|
|
957
|
+
var vertical = getAxisIntersection(intersectingRect, boundsRect, "vertical");
|
|
958
|
+
return {
|
|
959
|
+
horizontal: horizontal,
|
|
960
|
+
vertical: vertical
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* Determine if an element is obscured by other elements.
|
|
966
|
+
*
|
|
967
|
+
* This uses document.elementFromPoint to see if the given element is being
|
|
968
|
+
* overdrawn by another element. Note that this won't work if the given element
|
|
969
|
+
* has `pointer-events: none`.
|
|
970
|
+
*/
|
|
971
|
+
|
|
972
|
+
function isObscured(anchorElement, popperElement) {
|
|
973
|
+
// TODO(somewhatabstract): We should be smarter in this algorithm and
|
|
974
|
+
// actually look at the intersection of the elements doing the obscuring
|
|
975
|
+
// just as we already do with our scroll parent intersections. That way we
|
|
976
|
+
// can not only check that the entire element is obscured, but think about
|
|
977
|
+
// partial obscurement so we can move the tooltip bubble when it's anchor
|
|
978
|
+
// point is not visible.
|
|
979
|
+
// Before we assume we're visible let's check to see if something else
|
|
980
|
+
// is obscuring us. Here we check a variety of points of the element
|
|
981
|
+
// like topleft, bottomright, and center to see if they are covered by
|
|
982
|
+
// something, and if so, assume we're not visible.
|
|
983
|
+
// There are ways that this can still not work, such as different
|
|
984
|
+
// elements only covering those points and the remainder being visible,
|
|
985
|
+
// or if some covering element has none for pointer-events style, but
|
|
986
|
+
// those edge cases shouldn't bother the main usages for this method.
|
|
987
|
+
// NOTE: If the anchor element has `pointer-events: none`, we're always
|
|
988
|
+
// going to end up hiding, so, you know, probably don't do that.
|
|
989
|
+
// We're not explicitly checking for that CSS since it's a corner-case and
|
|
990
|
+
// would impact perf of the regular cases if we were always checking it.
|
|
991
|
+
// TODO(somewhatabstract, WB-300): Need to cater to the case where the
|
|
992
|
+
// viewport is zoomed such that both corners are off screen but the rest
|
|
993
|
+
// isn't. In this case some browsers don't return the element from
|
|
994
|
+
// `elementFromPoint` then doesn't return the element.
|
|
995
|
+
// Also, consider how we might mitigate the pointer-events issue and make
|
|
996
|
+
// this call more robust.
|
|
997
|
+
var bounds = getBounds(anchorElement); // This method does the main work, taking some coordinates and determining
|
|
998
|
+
// if our element is visible at that point or not.
|
|
999
|
+
|
|
1000
|
+
var isVisible = function isVisible(x, y) {
|
|
1001
|
+
var elAtPoint = document.elementFromPoint(x, y);
|
|
1002
|
+
|
|
1003
|
+
if (elAtPoint != null && (elAtPoint === popperElement || popperElement.contains(elAtPoint))) {
|
|
1004
|
+
// Oh no, we're being obscured by our own popper.
|
|
1005
|
+
// We need to look behind it. Shenanigans time.
|
|
1006
|
+
var pointerEventsStyle = elAtPoint.style.pointerEvents; // Remove pointer events so that we can look through it.
|
|
1007
|
+
|
|
1008
|
+
elAtPoint.style.pointerEvents = "none";
|
|
1009
|
+
|
|
1010
|
+
try {
|
|
1011
|
+
var visible = isVisible(x, y);
|
|
1012
|
+
return visible;
|
|
1013
|
+
} finally {
|
|
1014
|
+
// Make sure we put things back the way we found them. :)
|
|
1015
|
+
elAtPoint.style.pointerEvents = pointerEventsStyle;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
if (anchorElement instanceof Element) {
|
|
1020
|
+
// If we are working with an element, then we can do some decendency checks
|
|
1021
|
+
// to ensure we're not just hitting a child. We're ok with saying that
|
|
1022
|
+
// we're visible if we hit a parent because we check them for visibility
|
|
1023
|
+
// elsewhere.
|
|
1024
|
+
return elAtPoint != null && (anchorElement.contains(elAtPoint) || elAtPoint.contains(anchorElement));
|
|
1025
|
+
} // If element is a reference object, all we have to work with is
|
|
1026
|
+
// intersection for checking obscurity. Since this doesn't cover
|
|
1027
|
+
// parent/child relationships in the DOM, it's not really effective
|
|
1028
|
+
// on its own and is possibly about as good as just returning `true`.
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
var intersection = elAtPoint && getIntersection(bounds, getBounds(elAtPoint, true));
|
|
1032
|
+
return (intersection === null || intersection === void 0 ? void 0 : intersection.horizontal) !== "within" || (intersection === null || intersection === void 0 ? void 0 : intersection.vertical) !== "within";
|
|
1033
|
+
}; // NOTE: We are using functions here so that we only do as much work
|
|
1034
|
+
// as we need to, short-circuiting as soon as we have a definitive
|
|
1035
|
+
// answer.
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
var isTopLeftVisible = function isTopLeftVisible() {
|
|
1039
|
+
return isVisible(bounds.left, bounds.top);
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
var isBottomRightVisible = function isBottomRightVisible() {
|
|
1043
|
+
return isVisible(bounds.right, bounds.bottom);
|
|
1044
|
+
};
|
|
1045
|
+
|
|
1046
|
+
var isCenterVisible = function isCenterVisible() {
|
|
1047
|
+
return isVisible(bounds.left + (bounds.right - bounds.left) / 2, bounds.top + (bounds.bottom - bounds.top) / 2);
|
|
1048
|
+
};
|
|
1049
|
+
|
|
1050
|
+
return !isTopLeftVisible() && !isBottomRightVisible() && !isCenterVisible();
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
var _PopperJS$Defaults$mo, _PopperJS$Defaults$mo2;
|
|
1054
|
+
/**
|
|
1055
|
+
* The function that implements the modifier.
|
|
1056
|
+
*/
|
|
1057
|
+
|
|
1058
|
+
function visibilityModifierFn(data) {
|
|
1059
|
+
var anchorElement = data.instance.reference; // First, we see how the element intersects with its scroll parents.
|
|
1060
|
+
// If it doesn't, then we should hide it.
|
|
1061
|
+
// Otherwise, we check to see if anything else obscures the component (like
|
|
1062
|
+
// a fixed or absolute positioned element).
|
|
1063
|
+
|
|
1064
|
+
var _getElementIntersecti = getElementIntersection(anchorElement),
|
|
1065
|
+
horizontal = _getElementIntersecti.horizontal,
|
|
1066
|
+
vertical = _getElementIntersecti.vertical;
|
|
1067
|
+
|
|
1068
|
+
var hide = horizontal !== "within" || vertical !== "within" || isObscured(anchorElement, data.instance.popper); // If we're hidden, we mimic what the built-in hide method does,
|
|
1069
|
+
// and set the hide flag and the OOB attribute with appropriate
|
|
1070
|
+
// short-circuiting.
|
|
1071
|
+
// https://github.com/FezVrasta/popper.js/blob/08c5d6010346bf9df06e9f81a54fa6c2c51e3639/packages/popper/src/modifiers/hide.js#L29-L42
|
|
1072
|
+
|
|
1073
|
+
if (hide) {
|
|
1074
|
+
if (data.hide) {
|
|
1075
|
+
return data;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
data.hide = true;
|
|
1079
|
+
data.attributes["x-out-of-boundaries"] = "";
|
|
1080
|
+
} else {
|
|
1081
|
+
// Avoid unnecessary DOM access if visibility hasn't changed
|
|
1082
|
+
if (!data.hide) {
|
|
1083
|
+
return data;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
data.hide = false;
|
|
1087
|
+
data.attributes["x-out-of-boundaries"] = false;
|
|
1088
|
+
} // Always have to return the data object to ensure the modifier chain
|
|
1089
|
+
// in popper.js is unbroken.
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
return data;
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Default configuration that sets things up how usually want them.
|
|
1096
|
+
* Usage:
|
|
1097
|
+
* ```js
|
|
1098
|
+
* import visibilityModifier from "visibility-modifier.js";
|
|
1099
|
+
* const modifiers = [
|
|
1100
|
+
* wbvisibility: visibilityModifier,
|
|
1101
|
+
* ];
|
|
1102
|
+
* ```
|
|
1103
|
+
*
|
|
1104
|
+
* Where `wbvisibility` is a unique name to give the modifier entry,
|
|
1105
|
+
* and `modifiers` is the popper.js or react-popper modifiers array.
|
|
1106
|
+
*/
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
var visibilityModifierDefaultConfig = {
|
|
1110
|
+
enabled: true,
|
|
1111
|
+
// We want this to run after the "hide" modifier, by default.
|
|
1112
|
+
order: (((_PopperJS$Defaults$mo = PopperJS.Defaults.modifiers) === null || _PopperJS$Defaults$mo === void 0 ? void 0 : (_PopperJS$Defaults$mo2 = _PopperJS$Defaults$mo.hide) === null || _PopperJS$Defaults$mo2 === void 0 ? void 0 : _PopperJS$Defaults$mo2.order) || 0) + 1,
|
|
1113
|
+
fn: visibilityModifierFn
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* A virtualized list item - It's created by decorating the DropdownItem
|
|
1118
|
+
* (ActionItem, OptionItem, SeparatorItem) with custom styles to let
|
|
1119
|
+
* react-window make its own calculations.
|
|
1120
|
+
*/
|
|
1121
|
+
var DropdownVirtualizedItem = /*#__PURE__*/function (_React$Component) {
|
|
1122
|
+
_inherits(DropdownVirtualizedItem, _React$Component);
|
|
1123
|
+
|
|
1124
|
+
var _super = _createSuper(DropdownVirtualizedItem);
|
|
1125
|
+
|
|
1126
|
+
function DropdownVirtualizedItem() {
|
|
1127
|
+
_classCallCheck(this, DropdownVirtualizedItem);
|
|
1128
|
+
|
|
1129
|
+
return _super.apply(this, arguments);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
_createClass(DropdownVirtualizedItem, [{
|
|
1133
|
+
key: "render",
|
|
1134
|
+
value: function render() {
|
|
1135
|
+
var _this$props = this.props,
|
|
1136
|
+
data = _this$props.data,
|
|
1137
|
+
index = _this$props.index,
|
|
1138
|
+
style = _this$props.style;
|
|
1139
|
+
var item = data[index];
|
|
1140
|
+
|
|
1141
|
+
if (SeparatorItem.isClassOf(item.component)) {
|
|
1142
|
+
// add react-window style to the separator to preserve the correct
|
|
1143
|
+
// position
|
|
1144
|
+
return cloneElement(item.component, {
|
|
1145
|
+
style: style
|
|
1146
|
+
});
|
|
1147
|
+
} else {
|
|
1148
|
+
var component = item.component,
|
|
1149
|
+
populatedProps = item.populatedProps,
|
|
1150
|
+
onClick = item.onClick,
|
|
1151
|
+
role = item.role,
|
|
1152
|
+
ref = item.ref;
|
|
1153
|
+
return cloneElement(component, _objectSpread2(_objectSpread2({
|
|
1154
|
+
style: style
|
|
1155
|
+
}, populatedProps), {}, {
|
|
1156
|
+
key: index,
|
|
1157
|
+
onClick: onClick,
|
|
1158
|
+
ref: item.focusable && ref,
|
|
1159
|
+
role: role
|
|
1160
|
+
}));
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}]);
|
|
1164
|
+
|
|
1165
|
+
return DropdownVirtualizedItem;
|
|
1166
|
+
}(Component);
|
|
1167
|
+
|
|
1168
|
+
var SearchTextInput = /*#__PURE__*/function (_React$Component) {
|
|
1169
|
+
_inherits(SearchTextInput, _React$Component);
|
|
1170
|
+
|
|
1171
|
+
var _super = _createSuper(SearchTextInput);
|
|
1172
|
+
|
|
1173
|
+
function SearchTextInput() {
|
|
1174
|
+
var _this;
|
|
1175
|
+
|
|
1176
|
+
_classCallCheck(this, SearchTextInput);
|
|
1177
|
+
|
|
1178
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
1179
|
+
args[_key] = arguments[_key];
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
_this = _super.call.apply(_super, [this].concat(args));
|
|
1183
|
+
|
|
1184
|
+
_defineProperty(_assertThisInitialized(_this), "state", {
|
|
1185
|
+
focused: false,
|
|
1186
|
+
labels: _objectSpread2({
|
|
1187
|
+
clearSearch: defaultLabels.clearSearch,
|
|
1188
|
+
filter: defaultLabels.filter
|
|
1189
|
+
}, _this.props.labels)
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1192
|
+
_defineProperty(_assertThisInitialized(_this), "handleChange", function (e) {
|
|
1193
|
+
e.preventDefault();
|
|
1194
|
+
|
|
1195
|
+
_this.props.onChange(e.target.value);
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
_defineProperty(_assertThisInitialized(_this), "handleDismiss", function () {
|
|
1199
|
+
var _this$props = _this.props,
|
|
1200
|
+
onClick = _this$props.onClick,
|
|
1201
|
+
onChange = _this$props.onChange; // Empty the search text and focus the SearchTextInput
|
|
1202
|
+
|
|
1203
|
+
onChange("");
|
|
1204
|
+
|
|
1205
|
+
if (onClick) {
|
|
1206
|
+
onClick();
|
|
1207
|
+
}
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
_defineProperty(_assertThisInitialized(_this), "handleBlur", function (e) {
|
|
1211
|
+
_this.setState({
|
|
1212
|
+
focused: false
|
|
1213
|
+
});
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
_defineProperty(_assertThisInitialized(_this), "handleFocus", function (e) {
|
|
1217
|
+
_this.setState({
|
|
1218
|
+
focused: true
|
|
1219
|
+
});
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
return _this;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
_createClass(SearchTextInput, [{
|
|
1226
|
+
key: "componentDidUpdate",
|
|
1227
|
+
value: function componentDidUpdate(prevProps) {
|
|
1228
|
+
if (this.props.labels !== prevProps.labels) {
|
|
1229
|
+
// eslint-disable-next-line react/no-did-update-set-state
|
|
1230
|
+
this.setState({
|
|
1231
|
+
labels: _objectSpread2(_objectSpread2({}, this.state.labels), this.props.labels)
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}, {
|
|
1236
|
+
key: "maybeRenderDismissIconButton",
|
|
1237
|
+
value: function maybeRenderDismissIconButton() {
|
|
1238
|
+
var searchText = this.props.searchText;
|
|
1239
|
+
var clearSearch = this.state.labels.clearSearch;
|
|
1240
|
+
|
|
1241
|
+
if (searchText.length > 0) {
|
|
1242
|
+
return /*#__PURE__*/createElement(IconButton, {
|
|
1243
|
+
icon: icons.dismiss,
|
|
1244
|
+
kind: "tertiary",
|
|
1245
|
+
onClick: this.handleDismiss,
|
|
1246
|
+
style: styles$5.dismissIcon,
|
|
1247
|
+
"aria-label": clearSearch
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
return null;
|
|
1252
|
+
}
|
|
1253
|
+
}, {
|
|
1254
|
+
key: "render",
|
|
1255
|
+
value: function render() {
|
|
1256
|
+
var _this$props2 = this.props,
|
|
1257
|
+
onClick = _this$props2.onClick,
|
|
1258
|
+
itemRef = _this$props2.itemRef,
|
|
1259
|
+
searchText = _this$props2.searchText,
|
|
1260
|
+
style = _this$props2.style,
|
|
1261
|
+
testId = _this$props2.testId;
|
|
1262
|
+
var filter = this.state.labels.filter;
|
|
1263
|
+
return /*#__PURE__*/createElement(View, {
|
|
1264
|
+
onClick: onClick,
|
|
1265
|
+
style: [styles$5.inputContainer, this.state.focused && styles$5.focused, style]
|
|
1266
|
+
}, /*#__PURE__*/createElement(Icon, {
|
|
1267
|
+
icon: icons.search,
|
|
1268
|
+
size: "medium",
|
|
1269
|
+
color: Color.offBlack64,
|
|
1270
|
+
style: styles$5.searchIcon,
|
|
1271
|
+
"aria-hidden": "true"
|
|
1272
|
+
}), /*#__PURE__*/createElement("input", {
|
|
1273
|
+
type: "text",
|
|
1274
|
+
onChange: this.handleChange,
|
|
1275
|
+
onFocus: this.handleFocus,
|
|
1276
|
+
onBlur: this.handleBlur,
|
|
1277
|
+
ref: itemRef,
|
|
1278
|
+
placeholder: filter,
|
|
1279
|
+
value: searchText,
|
|
1280
|
+
className: css(styles$5.inputStyleReset, styles$a.LabelMedium),
|
|
1281
|
+
"data-test-id": testId
|
|
1282
|
+
}), this.maybeRenderDismissIconButton());
|
|
1283
|
+
}
|
|
1284
|
+
}], [{
|
|
1285
|
+
key: "isClassOf",
|
|
1286
|
+
value: function isClassOf(instance) {
|
|
1287
|
+
return instance && instance.type && instance.type.__IS_SEARCH_TEXT_INPUT__;
|
|
1288
|
+
}
|
|
1289
|
+
}]);
|
|
1290
|
+
|
|
1291
|
+
return SearchTextInput;
|
|
1292
|
+
}(Component);
|
|
1293
|
+
|
|
1294
|
+
_defineProperty(SearchTextInput, "defaultProps", {
|
|
1295
|
+
labels: {
|
|
1296
|
+
clearSearch: defaultLabels.clearSearch,
|
|
1297
|
+
filter: defaultLabels.filter
|
|
1298
|
+
}
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
_defineProperty(SearchTextInput, "__IS_SEARCH_TEXT_INPUT__", true);
|
|
1302
|
+
var styles$5 = StyleSheet.create({
|
|
1303
|
+
inputContainer: {
|
|
1304
|
+
flexDirection: "row",
|
|
1305
|
+
border: "1px solid ".concat(Color.offBlack16),
|
|
1306
|
+
borderRadius: Spacing.xxxSmall_4,
|
|
1307
|
+
alignItems: "center",
|
|
1308
|
+
// The height of the text input is 40 in design spec and we need to
|
|
1309
|
+
// specify the height as well as minHeight to make sure the search text
|
|
1310
|
+
// input takes enough height to render. (otherwise, it will get
|
|
1311
|
+
// squashed)
|
|
1312
|
+
height: DROPDOWN_ITEM_HEIGHT,
|
|
1313
|
+
minHeight: DROPDOWN_ITEM_HEIGHT
|
|
1314
|
+
},
|
|
1315
|
+
focused: {
|
|
1316
|
+
border: "1px solid ".concat(Color.blue)
|
|
1317
|
+
},
|
|
1318
|
+
searchIcon: {
|
|
1319
|
+
marginLeft: Spacing.xSmall_8,
|
|
1320
|
+
marginRight: Spacing.xSmall_8
|
|
1321
|
+
},
|
|
1322
|
+
dismissIcon: {
|
|
1323
|
+
margin: 0,
|
|
1324
|
+
":hover": {
|
|
1325
|
+
border: "none"
|
|
1326
|
+
}
|
|
1327
|
+
},
|
|
1328
|
+
inputStyleReset: {
|
|
1329
|
+
display: "flex",
|
|
1330
|
+
flex: 1,
|
|
1331
|
+
background: "inherit",
|
|
1332
|
+
border: "none",
|
|
1333
|
+
outline: "none",
|
|
1334
|
+
"::placeholder": {
|
|
1335
|
+
color: Color.offBlack64
|
|
1336
|
+
},
|
|
1337
|
+
width: "100%",
|
|
1338
|
+
color: "inherit"
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
/**
|
|
1343
|
+
* Maximum visible items inside the dropdown list
|
|
1344
|
+
*/
|
|
1345
|
+
var MAX_VISIBLE_ITEMS = 10;
|
|
1346
|
+
/**
|
|
1347
|
+
* A react-window's List wrapper that instantiates the virtualized list and
|
|
1348
|
+
* dynamically calculates the item height depending on the type
|
|
1349
|
+
*/
|
|
1350
|
+
|
|
1351
|
+
var DropdownCoreVirtualized = /*#__PURE__*/function (_React$Component) {
|
|
1352
|
+
_inherits(DropdownCoreVirtualized, _React$Component);
|
|
1353
|
+
|
|
1354
|
+
var _super = _createSuper(DropdownCoreVirtualized);
|
|
1355
|
+
|
|
1356
|
+
function DropdownCoreVirtualized() {
|
|
1357
|
+
var _this;
|
|
1358
|
+
|
|
1359
|
+
_classCallCheck(this, DropdownCoreVirtualized);
|
|
1360
|
+
|
|
1361
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
1362
|
+
args[_key] = arguments[_key];
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
_this = _super.call.apply(_super, [this].concat(args));
|
|
1366
|
+
|
|
1367
|
+
_defineProperty(_assertThisInitialized(_this), "state", {
|
|
1368
|
+
height: _this.getHeight(),
|
|
1369
|
+
width: _this.props.width
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
_defineProperty(_assertThisInitialized(_this), "getItemSize", function (index) {
|
|
1373
|
+
// get the current item in the list
|
|
1374
|
+
var item = _this.props.data[index];
|
|
1375
|
+
|
|
1376
|
+
if (SeparatorItem.isClassOf(item.component)) {
|
|
1377
|
+
// this is the separator's height (1px) + vertical margin (8px)
|
|
1378
|
+
return SEPARATOR_ITEM_HEIGHT;
|
|
1379
|
+
} else if (SearchTextInput.isClassOf(item.component)) {
|
|
1380
|
+
// search text input height
|
|
1381
|
+
return SEARCH_ITEM_HEIGHT;
|
|
1382
|
+
} else {
|
|
1383
|
+
// default dropdown item height
|
|
1384
|
+
return DROPDOWN_ITEM_HEIGHT;
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1388
|
+
return _this;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
_createClass(DropdownCoreVirtualized, [{
|
|
1392
|
+
key: "componentDidMount",
|
|
1393
|
+
value: function componentDidMount() {
|
|
1394
|
+
var _this2 = this;
|
|
1395
|
+
|
|
1396
|
+
var schedule = this.props.schedule; // Wait for styles to be applied. This way, we can get a more precise
|
|
1397
|
+
// value of the container dimensions.
|
|
1398
|
+
|
|
1399
|
+
schedule.animationFrame(function () {
|
|
1400
|
+
_this2.setWidth();
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
}, {
|
|
1404
|
+
key: "componentDidUpdate",
|
|
1405
|
+
value: function componentDidUpdate(prevProps) {
|
|
1406
|
+
var _this$props = this.props,
|
|
1407
|
+
data = _this$props.data,
|
|
1408
|
+
listRef = _this$props.listRef; // if the items size has changed, then recalculate each item position
|
|
1409
|
+
|
|
1410
|
+
if (prevProps.data.length !== data.length) {
|
|
1411
|
+
this.setHeight();
|
|
1412
|
+
|
|
1413
|
+
if (listRef && listRef.current) {
|
|
1414
|
+
// the ref can't associate this instance method
|
|
1415
|
+
// $FlowIgnore
|
|
1416
|
+
listRef.current.resetAfterIndex(1);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Update container width
|
|
1422
|
+
*/
|
|
1423
|
+
|
|
1424
|
+
}, {
|
|
1425
|
+
key: "setWidth",
|
|
1426
|
+
value: function setWidth() {
|
|
1427
|
+
var rootNode = ReactDOM.findDOMNode(this);
|
|
1428
|
+
var parentNode = rootNode === null || rootNode === void 0 ? void 0 : rootNode.parentElement; // after the non-virtualized items are rendered, we get the container
|
|
1429
|
+
// width to pass it to react-window's List
|
|
1430
|
+
|
|
1431
|
+
if (parentNode) {
|
|
1432
|
+
var width = parentNode.getBoundingClientRect().width;
|
|
1433
|
+
this.setState({
|
|
1434
|
+
width: width
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Update container height
|
|
1440
|
+
*/
|
|
1441
|
+
|
|
1442
|
+
}, {
|
|
1443
|
+
key: "setHeight",
|
|
1444
|
+
value: function setHeight() {
|
|
1445
|
+
// calculate dropdown's height depending on the type of items
|
|
1446
|
+
var height = this.getHeight();
|
|
1447
|
+
this.setState({
|
|
1448
|
+
height: height
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* The list height that is automatically calculated depending on the
|
|
1453
|
+
* component's type of each item (e.g. Separator, Option, Search, etc)
|
|
1454
|
+
*/
|
|
1455
|
+
|
|
1456
|
+
}, {
|
|
1457
|
+
key: "getHeight",
|
|
1458
|
+
value: function getHeight() {
|
|
1459
|
+
// calculate using the first 10 items on the array as we want to display
|
|
1460
|
+
// this number of elements in the visible area
|
|
1461
|
+
return this.props.data.slice(0, MAX_VISIBLE_ITEMS).reduce(function (sum, item) {
|
|
1462
|
+
if (SeparatorItem.isClassOf(item.component)) {
|
|
1463
|
+
return sum + SEPARATOR_ITEM_HEIGHT;
|
|
1464
|
+
} else if (SearchTextInput.isClassOf(item.component)) {
|
|
1465
|
+
// search text input height
|
|
1466
|
+
return sum + SEARCH_ITEM_HEIGHT;
|
|
1467
|
+
} else {
|
|
1468
|
+
return sum + DROPDOWN_ITEM_HEIGHT;
|
|
1469
|
+
}
|
|
1470
|
+
}, 0);
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Calculates item height
|
|
1474
|
+
*/
|
|
1475
|
+
|
|
1476
|
+
}, {
|
|
1477
|
+
key: "renderInitialItems",
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* render non virtualized items to calculate the container max-width that
|
|
1481
|
+
* will be used by DropdownCoreVirtualized
|
|
1482
|
+
*/
|
|
1483
|
+
value: function renderInitialItems() {
|
|
1484
|
+
var data = this.props.data;
|
|
1485
|
+
var allComponents = data.map(function (e) {
|
|
1486
|
+
return e.component;
|
|
1487
|
+
}); // 1. get the children opaque data structure to sort each item by its
|
|
1488
|
+
// label length
|
|
1489
|
+
|
|
1490
|
+
var longestItems = Children.toArray(allComponents).filter(Boolean).sort(function (a, b) {
|
|
1491
|
+
// 2. only sort elements that contain a `label` prop
|
|
1492
|
+
if (b.props.label && a.props.label) {
|
|
1493
|
+
return b.props.label.length - a.props.label.length;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
return -1;
|
|
1497
|
+
}) // 3. only render the possible visible items to minimize layout
|
|
1498
|
+
// jumps
|
|
1499
|
+
.slice(0, MAX_VISIBLE_ITEMS); // Append longest items to calculate the container width.
|
|
1500
|
+
// We need to hide these sorted elements to avoid any FOUC.
|
|
1501
|
+
|
|
1502
|
+
return longestItems.map(function (item) {
|
|
1503
|
+
return cloneElement(item, {
|
|
1504
|
+
style: {
|
|
1505
|
+
visibility: "hidden"
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
}, {
|
|
1511
|
+
key: "renderVirtualizedList",
|
|
1512
|
+
value: function renderVirtualizedList() {
|
|
1513
|
+
var _this$props2 = this.props,
|
|
1514
|
+
data = _this$props2.data,
|
|
1515
|
+
listRef = _this$props2.listRef;
|
|
1516
|
+
var _this$state = this.state,
|
|
1517
|
+
height = _this$state.height,
|
|
1518
|
+
width = _this$state.width;
|
|
1519
|
+
return (
|
|
1520
|
+
/*#__PURE__*/
|
|
1521
|
+
// react-window has some issues for typing lists when passing refs
|
|
1522
|
+
// $FlowIgnore
|
|
1523
|
+
createElement(VariableSizeList // react-window doesn't accept maybe numbers. It wants numbers
|
|
1524
|
+
// or strings.
|
|
1525
|
+
// $FlowFixMe
|
|
1526
|
+
, {
|
|
1527
|
+
height: height,
|
|
1528
|
+
itemCount: data.length,
|
|
1529
|
+
itemSize: this.getItemSize,
|
|
1530
|
+
itemData: data,
|
|
1531
|
+
style: {
|
|
1532
|
+
overflowX: "hidden"
|
|
1533
|
+
} // react-window doesn't accept maybe numbers. It wants numbers
|
|
1534
|
+
// or strings.
|
|
1535
|
+
// $FlowFixMe
|
|
1536
|
+
,
|
|
1537
|
+
width: width,
|
|
1538
|
+
overscanCount: 5,
|
|
1539
|
+
ref: listRef
|
|
1540
|
+
}, DropdownVirtualizedItem)
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
}, {
|
|
1544
|
+
key: "render",
|
|
1545
|
+
value: function render() {
|
|
1546
|
+
if (this.state.width === undefined) {
|
|
1547
|
+
// if we don't pass a fixed value, then we need to render
|
|
1548
|
+
// non-virtualized items to calculate width
|
|
1549
|
+
return this.renderInitialItems();
|
|
1550
|
+
} else {
|
|
1551
|
+
// width has been provided, then render the virtualized list
|
|
1552
|
+
return this.renderVirtualizedList();
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
}]);
|
|
1556
|
+
|
|
1557
|
+
return DropdownCoreVirtualized;
|
|
1558
|
+
}(Component);
|
|
1559
|
+
|
|
1560
|
+
var DropdownCoreVirtualized$1 = withActionScheduler(DropdownCoreVirtualized);
|
|
1561
|
+
|
|
1562
|
+
/**
|
|
1563
|
+
* A core dropdown component that takes an opener and children to display as
|
|
1564
|
+
* part of the dropdown menu. Renders the dropdown as a portal to avoid clipping
|
|
1565
|
+
* in overflow: auto containers.
|
|
1566
|
+
*/
|
|
1567
|
+
var DropdownCore = /*#__PURE__*/function (_React$Component) {
|
|
1568
|
+
_inherits(DropdownCore, _React$Component);
|
|
1569
|
+
|
|
1570
|
+
var _super = _createSuper(DropdownCore);
|
|
1571
|
+
|
|
1572
|
+
_createClass(DropdownCore, null, [{
|
|
1573
|
+
key: "sameItemsFocusable",
|
|
1574
|
+
// Keeps track of the index of the focused item, out of a list of focusable items
|
|
1575
|
+
// Keeps track of the index of the focused item in the context of all the
|
|
1576
|
+
// items contained by this menu, whether focusable or not, used for figuring
|
|
1577
|
+
// out focus correctly when the items have changed in terms of whether
|
|
1578
|
+
// they're focusable or not
|
|
1579
|
+
// Whether any items have been selected since the menu was opened
|
|
1580
|
+
// Keeps a reference of the virtualized list instance
|
|
1581
|
+
// Figure out if the same items are focusable. If an item has been added or
|
|
1582
|
+
// removed, this method will return false.
|
|
1583
|
+
value: function sameItemsFocusable(prevItems, currentItems) {
|
|
1584
|
+
if (prevItems.length !== currentItems.length) {
|
|
1585
|
+
return false;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
for (var i = 0; i < prevItems.length; i++) {
|
|
1589
|
+
if (prevItems[i].focusable !== currentItems[i].focusable) {
|
|
1590
|
+
return false;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
return true;
|
|
1595
|
+
}
|
|
1596
|
+
}, {
|
|
1597
|
+
key: "getDerivedStateFromProps",
|
|
1598
|
+
// This is here to avoid calling React.createRef on each rerender. Instead,
|
|
1599
|
+
// we create the itemRefs only if it's the first time or if the set of items
|
|
1600
|
+
// that are focusable has changed.
|
|
1601
|
+
value: function getDerivedStateFromProps(props, state) {
|
|
1602
|
+
if (state.itemRefs.length === 0 && props.open || !DropdownCore.sameItemsFocusable(state.prevItems, props.items)) {
|
|
1603
|
+
var itemRefs = [];
|
|
1604
|
+
|
|
1605
|
+
for (var i = 0; i < props.items.length; i++) {
|
|
1606
|
+
if (props.items[i].focusable) {
|
|
1607
|
+
var ref = createRef();
|
|
1608
|
+
itemRefs.push({
|
|
1609
|
+
ref: ref,
|
|
1610
|
+
originalIndex: i
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
return {
|
|
1616
|
+
itemRefs: itemRefs,
|
|
1617
|
+
prevItems: props.items,
|
|
1618
|
+
sameItemsFocusable: false
|
|
1619
|
+
};
|
|
1620
|
+
} else {
|
|
1621
|
+
return {
|
|
1622
|
+
prevItems: props.items,
|
|
1623
|
+
sameItemsFocusable: true
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
}]);
|
|
1628
|
+
|
|
1629
|
+
function DropdownCore(props) {
|
|
1630
|
+
var _this;
|
|
1631
|
+
|
|
1632
|
+
_classCallCheck(this, DropdownCore);
|
|
1633
|
+
|
|
1634
|
+
_this = _super.call(this, props);
|
|
1635
|
+
|
|
1636
|
+
_defineProperty(_assertThisInitialized(_this), "focusedIndex", void 0);
|
|
1637
|
+
|
|
1638
|
+
_defineProperty(_assertThisInitialized(_this), "focusedOriginalIndex", void 0);
|
|
1639
|
+
|
|
1640
|
+
_defineProperty(_assertThisInitialized(_this), "itemsClicked", void 0);
|
|
1641
|
+
|
|
1642
|
+
_defineProperty(_assertThisInitialized(_this), "popperElement", void 0);
|
|
1643
|
+
|
|
1644
|
+
_defineProperty(_assertThisInitialized(_this), "listRef", void 0);
|
|
1645
|
+
|
|
1646
|
+
_defineProperty(_assertThisInitialized(_this), "handleInteract", function (event) {
|
|
1647
|
+
var _this$props = _this.props,
|
|
1648
|
+
open = _this$props.open,
|
|
1649
|
+
onOpenChanged = _this$props.onOpenChanged;
|
|
1650
|
+
var target = event.target;
|
|
1651
|
+
var thisElement = ReactDOM.findDOMNode(_assertThisInitialized(_this));
|
|
1652
|
+
|
|
1653
|
+
if (open && thisElement && !thisElement.contains(target) && _this.popperElement && !_this.popperElement.contains(target)) {
|
|
1654
|
+
onOpenChanged(false);
|
|
1655
|
+
}
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1658
|
+
_defineProperty(_assertThisInitialized(_this), "handleKeyDown", function (event) {
|
|
1659
|
+
var _this$props2 = _this.props,
|
|
1660
|
+
onOpenChanged = _this$props2.onOpenChanged,
|
|
1661
|
+
open = _this$props2.open,
|
|
1662
|
+
onSearchTextChanged = _this$props2.onSearchTextChanged,
|
|
1663
|
+
searchText = _this$props2.searchText;
|
|
1664
|
+
var keyCode = event.which || event.keyCode; // If menu isn't open and user presses down, open the menu
|
|
1665
|
+
|
|
1666
|
+
if (!open) {
|
|
1667
|
+
if (keyCode === keyCodes.down) {
|
|
1668
|
+
event.preventDefault();
|
|
1669
|
+
onOpenChanged(true);
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
var showSearchTextInput = !!onSearchTextChanged && typeof searchText === "string"; // Handle all other key behavior
|
|
1677
|
+
|
|
1678
|
+
switch (keyCode) {
|
|
1679
|
+
case keyCodes.tab:
|
|
1680
|
+
// When we show SearchTextInput and that is focused and the
|
|
1681
|
+
// searchText is entered at least one character, dismiss button
|
|
1682
|
+
// is displayed. When user presses tab, we should move focus
|
|
1683
|
+
// to the dismiss button.
|
|
1684
|
+
if (showSearchTextInput && _this.focusedIndex === 0 && searchText) {
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
_this.restoreTabOrder();
|
|
1689
|
+
|
|
1690
|
+
onOpenChanged(false);
|
|
1691
|
+
return;
|
|
1692
|
+
|
|
1693
|
+
case keyCodes.space:
|
|
1694
|
+
// When we display SearchTextInput and the focus is on it,
|
|
1695
|
+
// we should let the user type space.
|
|
1696
|
+
if (showSearchTextInput && _this.focusedIndex === 0) {
|
|
1697
|
+
return;
|
|
1698
|
+
} // Prevent space from scrolling down the page
|
|
1699
|
+
|
|
1700
|
+
|
|
1701
|
+
event.preventDefault();
|
|
1702
|
+
return;
|
|
1703
|
+
|
|
1704
|
+
case keyCodes.up:
|
|
1705
|
+
event.preventDefault();
|
|
1706
|
+
|
|
1707
|
+
_this.focusPreviousItem();
|
|
1708
|
+
|
|
1709
|
+
return;
|
|
1710
|
+
|
|
1711
|
+
case keyCodes.down:
|
|
1712
|
+
event.preventDefault();
|
|
1713
|
+
|
|
1714
|
+
_this.focusNextItem();
|
|
1715
|
+
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1719
|
+
|
|
1720
|
+
_defineProperty(_assertThisInitialized(_this), "handleKeyUp", function (event) {
|
|
1721
|
+
var _this$props3 = _this.props,
|
|
1722
|
+
onOpenChanged = _this$props3.onOpenChanged,
|
|
1723
|
+
open = _this$props3.open,
|
|
1724
|
+
onSearchTextChanged = _this$props3.onSearchTextChanged,
|
|
1725
|
+
searchText = _this$props3.searchText;
|
|
1726
|
+
var keyCode = event.which || event.keyCode;
|
|
1727
|
+
var showSearchTextInput = !!onSearchTextChanged && typeof searchText === "string";
|
|
1728
|
+
|
|
1729
|
+
switch (keyCode) {
|
|
1730
|
+
case keyCodes.space:
|
|
1731
|
+
// When we display SearchTextInput and the focus is on it,
|
|
1732
|
+
// we should let the user type space.
|
|
1733
|
+
if (showSearchTextInput && _this.focusedIndex === 0) {
|
|
1734
|
+
return;
|
|
1735
|
+
} // Prevent space from scrolling down the page
|
|
1736
|
+
|
|
1737
|
+
|
|
1738
|
+
event.preventDefault();
|
|
1739
|
+
return;
|
|
1740
|
+
|
|
1741
|
+
case keyCodes.escape:
|
|
1742
|
+
// Close only the dropdown, not other elements that are
|
|
1743
|
+
// listening for an escape press
|
|
1744
|
+
if (open) {
|
|
1745
|
+
event.stopPropagation();
|
|
1746
|
+
|
|
1747
|
+
_this.restoreTabOrder();
|
|
1748
|
+
|
|
1749
|
+
onOpenChanged(false);
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
});
|
|
1755
|
+
|
|
1756
|
+
_defineProperty(_assertThisInitialized(_this), "handleClickFocus", function (index) {
|
|
1757
|
+
// Turn itemsClicked on so pressing up or down would focus the
|
|
1758
|
+
// appropriate item in handleKeyDown
|
|
1759
|
+
_this.itemsClicked = true;
|
|
1760
|
+
_this.focusedIndex = index;
|
|
1761
|
+
_this.focusedOriginalIndex = _this.state.itemRefs[_this.focusedIndex].originalIndex;
|
|
1762
|
+
});
|
|
1763
|
+
|
|
1764
|
+
_defineProperty(_assertThisInitialized(_this), "handleDropdownMouseUp", function (event) {
|
|
1765
|
+
// $FlowIgnore[method-unbinding]
|
|
1766
|
+
if (event.nativeEvent.stopImmediatePropagation) {
|
|
1767
|
+
event.nativeEvent.stopImmediatePropagation();
|
|
1768
|
+
} else {
|
|
1769
|
+
// Workaround for jsdom
|
|
1770
|
+
event.stopPropagation();
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
|
|
1774
|
+
_this.focusedIndex = _this.props.initialFocusedIndex;
|
|
1775
|
+
_this.state = {
|
|
1776
|
+
prevItems: _this.props.items,
|
|
1777
|
+
itemRefs: [],
|
|
1778
|
+
sameItemsFocusable: false,
|
|
1779
|
+
labels: _objectSpread2({
|
|
1780
|
+
noResults: defaultLabels.noResults
|
|
1781
|
+
}, props.labels)
|
|
1782
|
+
};
|
|
1783
|
+
_this.listRef = createRef();
|
|
1784
|
+
return _this;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
_createClass(DropdownCore, [{
|
|
1788
|
+
key: "componentDidMount",
|
|
1789
|
+
value: function componentDidMount() {
|
|
1790
|
+
this.updateEventListeners();
|
|
1791
|
+
this.initialFocusItem();
|
|
1792
|
+
}
|
|
1793
|
+
}, {
|
|
1794
|
+
key: "componentDidUpdate",
|
|
1795
|
+
value: function componentDidUpdate(prevProps) {
|
|
1796
|
+
var _this2 = this;
|
|
1797
|
+
|
|
1798
|
+
var open = this.props.open;
|
|
1799
|
+
|
|
1800
|
+
if (prevProps.open !== open) {
|
|
1801
|
+
this.updateEventListeners();
|
|
1802
|
+
this.initialFocusItem();
|
|
1803
|
+
} // If the menu changed, but from open to open, figure out if we need
|
|
1804
|
+
// to recalculate the focus somehow.
|
|
1805
|
+
else if (open) {
|
|
1806
|
+
var _this$state = this.state,
|
|
1807
|
+
itemRefs = _this$state.itemRefs,
|
|
1808
|
+
sameItemsFocusable = _this$state.sameItemsFocusable; // Check if the same items are focused by comparing the items at
|
|
1809
|
+
// each index and seeing if the {focusable} property is the same.
|
|
1810
|
+
// Very rarely do the set of focusable items change if the menu
|
|
1811
|
+
// hasn't been re-opened. This is for cases like a {Select all}
|
|
1812
|
+
// option that becomes disabled iff all the options are selected.
|
|
1813
|
+
|
|
1814
|
+
if (sameItemsFocusable) {
|
|
1815
|
+
return;
|
|
1816
|
+
} else {
|
|
1817
|
+
// If the set of items that was focusabled changed, it's very
|
|
1818
|
+
// likely that the previously focused item no longer has the
|
|
1819
|
+
// same index relative to the list of focusable items. Instead,
|
|
1820
|
+
// use the focusedOriginalIndex to find the new index of the
|
|
1821
|
+
// last item that was focused before this change
|
|
1822
|
+
var newFocusableIndex = itemRefs.findIndex(function (ref) {
|
|
1823
|
+
return ref.originalIndex === _this2.focusedOriginalIndex;
|
|
1824
|
+
});
|
|
1825
|
+
|
|
1826
|
+
if (newFocusableIndex === -1) {
|
|
1827
|
+
// Can't find the originally focused item, return focus to
|
|
1828
|
+
// the first item that IS focusable
|
|
1829
|
+
this.focusedIndex = 0; // Reset the knowlege that things had been clicked
|
|
1830
|
+
|
|
1831
|
+
this.itemsClicked = false;
|
|
1832
|
+
this.scheduleToFocusCurrentItem();
|
|
1833
|
+
} else {
|
|
1834
|
+
this.focusedIndex = newFocusableIndex;
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
if (this.props.labels !== prevProps.labels) {
|
|
1839
|
+
// eslint-disable-next-line react/no-did-update-set-state
|
|
1840
|
+
this.setState({
|
|
1841
|
+
labels: _objectSpread2(_objectSpread2({}, this.state.labels), this.props.labels)
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
}, {
|
|
1847
|
+
key: "componentWillUnmount",
|
|
1848
|
+
value: function componentWillUnmount() {
|
|
1849
|
+
this.removeEventListeners();
|
|
1850
|
+
} // Figure out focus states for the dropdown after it has changed from open
|
|
1851
|
+
// to closed or vice versa
|
|
1852
|
+
|
|
1853
|
+
}, {
|
|
1854
|
+
key: "initialFocusItem",
|
|
1855
|
+
value: function initialFocusItem() {
|
|
1856
|
+
var _this$props4 = this.props,
|
|
1857
|
+
initialFocusedIndex = _this$props4.initialFocusedIndex,
|
|
1858
|
+
open = _this$props4.open;
|
|
1859
|
+
|
|
1860
|
+
if (open) {
|
|
1861
|
+
// Reset focused index
|
|
1862
|
+
this.focusedIndex = initialFocusedIndex;
|
|
1863
|
+
this.scheduleToFocusCurrentItem();
|
|
1864
|
+
} else if (!open) {
|
|
1865
|
+
this.itemsClicked = false;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}, {
|
|
1869
|
+
key: "updateEventListeners",
|
|
1870
|
+
value: function updateEventListeners() {
|
|
1871
|
+
if (this.props.open) {
|
|
1872
|
+
this.addEventListeners();
|
|
1873
|
+
} else {
|
|
1874
|
+
this.removeEventListeners();
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
}, {
|
|
1878
|
+
key: "addEventListeners",
|
|
1879
|
+
value: function addEventListeners() {
|
|
1880
|
+
document.addEventListener("mouseup", this.handleInteract);
|
|
1881
|
+
document.addEventListener("touchend", this.handleInteract);
|
|
1882
|
+
}
|
|
1883
|
+
}, {
|
|
1884
|
+
key: "removeEventListeners",
|
|
1885
|
+
value: function removeEventListeners() {
|
|
1886
|
+
document.removeEventListener("mouseup", this.handleInteract);
|
|
1887
|
+
document.removeEventListener("touchend", this.handleInteract);
|
|
1888
|
+
}
|
|
1889
|
+
}, {
|
|
1890
|
+
key: "scheduleToFocusCurrentItem",
|
|
1891
|
+
value: function scheduleToFocusCurrentItem() {
|
|
1892
|
+
var _this3 = this;
|
|
1893
|
+
|
|
1894
|
+
// wait for windowed items to be recalculated
|
|
1895
|
+
this.props.schedule.animationFrame(function () {
|
|
1896
|
+
return _this3.focusCurrentItem();
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
}, {
|
|
1900
|
+
key: "focusCurrentItem",
|
|
1901
|
+
value: function focusCurrentItem() {
|
|
1902
|
+
var node = ReactDOM.findDOMNode(this.state.itemRefs[this.focusedIndex].ref.current);
|
|
1903
|
+
|
|
1904
|
+
if (node) {
|
|
1905
|
+
node.focus(); // Keep track of the original index of the newly focused item.
|
|
1906
|
+
// To be used if the set of focusable items in the menu changes
|
|
1907
|
+
|
|
1908
|
+
this.focusedOriginalIndex = this.state.itemRefs[this.focusedIndex].originalIndex;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
}, {
|
|
1912
|
+
key: "focusPreviousItem",
|
|
1913
|
+
value: function focusPreviousItem() {
|
|
1914
|
+
if (this.focusedIndex === 0) {
|
|
1915
|
+
this.focusedIndex = this.state.itemRefs.length - 1;
|
|
1916
|
+
} else {
|
|
1917
|
+
this.focusedIndex -= 1;
|
|
1918
|
+
} // force react-window to scroll on the correct position
|
|
1919
|
+
|
|
1920
|
+
|
|
1921
|
+
if (this.listRef.current) {
|
|
1922
|
+
this.listRef.current.scrollToItem(this.focusedIndex);
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
this.scheduleToFocusCurrentItem();
|
|
1926
|
+
}
|
|
1927
|
+
}, {
|
|
1928
|
+
key: "focusNextItem",
|
|
1929
|
+
value: function focusNextItem() {
|
|
1930
|
+
if (this.focusedIndex === this.state.itemRefs.length - 1) {
|
|
1931
|
+
this.focusedIndex = 0;
|
|
1932
|
+
} else {
|
|
1933
|
+
this.focusedIndex += 1;
|
|
1934
|
+
} // force react-window to scroll on the correct position
|
|
1935
|
+
|
|
1936
|
+
|
|
1937
|
+
if (this.listRef.current) {
|
|
1938
|
+
this.listRef.current.scrollToItem(this.focusedIndex);
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
this.scheduleToFocusCurrentItem();
|
|
1942
|
+
}
|
|
1943
|
+
}, {
|
|
1944
|
+
key: "restoreTabOrder",
|
|
1945
|
+
value: function restoreTabOrder() {
|
|
1946
|
+
// NOTE: Because the dropdown is portalled out of its natural
|
|
1947
|
+
// position in the DOM, we need to manually return focus to the
|
|
1948
|
+
// opener element before we let the natural propagation of tab
|
|
1949
|
+
// shift the focus to the next element in the tab order.
|
|
1950
|
+
if (this.props.openerElement) {
|
|
1951
|
+
this.props.openerElement.focus();
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
}, {
|
|
1955
|
+
key: "getItemRole",
|
|
1956
|
+
value: function getItemRole() {
|
|
1957
|
+
var role = this.props.role;
|
|
1958
|
+
|
|
1959
|
+
switch (role) {
|
|
1960
|
+
case "listbox":
|
|
1961
|
+
return "option";
|
|
1962
|
+
|
|
1963
|
+
case "menu":
|
|
1964
|
+
return "menuitem";
|
|
1965
|
+
|
|
1966
|
+
default:
|
|
1967
|
+
throw new Error("Expected \"listbox\" or \"menu\" for role, but receieved \"".concat(role, "\" instead."));
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
}, {
|
|
1971
|
+
key: "maybeRenderNoResults",
|
|
1972
|
+
value: function maybeRenderNoResults() {
|
|
1973
|
+
var _this$props5 = this.props,
|
|
1974
|
+
items = _this$props5.items,
|
|
1975
|
+
onSearchTextChanged = _this$props5.onSearchTextChanged,
|
|
1976
|
+
searchText = _this$props5.searchText,
|
|
1977
|
+
noResults = _this$props5.labels.noResults;
|
|
1978
|
+
var showSearchTextInput = !!onSearchTextChanged && typeof searchText === "string";
|
|
1979
|
+
var includeSearchCount = showSearchTextInput ? 1 : 0; // Verify if there are items to be rendered or not
|
|
1980
|
+
|
|
1981
|
+
var numResults = items.length - includeSearchCount;
|
|
1982
|
+
|
|
1983
|
+
if (numResults === 0) {
|
|
1984
|
+
return /*#__PURE__*/createElement(LabelMedium, {
|
|
1985
|
+
style: styles$6.noResult,
|
|
1986
|
+
testId: "dropdown-core-no-results"
|
|
1987
|
+
}, noResults);
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
return null;
|
|
1991
|
+
}
|
|
1992
|
+
/**
|
|
1993
|
+
* Process the items and wrap them into an array that react-window can
|
|
1994
|
+
* interpret
|
|
1995
|
+
*/
|
|
1996
|
+
|
|
1997
|
+
}, {
|
|
1998
|
+
key: "parseItemsList",
|
|
1999
|
+
value: function parseItemsList() {
|
|
2000
|
+
var _this4 = this;
|
|
2001
|
+
|
|
2002
|
+
var focusCounter = 0;
|
|
2003
|
+
var itemRole = this.getItemRole();
|
|
2004
|
+
return this.props.items.map(function (item, index) {
|
|
2005
|
+
if (!SeparatorItem.isClassOf(item.component) && item.focusable) {
|
|
2006
|
+
focusCounter += 1;
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
var focusIndex = focusCounter - 1;
|
|
2010
|
+
|
|
2011
|
+
if (SearchTextInput.isClassOf(item.component)) {
|
|
2012
|
+
return _objectSpread2(_objectSpread2({}, item), {}, {
|
|
2013
|
+
// override to avoid losing focus when pressing a key
|
|
2014
|
+
onClick: function onClick() {
|
|
2015
|
+
_this4.handleClickFocus(0);
|
|
2016
|
+
|
|
2017
|
+
_this4.focusCurrentItem();
|
|
2018
|
+
},
|
|
2019
|
+
populatedProps: {
|
|
2020
|
+
style: searchInputStyle,
|
|
2021
|
+
// pass the current ref down to the input element
|
|
2022
|
+
itemRef: _this4.state.itemRefs[focusIndex] ? _this4.state.itemRefs[focusIndex].ref : null
|
|
2023
|
+
}
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
return _objectSpread2(_objectSpread2({}, item), {}, {
|
|
2028
|
+
role: itemRole,
|
|
2029
|
+
ref: item.focusable ? _this4.state.itemRefs[focusIndex] ? _this4.state.itemRefs[focusIndex].ref : null : null,
|
|
2030
|
+
onClick: function onClick() {
|
|
2031
|
+
_this4.handleClickFocus(focusIndex);
|
|
2032
|
+
|
|
2033
|
+
if (item.component.props.onClick) {
|
|
2034
|
+
item.component.props.onClick();
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
if (item.populatedProps.onClick) {
|
|
2038
|
+
item.populatedProps.onClick();
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
});
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
}, {
|
|
2045
|
+
key: "renderItems",
|
|
2046
|
+
value: function renderItems(outOfBoundaries) {
|
|
2047
|
+
var _this$props6 = this.props,
|
|
2048
|
+
dropdownStyle = _this$props6.dropdownStyle,
|
|
2049
|
+
light = _this$props6.light,
|
|
2050
|
+
openerElement = _this$props6.openerElement; // The dropdown width is at least the width of the opener.
|
|
2051
|
+
// It's only used if the element exists in the DOM
|
|
2052
|
+
|
|
2053
|
+
var openerStyle = openerElement && window.getComputedStyle(openerElement);
|
|
2054
|
+
var minDropdownWidth = openerStyle ? openerStyle.getPropertyValue("width") : 0; // preprocess items data to pass it to the renderer
|
|
2055
|
+
|
|
2056
|
+
var itemsList = this.parseItemsList();
|
|
2057
|
+
return /*#__PURE__*/createElement(View // Stop propagation to prevent the mouseup listener on the
|
|
2058
|
+
// document from closing the menu.
|
|
2059
|
+
, {
|
|
2060
|
+
onMouseUp: this.handleDropdownMouseUp,
|
|
2061
|
+
role: this.props.role,
|
|
2062
|
+
style: [styles$6.dropdown, light && styles$6.light, outOfBoundaries && styles$6.hidden, {
|
|
2063
|
+
minWidth: minDropdownWidth
|
|
2064
|
+
}, dropdownStyle]
|
|
2065
|
+
}, /*#__PURE__*/createElement(DropdownCoreVirtualized$1, {
|
|
2066
|
+
data: itemsList,
|
|
2067
|
+
listRef: this.listRef
|
|
2068
|
+
}), this.maybeRenderNoResults());
|
|
2069
|
+
}
|
|
2070
|
+
}, {
|
|
2071
|
+
key: "renderDropdown",
|
|
2072
|
+
value: function renderDropdown() {
|
|
2073
|
+
var _this5 = this;
|
|
2074
|
+
|
|
2075
|
+
var _this$props7 = this.props,
|
|
2076
|
+
alignment = _this$props7.alignment,
|
|
2077
|
+
openerElement = _this$props7.openerElement; // If we are in a modal, we find where we should be portalling the menu
|
|
2078
|
+
// by using the helper function from the modal package on the opener
|
|
2079
|
+
// element.
|
|
2080
|
+
// If we are not in a modal, we use body as the location to portal to.
|
|
2081
|
+
|
|
2082
|
+
var modalHost = maybeGetPortalMountedModalHostElement(openerElement) || document.querySelector("body");
|
|
2083
|
+
|
|
2084
|
+
if (modalHost) {
|
|
2085
|
+
return ReactDOM.createPortal( /*#__PURE__*/createElement(Popper, {
|
|
2086
|
+
innerRef: function innerRef(node) {
|
|
2087
|
+
if (node) {
|
|
2088
|
+
_this5.popperElement = node;
|
|
2089
|
+
}
|
|
2090
|
+
},
|
|
2091
|
+
referenceElement: this.props.openerElement,
|
|
2092
|
+
placement: alignment === "left" ? "bottom-start" : "bottom-end",
|
|
2093
|
+
modifiers: {
|
|
2094
|
+
wbVisibility: visibilityModifierDefaultConfig,
|
|
2095
|
+
preventOverflow: {
|
|
2096
|
+
boundariesElement: "viewport",
|
|
2097
|
+
escapeWithReference: true
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
}, function (_ref) {
|
|
2101
|
+
var placement = _ref.placement,
|
|
2102
|
+
ref = _ref.ref,
|
|
2103
|
+
style = _ref.style,
|
|
2104
|
+
outOfBoundaries = _ref.outOfBoundaries;
|
|
2105
|
+
|
|
2106
|
+
// For some reason react-popper includes `pointerEvents: "none"`
|
|
2107
|
+
// in the `style` it passes to us, but only when running the tests.
|
|
2108
|
+
var _ = style.pointerEvents,
|
|
2109
|
+
restStyle = _objectWithoutProperties(style, ["pointerEvents"]);
|
|
2110
|
+
|
|
2111
|
+
return /*#__PURE__*/createElement("div", {
|
|
2112
|
+
ref: ref,
|
|
2113
|
+
style: restStyle,
|
|
2114
|
+
"data-placement": placement
|
|
2115
|
+
}, _this5.renderItems(outOfBoundaries));
|
|
2116
|
+
}), modalHost);
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
return null;
|
|
2120
|
+
}
|
|
2121
|
+
}, {
|
|
2122
|
+
key: "render",
|
|
2123
|
+
value: function render() {
|
|
2124
|
+
var _this$props8 = this.props,
|
|
2125
|
+
open = _this$props8.open,
|
|
2126
|
+
opener = _this$props8.opener,
|
|
2127
|
+
style = _this$props8.style,
|
|
2128
|
+
className = _this$props8.className;
|
|
2129
|
+
return /*#__PURE__*/createElement(View, {
|
|
2130
|
+
onKeyDown: this.handleKeyDown,
|
|
2131
|
+
onKeyUp: this.handleKeyUp,
|
|
2132
|
+
style: [styles$6.menuWrapper, style],
|
|
2133
|
+
className: className
|
|
2134
|
+
}, opener, open && this.renderDropdown());
|
|
2135
|
+
}
|
|
2136
|
+
}]);
|
|
2137
|
+
|
|
2138
|
+
return DropdownCore;
|
|
2139
|
+
}(Component);
|
|
2140
|
+
|
|
2141
|
+
_defineProperty(DropdownCore, "defaultProps", {
|
|
2142
|
+
alignment: "left",
|
|
2143
|
+
initialFocusedIndex: 0,
|
|
2144
|
+
labels: {
|
|
2145
|
+
noResults: defaultLabels.noResults
|
|
2146
|
+
},
|
|
2147
|
+
light: false
|
|
2148
|
+
});
|
|
2149
|
+
|
|
2150
|
+
var styles$6 = StyleSheet.create({
|
|
2151
|
+
menuWrapper: {
|
|
2152
|
+
width: "fit-content"
|
|
2153
|
+
},
|
|
2154
|
+
dropdown: {
|
|
2155
|
+
backgroundColor: Color.white,
|
|
2156
|
+
borderRadius: 4,
|
|
2157
|
+
paddingTop: Spacing.xxxSmall_4,
|
|
2158
|
+
paddingBottom: Spacing.xxxSmall_4,
|
|
2159
|
+
border: "solid 1px ".concat(Color.offBlack16),
|
|
2160
|
+
boxShadow: "0px 8px 8px 0px ".concat(fade(Color.offBlack, 0.1)),
|
|
2161
|
+
overflowY: "auto"
|
|
2162
|
+
},
|
|
2163
|
+
light: {
|
|
2164
|
+
// Pretty much just remove the border
|
|
2165
|
+
border: "none"
|
|
2166
|
+
},
|
|
2167
|
+
hidden: {
|
|
2168
|
+
visibility: "hidden"
|
|
2169
|
+
},
|
|
2170
|
+
noResult: {
|
|
2171
|
+
color: Color.offBlack64,
|
|
2172
|
+
alignSelf: "center",
|
|
2173
|
+
marginTop: Spacing.xxSmall_6
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
var DropdownCore$1 = withActionScheduler(DropdownCore);
|
|
2177
|
+
|
|
2178
|
+
var StyledButton$1 = addStyle("button");
|
|
2179
|
+
/**
|
|
2180
|
+
* Although this component shares a lot with ButtonCore there are a couple
|
|
2181
|
+
* of differences:
|
|
2182
|
+
* - the down caret icon appears on the right instead of the left
|
|
2183
|
+
* - the down caret icon is smaller that the one that would be used by ButtonCore
|
|
2184
|
+
*/
|
|
2185
|
+
|
|
2186
|
+
var ActionMenuOpenerCore = /*#__PURE__*/function (_React$Component) {
|
|
2187
|
+
_inherits(ActionMenuOpenerCore, _React$Component);
|
|
2188
|
+
|
|
2189
|
+
var _super = _createSuper(ActionMenuOpenerCore);
|
|
2190
|
+
|
|
2191
|
+
function ActionMenuOpenerCore() {
|
|
2192
|
+
_classCallCheck(this, ActionMenuOpenerCore);
|
|
2193
|
+
|
|
2194
|
+
return _super.apply(this, arguments);
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
_createClass(ActionMenuOpenerCore, [{
|
|
2198
|
+
key: "render",
|
|
2199
|
+
value: function render() {
|
|
2200
|
+
var _this$props = this.props,
|
|
2201
|
+
children = _this$props.children,
|
|
2202
|
+
disabledProp = _this$props.disabled,
|
|
2203
|
+
focused = _this$props.focused,
|
|
2204
|
+
hovered = _this$props.hovered,
|
|
2205
|
+
pressed = _this$props.pressed,
|
|
2206
|
+
_ = _this$props.waiting,
|
|
2207
|
+
testId = _this$props.testId,
|
|
2208
|
+
opened = _this$props.opened,
|
|
2209
|
+
ariaLabel = _this$props["aria-label"],
|
|
2210
|
+
restProps = _objectWithoutProperties(_this$props, ["children", "disabled", "focused", "hovered", "pressed", "waiting", "testId", "opened", "aria-label"]);
|
|
2211
|
+
|
|
2212
|
+
var buttonColor = SemanticColor.controlDefault;
|
|
2213
|
+
|
|
2214
|
+
var buttonStyles = _generateStyles(buttonColor);
|
|
2215
|
+
|
|
2216
|
+
var disabled = disabledProp;
|
|
2217
|
+
var defaultStyle = [sharedStyles.shared, disabled && sharedStyles.disabled, buttonStyles.default, disabled && buttonStyles.disabled, !disabled && pressed && buttonStyles.active];
|
|
2218
|
+
var label = /*#__PURE__*/createElement(LabelLarge, {
|
|
2219
|
+
style: sharedStyles.text
|
|
2220
|
+
}, children);
|
|
2221
|
+
return /*#__PURE__*/createElement(StyledButton$1, _extends({
|
|
2222
|
+
"aria-expanded": opened ? "true" : "false",
|
|
2223
|
+
"aria-haspopup": "menu",
|
|
2224
|
+
"aria-label": ariaLabel,
|
|
2225
|
+
disabled: disabled,
|
|
2226
|
+
style: defaultStyle,
|
|
2227
|
+
type: "button"
|
|
2228
|
+
}, restProps, {
|
|
2229
|
+
"data-test-id": testId
|
|
2230
|
+
}), /*#__PURE__*/createElement(View, {
|
|
2231
|
+
style: !disabled && (hovered || focused) && buttonStyles.focus
|
|
2232
|
+
}, label), /*#__PURE__*/createElement(Strut, {
|
|
2233
|
+
size: Spacing.xxxSmall_4
|
|
2234
|
+
}), /*#__PURE__*/createElement(Icon, {
|
|
2235
|
+
size: "small",
|
|
2236
|
+
color: "currentColor",
|
|
2237
|
+
icon: icons.caretDown
|
|
2238
|
+
}));
|
|
2239
|
+
}
|
|
2240
|
+
}]);
|
|
2241
|
+
|
|
2242
|
+
return ActionMenuOpenerCore;
|
|
2243
|
+
}(Component);
|
|
2244
|
+
|
|
2245
|
+
_defineProperty(ActionMenuOpenerCore, "contextTypes", {
|
|
2246
|
+
router: any
|
|
2247
|
+
});
|
|
2248
|
+
var sharedStyles = StyleSheet.create({
|
|
2249
|
+
shared: {
|
|
2250
|
+
position: "relative",
|
|
2251
|
+
display: "inline-flex",
|
|
2252
|
+
alignItems: "center",
|
|
2253
|
+
justifyContent: "center",
|
|
2254
|
+
height: DROPDOWN_ITEM_HEIGHT,
|
|
2255
|
+
border: "none",
|
|
2256
|
+
borderRadius: Spacing.xxxSmall_4,
|
|
2257
|
+
cursor: "pointer",
|
|
2258
|
+
outline: "none",
|
|
2259
|
+
textDecoration: "none",
|
|
2260
|
+
boxSizing: "border-box",
|
|
2261
|
+
// This removes the 300ms click delay on mobile browsers by indicating that
|
|
2262
|
+
// "double-tap to zoom" shouldn't be used on this element.
|
|
2263
|
+
touchAction: "manipulation",
|
|
2264
|
+
":focus": {
|
|
2265
|
+
// Mobile: Removes a blue highlight style shown when the user clicks a button
|
|
2266
|
+
WebkitTapHighlightColor: "rgba(0,0,0,0)"
|
|
2267
|
+
}
|
|
2268
|
+
},
|
|
2269
|
+
disabled: {
|
|
2270
|
+
cursor: "auto"
|
|
2271
|
+
},
|
|
2272
|
+
small: {
|
|
2273
|
+
height: Spacing.xLarge_32
|
|
2274
|
+
},
|
|
2275
|
+
text: {
|
|
2276
|
+
textAlign: "left",
|
|
2277
|
+
display: "inline-block",
|
|
2278
|
+
alignItems: "center",
|
|
2279
|
+
fontWeight: "bold",
|
|
2280
|
+
userSelect: "none",
|
|
2281
|
+
whiteSpace: "nowrap",
|
|
2282
|
+
overflow: "hidden",
|
|
2283
|
+
textOverflow: "ellipsis",
|
|
2284
|
+
pointerEvents: "none" // fix Safari bug where the browser was eating mouse events
|
|
2285
|
+
|
|
2286
|
+
},
|
|
2287
|
+
hiddenText: {
|
|
2288
|
+
visibility: "hidden"
|
|
2289
|
+
},
|
|
2290
|
+
spinner: {
|
|
2291
|
+
position: "absolute"
|
|
2292
|
+
}
|
|
2293
|
+
});
|
|
2294
|
+
var styles$7 = {};
|
|
2295
|
+
|
|
2296
|
+
var _generateStyles = function _generateStyles(color) {
|
|
2297
|
+
var buttonType = color;
|
|
2298
|
+
|
|
2299
|
+
if (styles$7[buttonType]) {
|
|
2300
|
+
return styles$7[buttonType];
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
var offBlack32 = Color.offBlack32;
|
|
2304
|
+
var activeColor = mix(offBlack32, color);
|
|
2305
|
+
var newStyles = {};
|
|
2306
|
+
newStyles = {
|
|
2307
|
+
default: {
|
|
2308
|
+
background: "none",
|
|
2309
|
+
color: color
|
|
2310
|
+
},
|
|
2311
|
+
focus: {
|
|
2312
|
+
":after": {
|
|
2313
|
+
content: "''",
|
|
2314
|
+
position: "absolute",
|
|
2315
|
+
height: 2,
|
|
2316
|
+
left: 0,
|
|
2317
|
+
right: 0,
|
|
2318
|
+
bottom: -1,
|
|
2319
|
+
background: "currentColor",
|
|
2320
|
+
borderRadius: 2
|
|
2321
|
+
}
|
|
2322
|
+
},
|
|
2323
|
+
active: {
|
|
2324
|
+
color: activeColor
|
|
2325
|
+
},
|
|
2326
|
+
disabled: {
|
|
2327
|
+
color: offBlack32,
|
|
2328
|
+
cursor: "default"
|
|
2329
|
+
}
|
|
2330
|
+
};
|
|
2331
|
+
styles$7[buttonType] = StyleSheet.create(newStyles);
|
|
2332
|
+
return styles$7[buttonType];
|
|
2333
|
+
};
|
|
2334
|
+
|
|
2335
|
+
/**
|
|
2336
|
+
* A menu that consists of various types of items.
|
|
2337
|
+
*/
|
|
2338
|
+
var ActionMenu = /*#__PURE__*/function (_React$Component) {
|
|
2339
|
+
_inherits(ActionMenu, _React$Component);
|
|
2340
|
+
|
|
2341
|
+
var _super = _createSuper(ActionMenu);
|
|
2342
|
+
|
|
2343
|
+
function ActionMenu() {
|
|
2344
|
+
var _this;
|
|
2345
|
+
|
|
2346
|
+
_classCallCheck(this, ActionMenu);
|
|
2347
|
+
|
|
2348
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
2349
|
+
args[_key] = arguments[_key];
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
_this = _super.call.apply(_super, [this].concat(args));
|
|
2353
|
+
|
|
2354
|
+
_defineProperty(_assertThisInitialized(_this), "openerElement", void 0);
|
|
2355
|
+
|
|
2356
|
+
_defineProperty(_assertThisInitialized(_this), "state", {
|
|
2357
|
+
opened: false
|
|
2358
|
+
});
|
|
2359
|
+
|
|
2360
|
+
_defineProperty(_assertThisInitialized(_this), "handleItemSelected", function () {
|
|
2361
|
+
// close menu
|
|
2362
|
+
_this.handleOpenChanged(false); // Bring focus back to the opener element.
|
|
2363
|
+
|
|
2364
|
+
|
|
2365
|
+
if (_this.openerElement) {
|
|
2366
|
+
_this.openerElement.focus();
|
|
2367
|
+
}
|
|
2368
|
+
});
|
|
2369
|
+
|
|
2370
|
+
_defineProperty(_assertThisInitialized(_this), "handleOpenChanged", function (opened) {
|
|
2371
|
+
_this.setState({
|
|
2372
|
+
opened: opened
|
|
2373
|
+
});
|
|
2374
|
+
|
|
2375
|
+
if (_this.props.onToggle) {
|
|
2376
|
+
_this.props.onToggle(opened);
|
|
2377
|
+
}
|
|
2378
|
+
});
|
|
2379
|
+
|
|
2380
|
+
_defineProperty(_assertThisInitialized(_this), "handleOptionSelected", function (selectedValue) {
|
|
2381
|
+
var _this$props = _this.props,
|
|
2382
|
+
onChange = _this$props.onChange,
|
|
2383
|
+
selectedValues = _this$props.selectedValues; // If either of these are not defined, return.
|
|
2384
|
+
|
|
2385
|
+
if (!onChange || !selectedValues) {
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
if (selectedValues.includes(selectedValue)) {
|
|
2390
|
+
var index = selectedValues.indexOf(selectedValue);
|
|
2391
|
+
var updatedSelection = [].concat(_toConsumableArray(selectedValues.slice(0, index)), _toConsumableArray(selectedValues.slice(index + 1)));
|
|
2392
|
+
onChange(updatedSelection);
|
|
2393
|
+
} else {
|
|
2394
|
+
// Item was newly selected
|
|
2395
|
+
onChange([].concat(_toConsumableArray(selectedValues), [selectedValue]));
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
_this.handleItemSelected();
|
|
2399
|
+
});
|
|
2400
|
+
|
|
2401
|
+
_defineProperty(_assertThisInitialized(_this), "handleOpenerRef", function (node) {
|
|
2402
|
+
_this.openerElement = ReactDOM.findDOMNode(node);
|
|
2403
|
+
});
|
|
2404
|
+
|
|
2405
|
+
_defineProperty(_assertThisInitialized(_this), "handleClick", function (e) {
|
|
2406
|
+
_this.handleOpenChanged(!_this.state.opened);
|
|
2407
|
+
});
|
|
2408
|
+
|
|
2409
|
+
return _this;
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
_createClass(ActionMenu, [{
|
|
2413
|
+
key: "getMenuItems",
|
|
2414
|
+
value: function getMenuItems() {
|
|
2415
|
+
var _this2 = this;
|
|
2416
|
+
|
|
2417
|
+
var _this$props2 = this.props,
|
|
2418
|
+
children = _this$props2.children,
|
|
2419
|
+
selectedValues = _this$props2.selectedValues;
|
|
2420
|
+
var allChildren = Children.toArray(children).filter(Boolean); // verify if there's at least one OptionItem element to indent the
|
|
2421
|
+
// possible Action items
|
|
2422
|
+
|
|
2423
|
+
var isOptionItemIncluded = allChildren.some(function (item) {
|
|
2424
|
+
return OptionItem.isClassOf(item);
|
|
2425
|
+
});
|
|
2426
|
+
return allChildren.map(function (item) {
|
|
2427
|
+
var _item$props = item.props,
|
|
2428
|
+
value = _item$props.value,
|
|
2429
|
+
disabled = _item$props.disabled;
|
|
2430
|
+
var itemObject = {
|
|
2431
|
+
component: item,
|
|
2432
|
+
focusable: ActionItem.isClassOf(item) || OptionItem.isClassOf(item) ? !disabled : false,
|
|
2433
|
+
populatedProps: {}
|
|
2434
|
+
};
|
|
2435
|
+
|
|
2436
|
+
if (ActionItem.isClassOf(item)) {
|
|
2437
|
+
return _objectSpread2(_objectSpread2({}, itemObject), {}, {
|
|
2438
|
+
populatedProps: {
|
|
2439
|
+
indent: isOptionItemIncluded,
|
|
2440
|
+
onClick: _this2.handleItemSelected
|
|
2441
|
+
}
|
|
2442
|
+
});
|
|
2443
|
+
} else if (OptionItem.isClassOf(item)) {
|
|
2444
|
+
return _objectSpread2(_objectSpread2({}, itemObject), {}, {
|
|
2445
|
+
populatedProps: {
|
|
2446
|
+
onToggle: _this2.handleOptionSelected,
|
|
2447
|
+
selected: selectedValues ? selectedValues.includes(value) : false,
|
|
2448
|
+
variant: "check"
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2451
|
+
} else {
|
|
2452
|
+
return itemObject;
|
|
2453
|
+
}
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
}, {
|
|
2457
|
+
key: "renderOpener",
|
|
2458
|
+
value: function renderOpener(numItems) {
|
|
2459
|
+
var _this$props3 = this.props,
|
|
2460
|
+
disabled = _this$props3.disabled,
|
|
2461
|
+
menuText = _this$props3.menuText,
|
|
2462
|
+
opened = _this$props3.opened,
|
|
2463
|
+
opener = _this$props3.opener,
|
|
2464
|
+
testId = _this$props3.testId;
|
|
2465
|
+
return /*#__PURE__*/createElement(DropdownOpener, {
|
|
2466
|
+
onClick: this.handleClick,
|
|
2467
|
+
disabled: numItems === 0 || disabled,
|
|
2468
|
+
text: menuText,
|
|
2469
|
+
ref: this.handleOpenerRef,
|
|
2470
|
+
testId: opener ? undefined : testId
|
|
2471
|
+
}, opener ? opener : function (openerProps) {
|
|
2472
|
+
var text = openerProps.text,
|
|
2473
|
+
eventState = _objectWithoutProperties(openerProps, ["text"]);
|
|
2474
|
+
|
|
2475
|
+
return /*#__PURE__*/createElement(ActionMenuOpenerCore, _extends({}, eventState, {
|
|
2476
|
+
disabled: disabled,
|
|
2477
|
+
opened: !!opened,
|
|
2478
|
+
testId: testId
|
|
2479
|
+
}), menuText);
|
|
2480
|
+
});
|
|
2481
|
+
}
|
|
2482
|
+
}, {
|
|
2483
|
+
key: "render",
|
|
2484
|
+
value: function render() {
|
|
2485
|
+
var _this$props4 = this.props,
|
|
2486
|
+
alignment = _this$props4.alignment,
|
|
2487
|
+
dropdownStyle = _this$props4.dropdownStyle,
|
|
2488
|
+
style = _this$props4.style,
|
|
2489
|
+
className = _this$props4.className;
|
|
2490
|
+
var items = this.getMenuItems();
|
|
2491
|
+
var dropdownOpener = this.renderOpener(items.length);
|
|
2492
|
+
return /*#__PURE__*/createElement(DropdownCore$1, {
|
|
2493
|
+
role: "menu",
|
|
2494
|
+
style: style,
|
|
2495
|
+
className: className,
|
|
2496
|
+
opener: dropdownOpener,
|
|
2497
|
+
alignment: alignment,
|
|
2498
|
+
open: this.state.opened,
|
|
2499
|
+
items: items,
|
|
2500
|
+
openerElement: this.openerElement,
|
|
2501
|
+
onOpenChanged: this.handleOpenChanged,
|
|
2502
|
+
dropdownStyle: [styles$8.menuTopSpace, dropdownStyle]
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
}], [{
|
|
2506
|
+
key: "getDerivedStateFromProps",
|
|
2507
|
+
|
|
2508
|
+
/**
|
|
2509
|
+
* Used to sync the `opened` state when this component acts as a controlled
|
|
2510
|
+
* component
|
|
2511
|
+
*/
|
|
2512
|
+
value: function getDerivedStateFromProps(props, state) {
|
|
2513
|
+
return {
|
|
2514
|
+
opened: typeof props.opened === "boolean" ? props.opened : state.opened
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
}]);
|
|
2518
|
+
|
|
2519
|
+
return ActionMenu;
|
|
2520
|
+
}(Component);
|
|
2521
|
+
|
|
2522
|
+
_defineProperty(ActionMenu, "defaultProps", {
|
|
2523
|
+
alignment: "left",
|
|
2524
|
+
disabled: false
|
|
2525
|
+
});
|
|
2526
|
+
var styles$8 = StyleSheet.create({
|
|
2527
|
+
caret: {
|
|
2528
|
+
marginLeft: 4
|
|
2529
|
+
},
|
|
2530
|
+
// The design calls for additional offset around the opener.
|
|
2531
|
+
opener: {
|
|
2532
|
+
whiteSpace: "nowrap",
|
|
2533
|
+
userSelect: "none",
|
|
2534
|
+
overflow: "hidden",
|
|
2535
|
+
textOverflow: "ellipsis"
|
|
2536
|
+
},
|
|
2537
|
+
// This is to adjust the space between the menu and the opener.
|
|
2538
|
+
menuTopSpace: {
|
|
2539
|
+
top: -4
|
|
2540
|
+
}
|
|
2541
|
+
});
|
|
2542
|
+
|
|
2543
|
+
var StyledButton$2 = addStyle("button");
|
|
2544
|
+
var blue$3 = Color.blue,
|
|
2545
|
+
white$4 = Color.white,
|
|
2546
|
+
white50 = Color.white50,
|
|
2547
|
+
offBlack$3 = Color.offBlack,
|
|
2548
|
+
offBlack16$1 = Color.offBlack16,
|
|
2549
|
+
offBlack32$4 = Color.offBlack32,
|
|
2550
|
+
offBlack64 = Color.offBlack64;
|
|
2551
|
+
|
|
2552
|
+
/**
|
|
2553
|
+
* An opener that opens select boxes.
|
|
2554
|
+
*/
|
|
2555
|
+
var SelectOpener = /*#__PURE__*/function (_React$Component) {
|
|
2556
|
+
_inherits(SelectOpener, _React$Component);
|
|
2557
|
+
|
|
2558
|
+
var _super = _createSuper(SelectOpener);
|
|
2559
|
+
|
|
2560
|
+
function SelectOpener() {
|
|
2561
|
+
var _this;
|
|
2562
|
+
|
|
2563
|
+
_classCallCheck(this, SelectOpener);
|
|
2564
|
+
|
|
2565
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
2566
|
+
args[_key] = arguments[_key];
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
_this = _super.call.apply(_super, [this].concat(args));
|
|
2570
|
+
|
|
2571
|
+
_defineProperty(_assertThisInitialized(_this), "handleClick", function (e) {
|
|
2572
|
+
var open = _this.props.open;
|
|
2573
|
+
|
|
2574
|
+
_this.props.onOpenChanged(!open);
|
|
2575
|
+
});
|
|
2576
|
+
|
|
2577
|
+
return _this;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
_createClass(SelectOpener, [{
|
|
2581
|
+
key: "render",
|
|
2582
|
+
value: function render() {
|
|
2583
|
+
var _this$props = this.props,
|
|
2584
|
+
children = _this$props.children,
|
|
2585
|
+
disabled = _this$props.disabled,
|
|
2586
|
+
id = _this$props.id,
|
|
2587
|
+
isPlaceholder = _this$props.isPlaceholder,
|
|
2588
|
+
light = _this$props.light,
|
|
2589
|
+
open = _this$props.open,
|
|
2590
|
+
testId = _this$props.testId,
|
|
2591
|
+
onOpenChanged = _this$props.onOpenChanged,
|
|
2592
|
+
sharedProps = _objectWithoutProperties(_this$props, ["children", "disabled", "id", "isPlaceholder", "light", "open", "testId", "onOpenChanged"]);
|
|
2593
|
+
|
|
2594
|
+
var ClickableBehavior = getClickableBehavior(this.context.router);
|
|
2595
|
+
return /*#__PURE__*/createElement(ClickableBehavior, {
|
|
2596
|
+
disabled: disabled,
|
|
2597
|
+
onClick: this.handleClick
|
|
2598
|
+
}, function (state, childrenProps) {
|
|
2599
|
+
var stateStyles = _generateStyles$1(light, isPlaceholder);
|
|
2600
|
+
|
|
2601
|
+
var hovered = state.hovered,
|
|
2602
|
+
focused = state.focused,
|
|
2603
|
+
pressed = state.pressed; // The icon colors are kind of fickle. This is just logic
|
|
2604
|
+
// based on the zeplin design.
|
|
2605
|
+
|
|
2606
|
+
var iconColor = light ? disabled || pressed ? "currentColor" : white$4 : disabled ? offBlack32$4 : offBlack64;
|
|
2607
|
+
var style = [styles$9.shared, stateStyles.default, disabled && stateStyles.disabled, !disabled && (pressed ? stateStyles.active : (hovered || focused) && stateStyles.focus)];
|
|
2608
|
+
return /*#__PURE__*/createElement(StyledButton$2, _extends({}, sharedProps, {
|
|
2609
|
+
"aria-expanded": open ? "true" : "false",
|
|
2610
|
+
"aria-haspopup": "listbox",
|
|
2611
|
+
"data-test-id": testId,
|
|
2612
|
+
disabled: disabled,
|
|
2613
|
+
id: id,
|
|
2614
|
+
style: style,
|
|
2615
|
+
type: "button"
|
|
2616
|
+
}, childrenProps), /*#__PURE__*/createElement(LabelMedium, {
|
|
2617
|
+
style: styles$9.text
|
|
2618
|
+
}, children), /*#__PURE__*/createElement(Icon, {
|
|
2619
|
+
icon: icons.caretDown,
|
|
2620
|
+
color: iconColor,
|
|
2621
|
+
size: "small",
|
|
2622
|
+
style: styles$9.caret,
|
|
2623
|
+
"aria-hidden": "true"
|
|
2624
|
+
}));
|
|
2625
|
+
});
|
|
2626
|
+
}
|
|
2627
|
+
}]);
|
|
2628
|
+
|
|
2629
|
+
return SelectOpener;
|
|
2630
|
+
}(Component);
|
|
2631
|
+
|
|
2632
|
+
_defineProperty(SelectOpener, "contextTypes", {
|
|
2633
|
+
router: any
|
|
2634
|
+
});
|
|
2635
|
+
|
|
2636
|
+
_defineProperty(SelectOpener, "defaultProps", {
|
|
2637
|
+
disabled: false,
|
|
2638
|
+
light: false,
|
|
2639
|
+
isPlaceholder: false
|
|
2640
|
+
});
|
|
2641
|
+
var buttonRadius = 4;
|
|
2642
|
+
var styles$9 = StyleSheet.create({
|
|
2643
|
+
// TODO: Dedupe with Button styles
|
|
2644
|
+
shared: {
|
|
2645
|
+
position: "relative",
|
|
2646
|
+
display: "inline-flex",
|
|
2647
|
+
alignItems: "center",
|
|
2648
|
+
justifyContent: "space-between",
|
|
2649
|
+
color: offBlack$3,
|
|
2650
|
+
height: DROPDOWN_ITEM_HEIGHT,
|
|
2651
|
+
// This asymmetry arises from the Icon on the right side, which has
|
|
2652
|
+
// extra padding built in. To have the component look more balanced,
|
|
2653
|
+
// we need to take off some paddingRight here.
|
|
2654
|
+
paddingLeft: 16,
|
|
2655
|
+
paddingRight: 12,
|
|
2656
|
+
borderWidth: 0,
|
|
2657
|
+
borderRadius: buttonRadius,
|
|
2658
|
+
borderStyle: "solid",
|
|
2659
|
+
outline: "none",
|
|
2660
|
+
textDecoration: "none",
|
|
2661
|
+
boxSizing: "border-box",
|
|
2662
|
+
whiteSpace: "nowrap",
|
|
2663
|
+
// This removes the 300ms click delay on mobile browsers by indicating that
|
|
2664
|
+
// "double-tap to zoom" shouldn't be used on this element.
|
|
2665
|
+
touchAction: "manipulation"
|
|
2666
|
+
},
|
|
2667
|
+
text: {
|
|
2668
|
+
marginRight: Spacing.xSmall_8,
|
|
2669
|
+
whiteSpace: "nowrap",
|
|
2670
|
+
userSelect: "none",
|
|
2671
|
+
overflow: "hidden",
|
|
2672
|
+
textOverflow: "ellipsis"
|
|
2673
|
+
},
|
|
2674
|
+
caret: {
|
|
2675
|
+
minWidth: 16
|
|
2676
|
+
}
|
|
2677
|
+
}); // These values are default padding (16 and 12) minus 1, because
|
|
2678
|
+
// changing the borderWidth to 2 messes up the button width
|
|
2679
|
+
// and causes it to move a couple pixels. This fixes that.
|
|
2680
|
+
|
|
2681
|
+
var adjustedPaddingLeft = 16 - 1;
|
|
2682
|
+
var adjustedPaddingRight = 12 - 1;
|
|
2683
|
+
var stateStyles = {};
|
|
2684
|
+
|
|
2685
|
+
var _generateStyles$1 = function _generateStyles(light, placeholder) {
|
|
2686
|
+
// "hash" the parameters
|
|
2687
|
+
var styleKey = "".concat(String(light), "-").concat(String(placeholder));
|
|
2688
|
+
|
|
2689
|
+
if (stateStyles[styleKey]) {
|
|
2690
|
+
return stateStyles[styleKey];
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
var newStyles = {};
|
|
2694
|
+
|
|
2695
|
+
if (light) {
|
|
2696
|
+
newStyles = {
|
|
2697
|
+
default: {
|
|
2698
|
+
backgroundColor: "transparent",
|
|
2699
|
+
color: placeholder ? white50 : white$4,
|
|
2700
|
+
borderColor: white50,
|
|
2701
|
+
borderWidth: 1
|
|
2702
|
+
},
|
|
2703
|
+
focus: {
|
|
2704
|
+
borderColor: white$4,
|
|
2705
|
+
borderWidth: 2,
|
|
2706
|
+
paddingLeft: adjustedPaddingLeft,
|
|
2707
|
+
paddingRight: adjustedPaddingRight
|
|
2708
|
+
},
|
|
2709
|
+
active: {
|
|
2710
|
+
paddingLeft: adjustedPaddingLeft,
|
|
2711
|
+
paddingRight: adjustedPaddingRight,
|
|
2712
|
+
borderColor: mix(fade(blue$3, 0.32), white$4),
|
|
2713
|
+
borderWidth: 2,
|
|
2714
|
+
color: placeholder ? mix(fade(white$4, 0.32), blue$3) : mix(fade(blue$3, 0.32), white$4),
|
|
2715
|
+
backgroundColor: mix(offBlack32$4, blue$3)
|
|
2716
|
+
},
|
|
2717
|
+
disabled: {
|
|
2718
|
+
borderColor: mix(fade(white$4, 0.32), blue$3),
|
|
2719
|
+
color: mix(fade(white$4, 0.32), blue$3),
|
|
2720
|
+
cursor: "auto"
|
|
2721
|
+
}
|
|
2722
|
+
};
|
|
2723
|
+
} else {
|
|
2724
|
+
newStyles = {
|
|
2725
|
+
default: {
|
|
2726
|
+
backgroundColor: white$4,
|
|
2727
|
+
borderColor: offBlack16$1,
|
|
2728
|
+
borderWidth: 1,
|
|
2729
|
+
color: placeholder ? offBlack64 : offBlack$3
|
|
2730
|
+
},
|
|
2731
|
+
focus: {
|
|
2732
|
+
borderColor: blue$3,
|
|
2733
|
+
borderWidth: 2,
|
|
2734
|
+
paddingLeft: adjustedPaddingLeft,
|
|
2735
|
+
paddingRight: adjustedPaddingRight
|
|
2736
|
+
},
|
|
2737
|
+
active: {
|
|
2738
|
+
background: mix(fade(blue$3, 0.32), white$4),
|
|
2739
|
+
borderColor: mix(offBlack32$4, blue$3),
|
|
2740
|
+
borderWidth: 2,
|
|
2741
|
+
paddingLeft: adjustedPaddingLeft,
|
|
2742
|
+
paddingRight: adjustedPaddingRight
|
|
2743
|
+
},
|
|
2744
|
+
disabled: {
|
|
2745
|
+
backgroundColor: "transparent",
|
|
2746
|
+
borderColor: offBlack16$1,
|
|
2747
|
+
color: offBlack64,
|
|
2748
|
+
cursor: "auto"
|
|
2749
|
+
}
|
|
2750
|
+
};
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
stateStyles[styleKey] = StyleSheet.create(newStyles);
|
|
2754
|
+
return stateStyles[styleKey];
|
|
2755
|
+
};
|
|
2756
|
+
|
|
2757
|
+
/**
|
|
2758
|
+
* The single select allows the selection of one item. Clients are responsible
|
|
2759
|
+
* for keeping track of the selected item in the select.
|
|
2760
|
+
*
|
|
2761
|
+
* The single select dropdown closes after the selection of an item. If the same
|
|
2762
|
+
* item is selected, there is no callback.
|
|
2763
|
+
*
|
|
2764
|
+
* *NOTE:* The component automatically uses
|
|
2765
|
+
* [react-window](https://github.com/bvaughn/react-window) to improve
|
|
2766
|
+
* performance when rendering these elements and is capable of handling many
|
|
2767
|
+
* hundreds of items without performance problems.
|
|
2768
|
+
*
|
|
2769
|
+
*/
|
|
2770
|
+
var SingleSelect = /*#__PURE__*/function (_React$Component) {
|
|
2771
|
+
_inherits(SingleSelect, _React$Component);
|
|
2772
|
+
|
|
2773
|
+
var _super = _createSuper(SingleSelect);
|
|
2774
|
+
|
|
2775
|
+
function SingleSelect(props) {
|
|
2776
|
+
var _this;
|
|
2777
|
+
|
|
2778
|
+
_classCallCheck(this, SingleSelect);
|
|
2779
|
+
|
|
2780
|
+
_this = _super.call(this, props);
|
|
2781
|
+
|
|
2782
|
+
_defineProperty(_assertThisInitialized(_this), "selectedIndex", void 0);
|
|
2783
|
+
|
|
2784
|
+
_defineProperty(_assertThisInitialized(_this), "handleOpenChanged", function (opened) {
|
|
2785
|
+
_this.setState({
|
|
2786
|
+
open: opened,
|
|
2787
|
+
searchText: ""
|
|
2788
|
+
});
|
|
2789
|
+
|
|
2790
|
+
if (_this.props.onToggle) {
|
|
2791
|
+
_this.props.onToggle(opened);
|
|
2792
|
+
}
|
|
2793
|
+
});
|
|
2794
|
+
|
|
2795
|
+
_defineProperty(_assertThisInitialized(_this), "handleToggle", function (selectedValue) {
|
|
2796
|
+
// Call callback if selection changed.
|
|
2797
|
+
if (selectedValue !== _this.props.selectedValue) {
|
|
2798
|
+
_this.props.onChange(selectedValue);
|
|
2799
|
+
} // Bring focus back to the opener element.
|
|
2800
|
+
|
|
2801
|
+
|
|
2802
|
+
if (_this.state.open && _this.state.openerElement) {
|
|
2803
|
+
_this.state.openerElement.focus();
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
_this.setState({
|
|
2807
|
+
open: false // close the menu upon selection
|
|
2808
|
+
|
|
2809
|
+
});
|
|
2810
|
+
|
|
2811
|
+
if (_this.props.onToggle) {
|
|
2812
|
+
_this.props.onToggle(false);
|
|
2813
|
+
}
|
|
2814
|
+
});
|
|
2815
|
+
|
|
2816
|
+
_defineProperty(_assertThisInitialized(_this), "mapOptionItemsToDropdownItems", function (children) {
|
|
2817
|
+
// Figure out which index should receive focus when this select opens
|
|
2818
|
+
// Needs to exclude counting items that are disabled
|
|
2819
|
+
var indexCounter = 0;
|
|
2820
|
+
_this.selectedIndex = 0;
|
|
2821
|
+
return children.map(function (option) {
|
|
2822
|
+
var selectedValue = _this.props.selectedValue;
|
|
2823
|
+
var _option$props = option.props,
|
|
2824
|
+
disabled = _option$props.disabled,
|
|
2825
|
+
value = _option$props.value;
|
|
2826
|
+
var selected = selectedValue === value;
|
|
2827
|
+
|
|
2828
|
+
if (!disabled) {
|
|
2829
|
+
indexCounter += 1;
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
if (selected) {
|
|
2833
|
+
_this.selectedIndex = indexCounter;
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
return {
|
|
2837
|
+
component: option,
|
|
2838
|
+
focusable: !disabled,
|
|
2839
|
+
populatedProps: {
|
|
2840
|
+
onToggle: _this.handleToggle,
|
|
2841
|
+
selected: selected,
|
|
2842
|
+
variant: "check"
|
|
2843
|
+
}
|
|
2844
|
+
};
|
|
2845
|
+
});
|
|
2846
|
+
});
|
|
2847
|
+
|
|
2848
|
+
_defineProperty(_assertThisInitialized(_this), "handleSearchTextChanged", function (searchText) {
|
|
2849
|
+
_this.setState({
|
|
2850
|
+
searchText: searchText
|
|
2851
|
+
});
|
|
2852
|
+
});
|
|
2853
|
+
|
|
2854
|
+
_defineProperty(_assertThisInitialized(_this), "handleOpenerRef", function (node) {
|
|
2855
|
+
var openerElement = ReactDOM.findDOMNode(node);
|
|
2856
|
+
|
|
2857
|
+
_this.setState({
|
|
2858
|
+
openerElement: openerElement
|
|
2859
|
+
});
|
|
2860
|
+
});
|
|
2861
|
+
|
|
2862
|
+
_defineProperty(_assertThisInitialized(_this), "handleClick", function (e) {
|
|
2863
|
+
_this.handleOpenChanged(!_this.state.open);
|
|
2864
|
+
});
|
|
2865
|
+
|
|
2866
|
+
_this.selectedIndex = 0;
|
|
2867
|
+
_this.state = {
|
|
2868
|
+
open: false,
|
|
2869
|
+
searchText: "",
|
|
2870
|
+
openerElement: null
|
|
2871
|
+
};
|
|
2872
|
+
return _this;
|
|
2873
|
+
}
|
|
2874
|
+
/**
|
|
2875
|
+
* Used to sync the `opened` state when this component acts as a controlled
|
|
2876
|
+
* component
|
|
2877
|
+
*/
|
|
2878
|
+
|
|
2879
|
+
|
|
2880
|
+
_createClass(SingleSelect, [{
|
|
2881
|
+
key: "filterChildren",
|
|
2882
|
+
value: function filterChildren(children) {
|
|
2883
|
+
var searchText = this.state.searchText;
|
|
2884
|
+
var lowercasedSearchText = searchText.toLowerCase(); // Filter the children with the searchText if any.
|
|
2885
|
+
|
|
2886
|
+
return children.filter(function (_ref) {
|
|
2887
|
+
var props = _ref.props;
|
|
2888
|
+
return !searchText || props.label.toLowerCase().indexOf(lowercasedSearchText) > -1;
|
|
2889
|
+
});
|
|
2890
|
+
}
|
|
2891
|
+
}, {
|
|
2892
|
+
key: "getMenuItems",
|
|
2893
|
+
value: function getMenuItems(children) {
|
|
2894
|
+
var isFilterable = this.props.isFilterable; // If it's not filterable, no need to do any extra besides mapping the
|
|
2895
|
+
// option items to dropdown items.
|
|
2896
|
+
|
|
2897
|
+
return this.mapOptionItemsToDropdownItems(isFilterable ? this.filterChildren(children) : children);
|
|
2898
|
+
}
|
|
2899
|
+
}, {
|
|
2900
|
+
key: "getSearchField",
|
|
2901
|
+
value: function getSearchField() {
|
|
2902
|
+
if (!this.props.isFilterable) {
|
|
2903
|
+
return null;
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
return {
|
|
2907
|
+
component: /*#__PURE__*/createElement(SearchTextInput, {
|
|
2908
|
+
key: "search-text-input",
|
|
2909
|
+
onChange: this.handleSearchTextChanged,
|
|
2910
|
+
searchText: this.state.searchText,
|
|
2911
|
+
labels: {
|
|
2912
|
+
clearSearch: defaultLabels.clearSearch,
|
|
2913
|
+
filter: defaultLabels.filter
|
|
2914
|
+
}
|
|
2915
|
+
}),
|
|
2916
|
+
focusable: true,
|
|
2917
|
+
populatedProps: {}
|
|
2918
|
+
};
|
|
2919
|
+
}
|
|
2920
|
+
}, {
|
|
2921
|
+
key: "renderOpener",
|
|
2922
|
+
value: function renderOpener(numItems) {
|
|
2923
|
+
var _this$props = this.props,
|
|
2924
|
+
children = _this$props.children,
|
|
2925
|
+
disabled = _this$props.disabled,
|
|
2926
|
+
id = _this$props.id,
|
|
2927
|
+
light = _this$props.light,
|
|
2928
|
+
opener = _this$props.opener,
|
|
2929
|
+
placeholder = _this$props.placeholder,
|
|
2930
|
+
selectedValue = _this$props.selectedValue,
|
|
2931
|
+
testId = _this$props.testId,
|
|
2932
|
+
alignment = _this$props.alignment,
|
|
2933
|
+
dropdownStyle = _this$props.dropdownStyle,
|
|
2934
|
+
isFilterable = _this$props.isFilterable,
|
|
2935
|
+
onChange = _this$props.onChange,
|
|
2936
|
+
onToggle = _this$props.onToggle,
|
|
2937
|
+
opened = _this$props.opened,
|
|
2938
|
+
style = _this$props.style,
|
|
2939
|
+
className = _this$props.className,
|
|
2940
|
+
sharedProps = _objectWithoutProperties(_this$props, ["children", "disabled", "id", "light", "opener", "placeholder", "selectedValue", "testId", "alignment", "dropdownStyle", "isFilterable", "onChange", "onToggle", "opened", "style", "className"]);
|
|
2941
|
+
|
|
2942
|
+
var selectedItem = Children.toArray(children).find(function (option) {
|
|
2943
|
+
return option.props.value === selectedValue;
|
|
2944
|
+
}); // If nothing is selected, or if the selectedValue doesn't match any
|
|
2945
|
+
// item in the menu, use the placeholder.
|
|
2946
|
+
|
|
2947
|
+
var menuText = selectedItem ? selectedItem.props.label : placeholder;
|
|
2948
|
+
var dropdownOpener = opener ? /*#__PURE__*/createElement(DropdownOpener, {
|
|
2949
|
+
onClick: this.handleClick,
|
|
2950
|
+
disabled: numItems === 0 || disabled,
|
|
2951
|
+
ref: this.handleOpenerRef,
|
|
2952
|
+
text: menuText
|
|
2953
|
+
}, opener) : /*#__PURE__*/createElement(SelectOpener, _extends({}, sharedProps, {
|
|
2954
|
+
disabled: numItems === 0 || disabled,
|
|
2955
|
+
id: id,
|
|
2956
|
+
isPlaceholder: !selectedItem,
|
|
2957
|
+
light: light,
|
|
2958
|
+
onOpenChanged: this.handleOpenChanged,
|
|
2959
|
+
open: this.state.open,
|
|
2960
|
+
ref: this.handleOpenerRef,
|
|
2961
|
+
testId: testId
|
|
2962
|
+
}), menuText);
|
|
2963
|
+
return dropdownOpener;
|
|
2964
|
+
}
|
|
2965
|
+
}, {
|
|
2966
|
+
key: "render",
|
|
2967
|
+
value: function render() {
|
|
2968
|
+
var _this$props2 = this.props,
|
|
2969
|
+
alignment = _this$props2.alignment,
|
|
2970
|
+
children = _this$props2.children,
|
|
2971
|
+
dropdownStyle = _this$props2.dropdownStyle,
|
|
2972
|
+
isFilterable = _this$props2.isFilterable,
|
|
2973
|
+
light = _this$props2.light,
|
|
2974
|
+
style = _this$props2.style,
|
|
2975
|
+
className = _this$props2.className;
|
|
2976
|
+
var searchText = this.state.searchText;
|
|
2977
|
+
var allChildren = Children.toArray(children).filter(Boolean);
|
|
2978
|
+
var filteredItems = this.getMenuItems(allChildren);
|
|
2979
|
+
var opener = this.renderOpener(allChildren.length);
|
|
2980
|
+
var searchField = this.getSearchField();
|
|
2981
|
+
var items = searchField ? [searchField].concat(_toConsumableArray(filteredItems)) : filteredItems;
|
|
2982
|
+
return /*#__PURE__*/createElement(DropdownCore$1, {
|
|
2983
|
+
role: "listbox",
|
|
2984
|
+
alignment: alignment,
|
|
2985
|
+
dropdownStyle: [isFilterable && filterableDropdownStyle, selectDropdownStyle, dropdownStyle],
|
|
2986
|
+
initialFocusedIndex: this.selectedIndex,
|
|
2987
|
+
items: items,
|
|
2988
|
+
light: light,
|
|
2989
|
+
onOpenChanged: this.handleOpenChanged,
|
|
2990
|
+
open: this.state.open,
|
|
2991
|
+
opener: opener,
|
|
2992
|
+
openerElement: this.state.openerElement,
|
|
2993
|
+
style: style,
|
|
2994
|
+
className: className,
|
|
2995
|
+
onSearchTextChanged: isFilterable ? this.handleSearchTextChanged : null,
|
|
2996
|
+
searchText: isFilterable ? searchText : ""
|
|
2997
|
+
});
|
|
2998
|
+
}
|
|
2999
|
+
}], [{
|
|
3000
|
+
key: "getDerivedStateFromProps",
|
|
3001
|
+
value: function getDerivedStateFromProps(props, state) {
|
|
3002
|
+
return {
|
|
3003
|
+
open: typeof props.opened === "boolean" ? props.opened : state.open
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
}]);
|
|
3007
|
+
|
|
3008
|
+
return SingleSelect;
|
|
3009
|
+
}(Component);
|
|
3010
|
+
|
|
3011
|
+
_defineProperty(SingleSelect, "defaultProps", {
|
|
3012
|
+
alignment: "left",
|
|
3013
|
+
disabled: false,
|
|
3014
|
+
light: false
|
|
3015
|
+
});
|
|
3016
|
+
|
|
3017
|
+
/**
|
|
3018
|
+
* A dropdown that consists of multiple selection items. This select allows
|
|
3019
|
+
* multiple options to be selected. Clients are responsible for keeping track
|
|
3020
|
+
* of the selected items.
|
|
3021
|
+
*
|
|
3022
|
+
* The multi select stays open until closed by the user. The onChange callback
|
|
3023
|
+
* happens every time there is a change in the selection of the items.
|
|
3024
|
+
*/
|
|
3025
|
+
var MultiSelect = /*#__PURE__*/function (_React$Component) {
|
|
3026
|
+
_inherits(MultiSelect, _React$Component);
|
|
3027
|
+
|
|
3028
|
+
var _super = _createSuper(MultiSelect);
|
|
3029
|
+
|
|
3030
|
+
function MultiSelect(props) {
|
|
3031
|
+
var _this;
|
|
3032
|
+
|
|
3033
|
+
_classCallCheck(this, MultiSelect);
|
|
3034
|
+
|
|
3035
|
+
_this = _super.call(this, props);
|
|
3036
|
+
|
|
3037
|
+
_defineProperty(_assertThisInitialized(_this), "labels", void 0);
|
|
3038
|
+
|
|
3039
|
+
_defineProperty(_assertThisInitialized(_this), "handleOpenChanged", function (opened) {
|
|
3040
|
+
_this.setState({
|
|
3041
|
+
open: opened,
|
|
3042
|
+
searchText: "",
|
|
3043
|
+
lastSelectedValues: _this.props.selectedValues
|
|
3044
|
+
});
|
|
3045
|
+
|
|
3046
|
+
if (_this.props.onToggle) {
|
|
3047
|
+
_this.props.onToggle(opened);
|
|
3048
|
+
}
|
|
3049
|
+
});
|
|
3050
|
+
|
|
3051
|
+
_defineProperty(_assertThisInitialized(_this), "handleToggle", function (selectedValue) {
|
|
3052
|
+
var _this$props = _this.props,
|
|
3053
|
+
onChange = _this$props.onChange,
|
|
3054
|
+
selectedValues = _this$props.selectedValues;
|
|
3055
|
+
|
|
3056
|
+
if (selectedValues.includes(selectedValue)) {
|
|
3057
|
+
var index = selectedValues.indexOf(selectedValue);
|
|
3058
|
+
var updatedSelection = [].concat(_toConsumableArray(selectedValues.slice(0, index)), _toConsumableArray(selectedValues.slice(index + 1)));
|
|
3059
|
+
onChange(updatedSelection);
|
|
3060
|
+
} else {
|
|
3061
|
+
// Item was newly selected
|
|
3062
|
+
onChange([].concat(_toConsumableArray(selectedValues), [selectedValue]));
|
|
3063
|
+
}
|
|
3064
|
+
});
|
|
3065
|
+
|
|
3066
|
+
_defineProperty(_assertThisInitialized(_this), "handleSelectAll", function () {
|
|
3067
|
+
var _this$props2 = _this.props,
|
|
3068
|
+
children = _this$props2.children,
|
|
3069
|
+
onChange = _this$props2.onChange;
|
|
3070
|
+
var selected = Children.toArray(children).filter(Boolean).map(function (option) {
|
|
3071
|
+
return option.props.value;
|
|
3072
|
+
});
|
|
3073
|
+
onChange(selected);
|
|
3074
|
+
});
|
|
3075
|
+
|
|
3076
|
+
_defineProperty(_assertThisInitialized(_this), "handleSelectNone", function () {
|
|
3077
|
+
var onChange = _this.props.onChange;
|
|
3078
|
+
onChange([]);
|
|
3079
|
+
});
|
|
3080
|
+
|
|
3081
|
+
_defineProperty(_assertThisInitialized(_this), "mapOptionItemToDropdownItem", function (option) {
|
|
3082
|
+
var selectedValues = _this.props.selectedValues;
|
|
3083
|
+
var _option$props = option.props,
|
|
3084
|
+
disabled = _option$props.disabled,
|
|
3085
|
+
value = _option$props.value;
|
|
3086
|
+
return {
|
|
3087
|
+
component: option,
|
|
3088
|
+
focusable: !disabled,
|
|
3089
|
+
populatedProps: {
|
|
3090
|
+
onToggle: _this.handleToggle,
|
|
3091
|
+
selected: selectedValues.includes(value),
|
|
3092
|
+
variant: "checkbox"
|
|
3093
|
+
}
|
|
3094
|
+
};
|
|
3095
|
+
});
|
|
3096
|
+
|
|
3097
|
+
_defineProperty(_assertThisInitialized(_this), "handleOpenerRef", function (node) {
|
|
3098
|
+
var openerElement = ReactDOM.findDOMNode(node);
|
|
3099
|
+
|
|
3100
|
+
_this.setState({
|
|
3101
|
+
openerElement: openerElement
|
|
3102
|
+
});
|
|
3103
|
+
});
|
|
3104
|
+
|
|
3105
|
+
_defineProperty(_assertThisInitialized(_this), "handleSearchTextChanged", function (searchText) {
|
|
3106
|
+
_this.setState({
|
|
3107
|
+
searchText: searchText
|
|
3108
|
+
});
|
|
3109
|
+
});
|
|
3110
|
+
|
|
3111
|
+
_defineProperty(_assertThisInitialized(_this), "handleClick", function (e) {
|
|
3112
|
+
_this.handleOpenChanged(!_this.state.open);
|
|
3113
|
+
});
|
|
3114
|
+
|
|
3115
|
+
_this.state = {
|
|
3116
|
+
open: false,
|
|
3117
|
+
searchText: "",
|
|
3118
|
+
lastSelectedValues: [],
|
|
3119
|
+
// merge custom labels with the default ones
|
|
3120
|
+
labels: _objectSpread2(_objectSpread2({}, defaultLabels), props.labels),
|
|
3121
|
+
openerElement: null
|
|
3122
|
+
}; // merge custom labels with the default ones
|
|
3123
|
+
|
|
3124
|
+
_this.labels = _objectSpread2(_objectSpread2({}, defaultLabels), props.labels);
|
|
3125
|
+
return _this;
|
|
3126
|
+
}
|
|
3127
|
+
/**
|
|
3128
|
+
* Used to sync the `opened` state when this component acts as a controlled
|
|
3129
|
+
* component
|
|
3130
|
+
*/
|
|
3131
|
+
|
|
3132
|
+
|
|
3133
|
+
_createClass(MultiSelect, [{
|
|
3134
|
+
key: "componentDidUpdate",
|
|
3135
|
+
value: function componentDidUpdate(prevProps) {
|
|
3136
|
+
if (this.props.labels !== prevProps.labels) {
|
|
3137
|
+
// eslint-disable-next-line react/no-did-update-set-state
|
|
3138
|
+
this.setState({
|
|
3139
|
+
labels: _objectSpread2(_objectSpread2({}, this.state.labels), this.props.labels)
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
}, {
|
|
3144
|
+
key: "getMenuText",
|
|
3145
|
+
value: function getMenuText(children) {
|
|
3146
|
+
var _this$props3 = this.props,
|
|
3147
|
+
implicitAllEnabled = _this$props3.implicitAllEnabled,
|
|
3148
|
+
selectedValues = _this$props3.selectedValues;
|
|
3149
|
+
var _this$state$labels = this.state.labels,
|
|
3150
|
+
noneSelected = _this$state$labels.noneSelected,
|
|
3151
|
+
someSelected = _this$state$labels.someSelected,
|
|
3152
|
+
allSelected = _this$state$labels.allSelected; // When implicit all enabled, use `labels.allSelected` when no selection
|
|
3153
|
+
// otherwise, use the `labels.noneSelected` value
|
|
3154
|
+
|
|
3155
|
+
var noSelectionText = implicitAllEnabled ? allSelected : noneSelected;
|
|
3156
|
+
|
|
3157
|
+
switch (selectedValues.length) {
|
|
3158
|
+
case 0:
|
|
3159
|
+
return noSelectionText;
|
|
3160
|
+
|
|
3161
|
+
case 1:
|
|
3162
|
+
// If there is one item selected, we display its label. If for
|
|
3163
|
+
// some reason we can't find the selected item, we use the
|
|
3164
|
+
// display text for the case where nothing is selected.
|
|
3165
|
+
var selectedItem = children.find(function (option) {
|
|
3166
|
+
return option.props.value === selectedValues[0];
|
|
3167
|
+
});
|
|
3168
|
+
return selectedItem ? selectedItem.props.label : noSelectionText;
|
|
3169
|
+
|
|
3170
|
+
case children.length:
|
|
3171
|
+
return allSelected;
|
|
3172
|
+
|
|
3173
|
+
default:
|
|
3174
|
+
return someSelected(selectedValues.length);
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
}, {
|
|
3178
|
+
key: "getSearchField",
|
|
3179
|
+
value: function getSearchField() {
|
|
3180
|
+
if (!this.props.isFilterable) {
|
|
3181
|
+
return [];
|
|
3182
|
+
}
|
|
3183
|
+
|
|
3184
|
+
var _this$state$labels2 = this.state.labels,
|
|
3185
|
+
clearSearch = _this$state$labels2.clearSearch,
|
|
3186
|
+
filter = _this$state$labels2.filter;
|
|
3187
|
+
return [{
|
|
3188
|
+
component: /*#__PURE__*/createElement(SearchTextInput, {
|
|
3189
|
+
key: "search-text-input",
|
|
3190
|
+
onChange: this.handleSearchTextChanged,
|
|
3191
|
+
searchText: this.state.searchText,
|
|
3192
|
+
labels: {
|
|
3193
|
+
clearSearch: clearSearch,
|
|
3194
|
+
filter: filter
|
|
3195
|
+
}
|
|
3196
|
+
}),
|
|
3197
|
+
focusable: true,
|
|
3198
|
+
populatedProps: {}
|
|
3199
|
+
}];
|
|
3200
|
+
}
|
|
3201
|
+
}, {
|
|
3202
|
+
key: "getShortcuts",
|
|
3203
|
+
value: function getShortcuts(numOptions) {
|
|
3204
|
+
var _this$props4 = this.props,
|
|
3205
|
+
selectedValues = _this$props4.selectedValues,
|
|
3206
|
+
shortcuts = _this$props4.shortcuts;
|
|
3207
|
+
var _this$state$labels3 = this.state.labels,
|
|
3208
|
+
selectAllLabel = _this$state$labels3.selectAllLabel,
|
|
3209
|
+
selectNoneLabel = _this$state$labels3.selectNoneLabel; // When there's search text input to filter, shortcuts should be hidden
|
|
3210
|
+
|
|
3211
|
+
if (shortcuts && !this.state.searchText) {
|
|
3212
|
+
var selectAllDisabled = numOptions === selectedValues.length;
|
|
3213
|
+
var selectAll = {
|
|
3214
|
+
component: /*#__PURE__*/createElement(ActionItem, {
|
|
3215
|
+
disabled: selectAllDisabled,
|
|
3216
|
+
label: selectAllLabel(numOptions),
|
|
3217
|
+
indent: true,
|
|
3218
|
+
onClick: this.handleSelectAll
|
|
3219
|
+
}),
|
|
3220
|
+
focusable: !selectAllDisabled,
|
|
3221
|
+
populatedProps: {}
|
|
3222
|
+
};
|
|
3223
|
+
var selectNoneDisabled = selectedValues.length === 0;
|
|
3224
|
+
var selectNone = {
|
|
3225
|
+
component: /*#__PURE__*/createElement(ActionItem, {
|
|
3226
|
+
disabled: selectNoneDisabled,
|
|
3227
|
+
label: selectNoneLabel,
|
|
3228
|
+
indent: true,
|
|
3229
|
+
onClick: this.handleSelectNone
|
|
3230
|
+
}),
|
|
3231
|
+
focusable: !selectNoneDisabled,
|
|
3232
|
+
populatedProps: {}
|
|
3233
|
+
};
|
|
3234
|
+
var separator = {
|
|
3235
|
+
component: /*#__PURE__*/createElement(SeparatorItem, {
|
|
3236
|
+
key: "shortcuts-separator"
|
|
3237
|
+
}),
|
|
3238
|
+
focusable: false,
|
|
3239
|
+
populatedProps: {}
|
|
3240
|
+
};
|
|
3241
|
+
return [selectAll, selectNone, separator];
|
|
3242
|
+
} else {
|
|
3243
|
+
return [];
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
}, {
|
|
3247
|
+
key: "getMenuItems",
|
|
3248
|
+
value: function getMenuItems(children) {
|
|
3249
|
+
var isFilterable = this.props.isFilterable; // If it's not filterable, no need to do any extra besides mapping the
|
|
3250
|
+
// option items to dropdown items.
|
|
3251
|
+
|
|
3252
|
+
if (!isFilterable) {
|
|
3253
|
+
return children.map(this.mapOptionItemToDropdownItem);
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
var _this$state = this.state,
|
|
3257
|
+
searchText = _this$state.searchText,
|
|
3258
|
+
lastSelectedValues = _this$state.lastSelectedValues;
|
|
3259
|
+
var lowercasedSearchText = searchText.toLowerCase(); // Filter the children with the searchText if any.
|
|
3260
|
+
|
|
3261
|
+
var filteredChildren = children.filter(function (_ref) {
|
|
3262
|
+
var props = _ref.props;
|
|
3263
|
+
return !searchText || props.label.toLowerCase().indexOf(lowercasedSearchText) > -1;
|
|
3264
|
+
});
|
|
3265
|
+
var lastSelectedChildren = [];
|
|
3266
|
+
var restOfTheChildren = [];
|
|
3267
|
+
|
|
3268
|
+
var _iterator = _createForOfIteratorHelper(filteredChildren),
|
|
3269
|
+
_step;
|
|
3270
|
+
|
|
3271
|
+
try {
|
|
3272
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
3273
|
+
var child = _step.value;
|
|
3274
|
+
|
|
3275
|
+
if (lastSelectedValues.includes(child.props.value)) {
|
|
3276
|
+
lastSelectedChildren.push(child);
|
|
3277
|
+
} else {
|
|
3278
|
+
restOfTheChildren.push(child);
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
} catch (err) {
|
|
3282
|
+
_iterator.e(err);
|
|
3283
|
+
} finally {
|
|
3284
|
+
_iterator.f();
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
var lastSelectedItems = lastSelectedChildren.map(this.mapOptionItemToDropdownItem); // We want to add SeparatorItem in between last selected items and the
|
|
3288
|
+
// rest of the items only when both of them exists.
|
|
3289
|
+
|
|
3290
|
+
if (lastSelectedChildren.length && restOfTheChildren.length) {
|
|
3291
|
+
lastSelectedItems.push({
|
|
3292
|
+
component: /*#__PURE__*/createElement(SeparatorItem, {
|
|
3293
|
+
key: "selected-separator"
|
|
3294
|
+
}),
|
|
3295
|
+
focusable: false,
|
|
3296
|
+
populatedProps: {}
|
|
3297
|
+
});
|
|
3298
|
+
}
|
|
3299
|
+
|
|
3300
|
+
return [].concat(_toConsumableArray(lastSelectedItems), _toConsumableArray(restOfTheChildren.map(this.mapOptionItemToDropdownItem)));
|
|
3301
|
+
}
|
|
3302
|
+
}, {
|
|
3303
|
+
key: "renderOpener",
|
|
3304
|
+
value: function renderOpener(allChildren) {
|
|
3305
|
+
var _this$props5 = this.props,
|
|
3306
|
+
disabled = _this$props5.disabled,
|
|
3307
|
+
id = _this$props5.id,
|
|
3308
|
+
light = _this$props5.light,
|
|
3309
|
+
opener = _this$props5.opener,
|
|
3310
|
+
testId = _this$props5.testId,
|
|
3311
|
+
alignment = _this$props5.alignment,
|
|
3312
|
+
dropdownStyle = _this$props5.dropdownStyle,
|
|
3313
|
+
implicitAllEnabled = _this$props5.implicitAllEnabled,
|
|
3314
|
+
isFilterable = _this$props5.isFilterable,
|
|
3315
|
+
labels = _this$props5.labels,
|
|
3316
|
+
onChange = _this$props5.onChange,
|
|
3317
|
+
onToggle = _this$props5.onToggle,
|
|
3318
|
+
opened = _this$props5.opened,
|
|
3319
|
+
selectedValues = _this$props5.selectedValues,
|
|
3320
|
+
shortcuts = _this$props5.shortcuts,
|
|
3321
|
+
style = _this$props5.style,
|
|
3322
|
+
className = _this$props5.className,
|
|
3323
|
+
sharedProps = _objectWithoutProperties(_this$props5, ["disabled", "id", "light", "opener", "testId", "alignment", "dropdownStyle", "implicitAllEnabled", "isFilterable", "labels", "onChange", "onToggle", "opened", "selectedValues", "shortcuts", "style", "className"]);
|
|
3324
|
+
|
|
3325
|
+
var noneSelected = this.state.labels.noneSelected;
|
|
3326
|
+
var menuText = this.getMenuText(allChildren);
|
|
3327
|
+
var numOptions = allChildren.length;
|
|
3328
|
+
var dropdownOpener = opener ? /*#__PURE__*/createElement(DropdownOpener, {
|
|
3329
|
+
onClick: this.handleClick,
|
|
3330
|
+
disabled: numOptions === 0 || disabled,
|
|
3331
|
+
ref: this.handleOpenerRef,
|
|
3332
|
+
text: menuText
|
|
3333
|
+
}, opener) : /*#__PURE__*/createElement(SelectOpener, _extends({}, sharedProps, {
|
|
3334
|
+
disabled: numOptions === 0 || disabled,
|
|
3335
|
+
id: id,
|
|
3336
|
+
isPlaceholder: menuText === noneSelected,
|
|
3337
|
+
light: light,
|
|
3338
|
+
onOpenChanged: this.handleOpenChanged,
|
|
3339
|
+
open: this.state.open,
|
|
3340
|
+
ref: this.handleOpenerRef,
|
|
3341
|
+
testId: testId
|
|
3342
|
+
}), menuText);
|
|
3343
|
+
return dropdownOpener;
|
|
3344
|
+
}
|
|
3345
|
+
}, {
|
|
3346
|
+
key: "render",
|
|
3347
|
+
value: function render() {
|
|
3348
|
+
var _this$props6 = this.props,
|
|
3349
|
+
alignment = _this$props6.alignment,
|
|
3350
|
+
light = _this$props6.light,
|
|
3351
|
+
style = _this$props6.style,
|
|
3352
|
+
className = _this$props6.className,
|
|
3353
|
+
dropdownStyle = _this$props6.dropdownStyle,
|
|
3354
|
+
children = _this$props6.children,
|
|
3355
|
+
isFilterable = _this$props6.isFilterable;
|
|
3356
|
+
var _this$state2 = this.state,
|
|
3357
|
+
open = _this$state2.open,
|
|
3358
|
+
searchText = _this$state2.searchText;
|
|
3359
|
+
var noResults = this.state.labels.noResults;
|
|
3360
|
+
var allChildren = Children.toArray(children).filter(Boolean);
|
|
3361
|
+
var numOptions = allChildren.length;
|
|
3362
|
+
var filteredItems = this.getMenuItems(allChildren);
|
|
3363
|
+
var opener = this.renderOpener(allChildren);
|
|
3364
|
+
return /*#__PURE__*/createElement(DropdownCore$1, {
|
|
3365
|
+
role: "listbox",
|
|
3366
|
+
alignment: alignment,
|
|
3367
|
+
dropdownStyle: [isFilterable && filterableDropdownStyle, selectDropdownStyle, dropdownStyle],
|
|
3368
|
+
items: [].concat(_toConsumableArray(this.getSearchField()), _toConsumableArray(this.getShortcuts(numOptions)), _toConsumableArray(filteredItems)),
|
|
3369
|
+
light: light,
|
|
3370
|
+
onOpenChanged: this.handleOpenChanged,
|
|
3371
|
+
open: open,
|
|
3372
|
+
opener: opener,
|
|
3373
|
+
openerElement: this.state.openerElement,
|
|
3374
|
+
style: style,
|
|
3375
|
+
className: className,
|
|
3376
|
+
onSearchTextChanged: isFilterable ? this.handleSearchTextChanged : null,
|
|
3377
|
+
searchText: isFilterable ? searchText : "",
|
|
3378
|
+
labels: {
|
|
3379
|
+
noResults: noResults
|
|
3380
|
+
}
|
|
3381
|
+
});
|
|
3382
|
+
}
|
|
3383
|
+
}], [{
|
|
3384
|
+
key: "getDerivedStateFromProps",
|
|
3385
|
+
value: function getDerivedStateFromProps(props, state) {
|
|
3386
|
+
return {
|
|
3387
|
+
open: typeof props.opened === "boolean" ? props.opened : state.open
|
|
3388
|
+
};
|
|
3389
|
+
}
|
|
3390
|
+
}]);
|
|
3391
|
+
|
|
3392
|
+
return MultiSelect;
|
|
3393
|
+
}(Component);
|
|
3394
|
+
|
|
3395
|
+
_defineProperty(MultiSelect, "defaultProps", {
|
|
3396
|
+
alignment: "left",
|
|
3397
|
+
disabled: false,
|
|
3398
|
+
light: false,
|
|
3399
|
+
shortcuts: false,
|
|
3400
|
+
selectedValues: []
|
|
3401
|
+
});
|
|
3402
|
+
|
|
3403
|
+
export { ActionItem, ActionMenu, MultiSelect, OptionItem, SeparatorItem, SingleSelect };
|