@zendeskgarden/react-dropdowns.legacy 9.0.0-next.10

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.
Files changed (114) hide show
  1. package/LICENSE.md +176 -0
  2. package/README.md +100 -0
  3. package/dist/esm/elements/Autocomplete/Autocomplete.js +144 -0
  4. package/dist/esm/elements/Combobox/Combobox.js +106 -0
  5. package/dist/esm/elements/Dropdown/Dropdown.js +178 -0
  6. package/dist/esm/elements/Fields/Field.js +35 -0
  7. package/dist/esm/elements/Fields/Hint.js +15 -0
  8. package/dist/esm/elements/Fields/Label.js +46 -0
  9. package/dist/esm/elements/Fields/Message.js +19 -0
  10. package/dist/esm/elements/Menu/Items/AddItem.js +66 -0
  11. package/dist/esm/elements/Menu/Items/HeaderIcon.js +44 -0
  12. package/dist/esm/elements/Menu/Items/HeaderItem.js +44 -0
  13. package/dist/esm/elements/Menu/Items/Item.js +140 -0
  14. package/dist/esm/elements/Menu/Items/ItemMeta.js +49 -0
  15. package/dist/esm/elements/Menu/Items/MediaBody.js +44 -0
  16. package/dist/esm/elements/Menu/Items/MediaFigure.js +42 -0
  17. package/dist/esm/elements/Menu/Items/MediaItem.js +44 -0
  18. package/dist/esm/elements/Menu/Items/NextItem.js +90 -0
  19. package/dist/esm/elements/Menu/Items/PreviousItem.js +87 -0
  20. package/dist/esm/elements/Menu/Menu.js +160 -0
  21. package/dist/esm/elements/Menu/Separator.js +37 -0
  22. package/dist/esm/elements/Multiselect/Multiselect.js +316 -0
  23. package/dist/esm/elements/Select/Select.js +195 -0
  24. package/dist/esm/elements/Trigger/Trigger.js +185 -0
  25. package/dist/esm/index.js +29 -0
  26. package/dist/esm/node_modules/@zendeskgarden/svg-icons/src/16/check-lg-stroke.svg.js +28 -0
  27. package/dist/esm/node_modules/@zendeskgarden/svg-icons/src/16/chevron-down-stroke.svg.js +25 -0
  28. package/dist/esm/node_modules/@zendeskgarden/svg-icons/src/16/chevron-left-stroke.svg.js +25 -0
  29. package/dist/esm/node_modules/@zendeskgarden/svg-icons/src/16/chevron-right-stroke.svg.js +25 -0
  30. package/dist/esm/node_modules/@zendeskgarden/svg-icons/src/16/plus-stroke.svg.js +26 -0
  31. package/dist/esm/styled/items/StyledAddItem.js +23 -0
  32. package/dist/esm/styled/items/StyledItem.js +44 -0
  33. package/dist/esm/styled/items/StyledItemIcon.js +27 -0
  34. package/dist/esm/styled/items/StyledItemMeta.js +22 -0
  35. package/dist/esm/styled/items/StyledNextIcon.js +31 -0
  36. package/dist/esm/styled/items/StyledNextItem.js +24 -0
  37. package/dist/esm/styled/items/StyledPreviousIcon.js +31 -0
  38. package/dist/esm/styled/items/StyledPreviousItem.js +23 -0
  39. package/dist/esm/styled/items/header/StyledHeaderIcon.js +22 -0
  40. package/dist/esm/styled/items/header/StyledHeaderItem.js +29 -0
  41. package/dist/esm/styled/items/media/StyledMediaBody.js +22 -0
  42. package/dist/esm/styled/items/media/StyledMediaFigure.js +34 -0
  43. package/dist/esm/styled/items/media/StyledMediaItem.js +23 -0
  44. package/dist/esm/styled/menu/StyledMenu.js +28 -0
  45. package/dist/esm/styled/menu/StyledMenuWrapper.js +30 -0
  46. package/dist/esm/styled/menu/StyledSeparator.js +23 -0
  47. package/dist/esm/styled/multiselect/StyledMultiselectInput.js +33 -0
  48. package/dist/esm/styled/multiselect/StyledMultiselectItemWrapper.js +22 -0
  49. package/dist/esm/styled/multiselect/StyledMultiselectItemsContainer.js +34 -0
  50. package/dist/esm/styled/multiselect/StyledMultiselectMoreAnchor.js +22 -0
  51. package/dist/esm/styled/select/StyledFauxInput.js +25 -0
  52. package/dist/esm/styled/select/StyledInput.js +25 -0
  53. package/dist/esm/styled/select/StyledSelect.js +22 -0
  54. package/dist/esm/types/index.js +10 -0
  55. package/dist/esm/utils/garden-placements.js +77 -0
  56. package/dist/esm/utils/useDropdownContext.js +18 -0
  57. package/dist/esm/utils/useFieldContext.js +18 -0
  58. package/dist/esm/utils/useItemContext.js +18 -0
  59. package/dist/esm/utils/useMenuContext.js +18 -0
  60. package/dist/index.cjs.js +2066 -0
  61. package/dist/typings/elements/Autocomplete/Autocomplete.d.ts +14 -0
  62. package/dist/typings/elements/Combobox/Combobox.d.ts +14 -0
  63. package/dist/typings/elements/Dropdown/Dropdown.d.ts +26 -0
  64. package/dist/typings/elements/Fields/Field.d.ts +11 -0
  65. package/dist/typings/elements/Fields/Hint.d.ts +11 -0
  66. package/dist/typings/elements/Fields/Label.d.ts +12 -0
  67. package/dist/typings/elements/Fields/Message.d.ts +12 -0
  68. package/dist/typings/elements/Menu/Items/AddItem.d.ts +12 -0
  69. package/dist/typings/elements/Menu/Items/HeaderIcon.d.ts +11 -0
  70. package/dist/typings/elements/Menu/Items/HeaderItem.d.ts +12 -0
  71. package/dist/typings/elements/Menu/Items/Item.d.ts +12 -0
  72. package/dist/typings/elements/Menu/Items/ItemMeta.d.ts +11 -0
  73. package/dist/typings/elements/Menu/Items/MediaBody.d.ts +11 -0
  74. package/dist/typings/elements/Menu/Items/MediaFigure.d.ts +11 -0
  75. package/dist/typings/elements/Menu/Items/MediaItem.d.ts +12 -0
  76. package/dist/typings/elements/Menu/Items/NextItem.d.ts +12 -0
  77. package/dist/typings/elements/Menu/Items/PreviousItem.d.ts +12 -0
  78. package/dist/typings/elements/Menu/Menu.d.ts +14 -0
  79. package/dist/typings/elements/Menu/Separator.d.ts +11 -0
  80. package/dist/typings/elements/Multiselect/Multiselect.d.ts +14 -0
  81. package/dist/typings/elements/Select/Select.d.ts +14 -0
  82. package/dist/typings/elements/Trigger/Trigger.d.ts +24 -0
  83. package/dist/typings/index.d.ts +31 -0
  84. package/dist/typings/styled/index.d.ts +29 -0
  85. package/dist/typings/styled/items/StyledAddItem.d.ts +13 -0
  86. package/dist/typings/styled/items/StyledItem.d.ts +20 -0
  87. package/dist/typings/styled/items/StyledItemIcon.d.ts +17 -0
  88. package/dist/typings/styled/items/StyledItemMeta.d.ts +18 -0
  89. package/dist/typings/styled/items/StyledNextIcon.d.ts +12 -0
  90. package/dist/typings/styled/items/StyledNextItem.d.ts +13 -0
  91. package/dist/typings/styled/items/StyledPreviousIcon.d.ts +12 -0
  92. package/dist/typings/styled/items/StyledPreviousItem.d.ts +13 -0
  93. package/dist/typings/styled/items/header/StyledHeaderIcon.d.ts +14 -0
  94. package/dist/typings/styled/items/header/StyledHeaderItem.d.ts +16 -0
  95. package/dist/typings/styled/items/media/StyledMediaBody.d.ts +17 -0
  96. package/dist/typings/styled/items/media/StyledMediaFigure.d.ts +285 -0
  97. package/dist/typings/styled/items/media/StyledMediaItem.d.ts +13 -0
  98. package/dist/typings/styled/menu/StyledMenu.d.ts +20 -0
  99. package/dist/typings/styled/menu/StyledMenuWrapper.d.ts +16 -0
  100. package/dist/typings/styled/menu/StyledSeparator.d.ts +10 -0
  101. package/dist/typings/styled/multiselect/StyledMultiselectInput.d.ts +22 -0
  102. package/dist/typings/styled/multiselect/StyledMultiselectItemWrapper.d.ts +10 -0
  103. package/dist/typings/styled/multiselect/StyledMultiselectItemsContainer.d.ts +16 -0
  104. package/dist/typings/styled/multiselect/StyledMultiselectMoreAnchor.d.ts +15 -0
  105. package/dist/typings/styled/select/StyledFauxInput.d.ts +22 -0
  106. package/dist/typings/styled/select/StyledInput.d.ts +15 -0
  107. package/dist/typings/styled/select/StyledSelect.d.ts +10 -0
  108. package/dist/typings/types/index.d.ts +150 -0
  109. package/dist/typings/utils/garden-placements.d.ts +26 -0
  110. package/dist/typings/utils/useDropdownContext.d.ts +28 -0
  111. package/dist/typings/utils/useFieldContext.d.ts +17 -0
  112. package/dist/typings/utils/useItemContext.d.ts +16 -0
  113. package/dist/typings/utils/useMenuContext.d.ts +17 -0
  114. package/package.json +56 -0
@@ -0,0 +1,316 @@
1
+ /**
2
+ * Copyright Zendesk, Inc.
3
+ *
4
+ * Use of this source code is governed under the Apache License, Version 2.0
5
+ * found at http://www.apache.org/licenses/LICENSE-2.0.
6
+ */
7
+ import React__default, { useRef, useState, useContext, useEffect, useCallback, useMemo } from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import { ThemeContext } from 'styled-components';
10
+ import { Reference } from 'react-popper';
11
+ import { useSelection } from '@zendeskgarden/container-selection';
12
+ import { composeEventHandlers, KEY_CODES } from '@zendeskgarden/container-utilities';
13
+ import { useDocument } from '@zendeskgarden/react-theming';
14
+ import SvgChevronDownStroke from '../../node_modules/@zendeskgarden/svg-icons/src/16/chevron-down-stroke.svg.js';
15
+ import { mergeRefs } from 'react-merge-refs';
16
+ import '../../styled/menu/StyledMenu.js';
17
+ import '../../styled/menu/StyledMenuWrapper.js';
18
+ import '../../styled/menu/StyledSeparator.js';
19
+ import '../../styled/items/StyledAddItem.js';
20
+ import '../../styled/items/StyledItem.js';
21
+ import '../../styled/items/StyledItemMeta.js';
22
+ import '../../styled/items/StyledNextItem.js';
23
+ import '../../styled/items/StyledNextIcon.js';
24
+ import '../../styled/items/StyledPreviousItem.js';
25
+ import '../../styled/items/StyledPreviousIcon.js';
26
+ import '../../styled/items/StyledItemIcon.js';
27
+ import '../../styled/items/header/StyledHeaderIcon.js';
28
+ import '../../styled/items/header/StyledHeaderItem.js';
29
+ import '../../styled/items/media/StyledMediaBody.js';
30
+ import '../../styled/items/media/StyledMediaFigure.js';
31
+ import '../../styled/items/media/StyledMediaItem.js';
32
+ import { StyledFauxInput } from '../../styled/select/StyledFauxInput.js';
33
+ import '../../styled/select/StyledInput.js';
34
+ import '../../styled/select/StyledSelect.js';
35
+ import { StyledMultiselectInput } from '../../styled/multiselect/StyledMultiselectInput.js';
36
+ import { StyledMultiselectItemsContainer } from '../../styled/multiselect/StyledMultiselectItemsContainer.js';
37
+ import { StyledMultiselectItemWrapper } from '../../styled/multiselect/StyledMultiselectItemWrapper.js';
38
+ import { StyledMultiselectMoreAnchor } from '../../styled/multiselect/StyledMultiselectMoreAnchor.js';
39
+ import useDropdownContext from '../../utils/useDropdownContext.js';
40
+ import useFieldContext from '../../utils/useFieldContext.js';
41
+ import { REMOVE_ITEM_STATE_TYPE } from '../Dropdown/Dropdown.js';
42
+
43
+ const Multiselect = React__default.forwardRef((_ref, ref) => {
44
+ let {
45
+ renderItem,
46
+ placeholder,
47
+ maxItems,
48
+ renderShowMore,
49
+ inputRef: externalInputRef = null,
50
+ start,
51
+ onKeyDown,
52
+ ...props
53
+ } = _ref;
54
+ const {
55
+ popperReferenceElementRef,
56
+ selectedItems = [],
57
+ containsMultiselectRef,
58
+ previousIndexRef,
59
+ downshift: {
60
+ getToggleButtonProps,
61
+ getRootProps,
62
+ getInputProps,
63
+ isOpen,
64
+ closeMenu,
65
+ inputValue,
66
+ setState: setDownshiftState,
67
+ itemToString
68
+ },
69
+ setDropdownType
70
+ } = useDropdownContext();
71
+ const {
72
+ isLabelHovered
73
+ } = useFieldContext();
74
+ const inputRef = useRef();
75
+ const triggerRef = useRef();
76
+ const blurTimeoutRef = useRef();
77
+ const previousIsOpenRef = useRef(undefined);
78
+ const previousIsFocusedRef = useRef(undefined);
79
+ const [isHovered, setIsHovered] = useState(false);
80
+ const [isFocused, setIsFocused] = useState(false);
81
+ const [focusedItem, setFocusedItem] = useState(undefined);
82
+ const themeContext = useContext(ThemeContext);
83
+ const environment = useDocument(themeContext);
84
+ const {
85
+ getContainerProps,
86
+ getItemProps
87
+ } = useSelection({
88
+ rtl: themeContext.rtl,
89
+ focusedItem,
90
+ selectedItem: undefined,
91
+ onFocus: item => {
92
+ setFocusedItem(item);
93
+ }
94
+ });
95
+ useEffect(() => {
96
+ containsMultiselectRef.current = true;
97
+ const tempRef = blurTimeoutRef;
98
+ return () => {
99
+ clearTimeout(tempRef.current);
100
+ };
101
+ }, []);
102
+ useEffect(() => {
103
+ if (inputRef.current) {
104
+ if (isOpen && !previousIsOpenRef.current) {
105
+ inputRef.current.focus();
106
+ } else if (isFocused && !previousIsFocusedRef.current && focusedItem === undefined) {
107
+ inputRef.current.focus();
108
+ }
109
+ }
110
+ previousIsOpenRef.current = isOpen;
111
+ previousIsFocusedRef.current = isFocused;
112
+ }, [isOpen, inputRef, isFocused, focusedItem]);
113
+ useEffect(() => {
114
+ if (focusedItem !== undefined && isOpen) {
115
+ closeMenu();
116
+ }
117
+ }, [focusedItem, isOpen, closeMenu]);
118
+ const {
119
+ type,
120
+ ...selectProps
121
+ } = getToggleButtonProps(getRootProps({
122
+ tabIndex: props.disabled ? undefined : -1,
123
+ onKeyDown: composeEventHandlers(onKeyDown, e => {
124
+ if (isOpen) {
125
+ e.nativeEvent.preventDownshiftDefault = true;
126
+ } else if (!inputValue && e.keyCode === KEY_CODES.HOME) {
127
+ setFocusedItem(selectedItems[0]);
128
+ e.preventDefault();
129
+ }
130
+ }),
131
+ onFocus: () => {
132
+ setIsFocused(true);
133
+ },
134
+ onBlur: e => {
135
+ const currentTarget = e.currentTarget;
136
+ blurTimeoutRef.current = setTimeout(() => {
137
+ if (environment && !currentTarget.contains(environment.activeElement)) {
138
+ setIsFocused(false);
139
+ }
140
+ }, 0);
141
+ },
142
+ onMouseEnter: composeEventHandlers(props.onMouseEnter, () => setIsHovered(true)),
143
+ onMouseLeave: composeEventHandlers(props.onMouseLeave, () => setIsHovered(false)),
144
+ role: null,
145
+ ...props
146
+ }));
147
+ const renderSelectableItem = useCallback((item, index) => {
148
+ const removeValue = () => {
149
+ setDownshiftState({
150
+ type: REMOVE_ITEM_STATE_TYPE,
151
+ selectedItem: item
152
+ });
153
+ inputRef.current && inputRef.current.focus();
154
+ };
155
+ const renderedItem = renderItem({
156
+ value: item,
157
+ removeValue
158
+ });
159
+ const focusRef = React__default.createRef();
160
+ const clonedChild = React__default.cloneElement(renderedItem, {
161
+ ...getItemProps({
162
+ item,
163
+ focusRef,
164
+ onKeyDown: e => {
165
+ if (e.keyCode === KEY_CODES.DELETE || e.keyCode === KEY_CODES.BACKSPACE) {
166
+ e.preventDefault();
167
+ removeValue();
168
+ }
169
+ if (e.keyCode === KEY_CODES.END && !inputValue) {
170
+ inputRef.current && inputRef.current.focus();
171
+ e.preventDefault();
172
+ }
173
+ if (themeContext.rtl) {
174
+ if (e.keyCode === KEY_CODES.RIGHT && index === 0) {
175
+ e.preventDefault();
176
+ }
177
+ if (e.keyCode === KEY_CODES.LEFT && index === selectedItems.length - 1) {
178
+ e.preventDefault();
179
+ inputRef.current && inputRef.current.focus();
180
+ }
181
+ } else {
182
+ if (e.keyCode === KEY_CODES.LEFT && index === 0) {
183
+ e.preventDefault();
184
+ }
185
+ if (e.keyCode === KEY_CODES.RIGHT && index === selectedItems.length - 1) {
186
+ e.preventDefault();
187
+ inputRef.current && inputRef.current.focus();
188
+ }
189
+ }
190
+ },
191
+ onClick: e => {
192
+ e.nativeEvent.preventDownshiftDefault = true;
193
+ },
194
+ tabIndex: -1
195
+ }),
196
+ size: props.isCompact ? 'medium' : 'large'
197
+ });
198
+ const key = `${itemToString(item)}-${index}`;
199
+ return React__default.createElement(StyledMultiselectItemWrapper, {
200
+ key: key
201
+ }, clonedChild);
202
+ }, [getItemProps, inputValue, renderItem, setDownshiftState, itemToString, selectedItems, props, inputRef, themeContext.rtl]);
203
+ const items = useMemo(() => {
204
+ const itemValues = selectedItems || [];
205
+ const output = [];
206
+ for (let x = 0; x < itemValues.length; x++) {
207
+ const item = itemValues[x];
208
+ if (x < maxItems) {
209
+ if (props.disabled) {
210
+ const renderedItem = React__default.cloneElement(renderItem({
211
+ value: item,
212
+ removeValue: () => {
213
+ return undefined;
214
+ }
215
+ }), {
216
+ size: props.isCompact ? 'medium' : 'large'
217
+ });
218
+ output.push( React__default.createElement(StyledMultiselectItemWrapper, {
219
+ key: x
220
+ }, renderedItem));
221
+ } else {
222
+ output.push(renderSelectableItem(item, x));
223
+ }
224
+ } else if (!isFocused && !inputValue || props.disabled) {
225
+ output.push( React__default.createElement(StyledMultiselectItemWrapper, {
226
+ key: "more-anchor"
227
+ }, React__default.createElement(StyledMultiselectMoreAnchor, {
228
+ isCompact: props.isCompact,
229
+ isDisabled: props.disabled
230
+ }, renderShowMore ? renderShowMore(itemValues.length - x) : `+ ${itemValues.length - x} more`)));
231
+ break;
232
+ } else {
233
+ output.push(renderSelectableItem(item, x));
234
+ }
235
+ }
236
+ return output;
237
+ }, [isFocused, props.disabled, renderSelectableItem, selectedItems, renderItem, inputValue, maxItems, renderShowMore, props.isCompact]);
238
+ const isContainerHovered = isLabelHovered && !isOpen;
239
+ const isContainerFocused = isOpen || isFocused;
240
+ useEffect(() => {
241
+ setDropdownType('multiselect');
242
+ }, [setDropdownType]);
243
+ return React__default.createElement(Reference, null, _ref2 => {
244
+ let {
245
+ ref: popperReference
246
+ } = _ref2;
247
+ return React__default.createElement(StyledFauxInput, getContainerProps({
248
+ ...selectProps,
249
+ isHovered: isContainerHovered,
250
+ isFocused: isContainerFocused,
251
+ ref: selectRef => {
252
+ popperReference(selectRef);
253
+ mergeRefs([triggerRef, popperReferenceElementRef, ref])(selectRef);
254
+ }
255
+ }), start && React__default.createElement(StyledFauxInput.StartIcon, {
256
+ isHovered: isHovered || isLabelHovered && !isOpen,
257
+ isFocused: isContainerFocused,
258
+ isDisabled: props.disabled
259
+ }, start), React__default.createElement(StyledMultiselectItemsContainer, {
260
+ isBare: props.isBare,
261
+ isCompact: props.isCompact
262
+ }, items, React__default.createElement(StyledMultiselectInput, getInputProps({
263
+ disabled: props.disabled,
264
+ onFocus: () => {
265
+ setFocusedItem(undefined);
266
+ },
267
+ onClick: e => {
268
+ if (inputValue && inputValue.length > 0 && isOpen) {
269
+ e.nativeEvent.preventDownshiftDefault = true;
270
+ }
271
+ },
272
+ onKeyDown: e => {
273
+ if (!inputValue) {
274
+ if (themeContext.rtl && e.keyCode === KEY_CODES.RIGHT && selectedItems.length > 0 && previousIndexRef.current === undefined) {
275
+ setFocusedItem(selectedItems[selectedItems.length - 1]);
276
+ } else if (!themeContext.rtl && e.keyCode === KEY_CODES.LEFT && selectedItems.length > 0 && previousIndexRef.current === undefined) {
277
+ setFocusedItem(selectedItems[selectedItems.length - 1]);
278
+ } else if (e.keyCode === KEY_CODES.BACKSPACE && selectedItems.length > 0) {
279
+ setDownshiftState({
280
+ type: REMOVE_ITEM_STATE_TYPE,
281
+ selectedItem: selectedItems[selectedItems.length - 1]
282
+ });
283
+ e.nativeEvent.preventDownshiftDefault = true;
284
+ e.preventDefault();
285
+ e.stopPropagation();
286
+ }
287
+ }
288
+ },
289
+ isVisible: isFocused || inputValue || selectedItems.length === 0,
290
+ isCompact: props.isCompact,
291
+ role: 'combobox',
292
+ ref: mergeRefs([inputRef, externalInputRef]),
293
+ placeholder: selectedItems.length === 0 ? placeholder : undefined
294
+ }))), !props.isBare && React__default.createElement(StyledFauxInput.EndIcon, {
295
+ isHovered: isHovered || isLabelHovered && !isOpen,
296
+ isFocused: isContainerFocused,
297
+ isDisabled: props.disabled,
298
+ isRotated: isOpen
299
+ }, React__default.createElement(SvgChevronDownStroke, null)));
300
+ });
301
+ });
302
+ Multiselect.propTypes = {
303
+ isCompact: PropTypes.bool,
304
+ isBare: PropTypes.bool,
305
+ disabled: PropTypes.bool,
306
+ focusInset: PropTypes.bool,
307
+ renderItem: PropTypes.func.isRequired,
308
+ maxItems: PropTypes.number,
309
+ validation: PropTypes.oneOf(['success', 'warning', 'error'])
310
+ };
311
+ Multiselect.defaultProps = {
312
+ maxItems: 4
313
+ };
314
+ Multiselect.displayName = 'Multiselect';
315
+
316
+ export { Multiselect };
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Copyright Zendesk, Inc.
3
+ *
4
+ * Use of this source code is governed under the Apache License, Version 2.0
5
+ * found at http://www.apache.org/licenses/LICENSE-2.0.
6
+ */
7
+ import React__default, { useState, useRef, useEffect, useCallback } from 'react';
8
+ import { KEY_CODES, composeEventHandlers } from '@zendeskgarden/container-utilities';
9
+ import SvgChevronDownStroke from '../../node_modules/@zendeskgarden/svg-icons/src/16/chevron-down-stroke.svg.js';
10
+ import { VALIDATION } from '@zendeskgarden/react-forms';
11
+ import PropTypes from 'prop-types';
12
+ import { Reference } from 'react-popper';
13
+ import { mergeRefs } from 'react-merge-refs';
14
+ import '../../styled/menu/StyledMenu.js';
15
+ import '../../styled/menu/StyledMenuWrapper.js';
16
+ import '../../styled/menu/StyledSeparator.js';
17
+ import '../../styled/items/StyledAddItem.js';
18
+ import '../../styled/items/StyledItem.js';
19
+ import '../../styled/items/StyledItemMeta.js';
20
+ import '../../styled/items/StyledNextItem.js';
21
+ import '../../styled/items/StyledNextIcon.js';
22
+ import '../../styled/items/StyledPreviousItem.js';
23
+ import '../../styled/items/StyledPreviousIcon.js';
24
+ import '../../styled/items/StyledItemIcon.js';
25
+ import '../../styled/items/header/StyledHeaderIcon.js';
26
+ import '../../styled/items/header/StyledHeaderItem.js';
27
+ import '../../styled/items/media/StyledMediaBody.js';
28
+ import '../../styled/items/media/StyledMediaFigure.js';
29
+ import '../../styled/items/media/StyledMediaItem.js';
30
+ import { StyledFauxInput } from '../../styled/select/StyledFauxInput.js';
31
+ import { StyledInput } from '../../styled/select/StyledInput.js';
32
+ import { StyledSelect } from '../../styled/select/StyledSelect.js';
33
+ import '../../styled/multiselect/StyledMultiselectInput.js';
34
+ import '../../styled/multiselect/StyledMultiselectItemsContainer.js';
35
+ import '../../styled/multiselect/StyledMultiselectItemWrapper.js';
36
+ import '../../styled/multiselect/StyledMultiselectMoreAnchor.js';
37
+ import useDropdownContext from '../../utils/useDropdownContext.js';
38
+ import useFieldContext from '../../utils/useFieldContext.js';
39
+
40
+ const Select = React__default.forwardRef((_ref, ref) => {
41
+ let {
42
+ children,
43
+ start,
44
+ ...props
45
+ } = _ref;
46
+ const {
47
+ popperReferenceElementRef,
48
+ itemSearchRegistry,
49
+ downshift: {
50
+ getToggleButtonProps,
51
+ getInputProps,
52
+ isOpen,
53
+ highlightedIndex,
54
+ setHighlightedIndex,
55
+ selectItemAtIndex,
56
+ closeMenu
57
+ }
58
+ } = useDropdownContext();
59
+ const {
60
+ isLabelHovered
61
+ } = useFieldContext();
62
+ const [isHovered, setIsHovered] = useState(false);
63
+ const [isFocused, setIsFocused] = useState(false);
64
+ const hiddenInputRef = useRef();
65
+ const triggerRef = useRef();
66
+ const previousIsOpenRef = useRef(undefined);
67
+ const [searchString, setSearchString] = useState('');
68
+ const searchTimeoutRef = useRef();
69
+ const currentSearchIndexRef = useRef(0);
70
+ useEffect(() => {
71
+ if (hiddenInputRef.current && isOpen && !previousIsOpenRef.current) {
72
+ hiddenInputRef.current.focus();
73
+ }
74
+ if (triggerRef.current && !isOpen && previousIsOpenRef.current) {
75
+ triggerRef.current.focus();
76
+ }
77
+ previousIsOpenRef.current = isOpen;
78
+ }, [isOpen, triggerRef]);
79
+ useEffect(() => {
80
+ if (searchTimeoutRef.current) {
81
+ clearTimeout(searchTimeoutRef.current);
82
+ }
83
+ searchTimeoutRef.current = window.setTimeout(() => {
84
+ setSearchString('');
85
+ }, 500);
86
+ return () => {
87
+ clearTimeout(searchTimeoutRef.current);
88
+ };
89
+ }, [searchString]);
90
+ const searchItems = useCallback((searchValue, startIndex, endIndex) => {
91
+ for (let index = startIndex; index < endIndex; index++) {
92
+ const itemTextValue = itemSearchRegistry.current[index];
93
+ if (itemTextValue && itemTextValue.toUpperCase().indexOf(searchValue.toUpperCase()) === 0) {
94
+ return index;
95
+ }
96
+ }
97
+ return undefined;
98
+ }, [itemSearchRegistry]);
99
+ const onInputKeyDown = useCallback(e => {
100
+ if (e.keyCode === KEY_CODES.SPACE) {
101
+ if (searchString) {
102
+ e.preventDefault();
103
+ e.stopPropagation();
104
+ } else if (highlightedIndex !== null && highlightedIndex !== undefined) {
105
+ e.preventDefault();
106
+ e.stopPropagation();
107
+ selectItemAtIndex(highlightedIndex);
108
+ closeMenu();
109
+ }
110
+ }
111
+ if ((e.keyCode < 48 || e.keyCode > 57) && (e.keyCode < 65 || e.keyCode > 90) && e.keyCode !== KEY_CODES.SPACE) {
112
+ return;
113
+ }
114
+ const character = String.fromCharCode(e.which || e.keyCode);
115
+ if (!character || character.length === 0) {
116
+ return;
117
+ }
118
+ if (!searchString) {
119
+ if (highlightedIndex === null || highlightedIndex === undefined) {
120
+ currentSearchIndexRef.current = -1;
121
+ } else {
122
+ currentSearchIndexRef.current = highlightedIndex;
123
+ }
124
+ }
125
+ const newSearchString = searchString + character;
126
+ setSearchString(newSearchString);
127
+ let matchingIndex = searchItems(newSearchString, currentSearchIndexRef.current + 1, itemSearchRegistry.current.length);
128
+ if (matchingIndex === undefined) {
129
+ matchingIndex = searchItems(newSearchString, 0, currentSearchIndexRef.current);
130
+ }
131
+ if (matchingIndex !== undefined) {
132
+ setHighlightedIndex(matchingIndex);
133
+ }
134
+ }, [searchString, searchItems, itemSearchRegistry, highlightedIndex, selectItemAtIndex, closeMenu, setHighlightedIndex]);
135
+ const {
136
+ type,
137
+ ...selectProps
138
+ } = getToggleButtonProps({
139
+ tabIndex: props.disabled ? undefined : 0,
140
+ onMouseEnter: composeEventHandlers(props.onMouseEnter, () => setIsHovered(true)),
141
+ onMouseLeave: composeEventHandlers(props.onMouseLeave, () => setIsHovered(false)),
142
+ onFocus: composeEventHandlers(props.onFocus, () => setIsFocused(true)),
143
+ onBlur: composeEventHandlers(props.onBlur, () => setIsFocused(false)),
144
+ ...props
145
+ });
146
+ const isContainerHovered = isLabelHovered && !isOpen;
147
+ const isContainerFocused = isFocused || isOpen;
148
+ return React__default.createElement(Reference, null, _ref2 => {
149
+ let {
150
+ ref: popperReference
151
+ } = _ref2;
152
+ return React__default.createElement(StyledFauxInput, Object.assign({
153
+ isHovered: isContainerHovered,
154
+ isFocused: isContainerFocused
155
+ }, selectProps, {
156
+ role: "none",
157
+ ref: selectRef => {
158
+ popperReference(selectRef);
159
+ mergeRefs([triggerRef, ref, popperReferenceElementRef])(selectRef);
160
+ }
161
+ }), start && React__default.createElement(StyledFauxInput.StartIcon, {
162
+ isHovered: isHovered || isLabelHovered && !isOpen,
163
+ isFocused: isContainerFocused,
164
+ isDisabled: props.disabled
165
+ }, start), React__default.createElement(StyledSelect, null, children), React__default.createElement(StyledInput, getInputProps({
166
+ readOnly: true,
167
+ isHidden: true,
168
+ tabIndex: -1,
169
+ ref: hiddenInputRef,
170
+ value: '',
171
+ onClick: e => {
172
+ if (isOpen) {
173
+ e.nativeEvent.preventDownshiftDefault = true;
174
+ }
175
+ },
176
+ onKeyDown: onInputKeyDown
177
+ })), !props.isBare && React__default.createElement(StyledFauxInput.EndIcon, {
178
+ isHovered: isHovered || isLabelHovered && !isOpen,
179
+ isFocused: isContainerFocused,
180
+ isDisabled: props.disabled,
181
+ isRotated: isOpen
182
+ }, React__default.createElement(SvgChevronDownStroke, null)));
183
+ });
184
+ });
185
+ Select.displayName = 'Select';
186
+ Select.propTypes = {
187
+ isCompact: PropTypes.bool,
188
+ isBare: PropTypes.bool,
189
+ disabled: PropTypes.bool,
190
+ focusInset: PropTypes.bool,
191
+ validation: PropTypes.oneOf(VALIDATION),
192
+ start: PropTypes.any
193
+ };
194
+
195
+ export { Select };
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Copyright Zendesk, Inc.
3
+ *
4
+ * Use of this source code is governed under the Apache License, Version 2.0
5
+ * found at http://www.apache.org/licenses/LICENSE-2.0.
6
+ */
7
+ import React__default, { useRef, useState, useEffect, useCallback } from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import { Reference } from 'react-popper';
10
+ import { KEY_CODES } from '@zendeskgarden/container-utilities';
11
+ import '../../styled/menu/StyledMenu.js';
12
+ import '../../styled/menu/StyledMenuWrapper.js';
13
+ import '../../styled/menu/StyledSeparator.js';
14
+ import '../../styled/items/StyledAddItem.js';
15
+ import '../../styled/items/StyledItem.js';
16
+ import '../../styled/items/StyledItemMeta.js';
17
+ import '../../styled/items/StyledNextItem.js';
18
+ import '../../styled/items/StyledNextIcon.js';
19
+ import '../../styled/items/StyledPreviousItem.js';
20
+ import '../../styled/items/StyledPreviousIcon.js';
21
+ import '../../styled/items/StyledItemIcon.js';
22
+ import '../../styled/items/header/StyledHeaderIcon.js';
23
+ import '../../styled/items/header/StyledHeaderItem.js';
24
+ import '../../styled/items/media/StyledMediaBody.js';
25
+ import '../../styled/items/media/StyledMediaFigure.js';
26
+ import '../../styled/items/media/StyledMediaItem.js';
27
+ import '../../styled/select/StyledFauxInput.js';
28
+ import { StyledInput } from '../../styled/select/StyledInput.js';
29
+ import '../../styled/select/StyledSelect.js';
30
+ import '../../styled/multiselect/StyledMultiselectInput.js';
31
+ import '../../styled/multiselect/StyledMultiselectItemsContainer.js';
32
+ import '../../styled/multiselect/StyledMultiselectItemWrapper.js';
33
+ import '../../styled/multiselect/StyledMultiselectMoreAnchor.js';
34
+ import useDropdownContext from '../../utils/useDropdownContext.js';
35
+
36
+ const Trigger = _ref => {
37
+ let {
38
+ children,
39
+ refKey,
40
+ ...triggerProps
41
+ } = _ref;
42
+ const {
43
+ hasMenuRef,
44
+ itemSearchRegistry,
45
+ downshift: {
46
+ getRootProps,
47
+ getToggleButtonProps,
48
+ getInputProps,
49
+ isOpen,
50
+ highlightedIndex,
51
+ selectItemAtIndex,
52
+ setHighlightedIndex
53
+ }
54
+ } = useDropdownContext();
55
+ const hiddenInputRef = useRef(null);
56
+ const triggerRef = useRef(null);
57
+ const previousIsOpenRef = useRef(undefined);
58
+ const [searchString, setSearchString] = useState('');
59
+ const searchTimeoutRef = useRef();
60
+ const currentSearchIndexRef = useRef(0);
61
+ useEffect(() => {
62
+ if (hiddenInputRef.current && isOpen && !previousIsOpenRef.current) {
63
+ hiddenInputRef.current.focus();
64
+ }
65
+ if (triggerRef.current && !isOpen && previousIsOpenRef.current) {
66
+ triggerRef.current.focus();
67
+ }
68
+ previousIsOpenRef.current = isOpen;
69
+ }, [isOpen, hasMenuRef]);
70
+ useEffect(() => {
71
+ if (hasMenuRef.current === false) {
72
+ hasMenuRef.current = true;
73
+ }
74
+ }, [hasMenuRef]);
75
+ useEffect(() => {
76
+ if (searchTimeoutRef.current) {
77
+ clearTimeout(searchTimeoutRef.current);
78
+ }
79
+ searchTimeoutRef.current = window.setTimeout(() => {
80
+ setSearchString('');
81
+ }, 500);
82
+ return () => {
83
+ clearTimeout(searchTimeoutRef.current);
84
+ };
85
+ }, [searchString]);
86
+ const searchItems = useCallback((searchValue, startIndex, endIndex) => {
87
+ for (let index = startIndex; index < endIndex; index++) {
88
+ const itemTextValue = itemSearchRegistry.current[index];
89
+ if (itemTextValue && itemTextValue.toUpperCase().indexOf(searchValue.toUpperCase()) === 0) {
90
+ return index;
91
+ }
92
+ }
93
+ return undefined;
94
+ }, [itemSearchRegistry]);
95
+ const onInputKeyDown = useCallback(e => {
96
+ if (e.keyCode === KEY_CODES.SPACE) {
97
+ if (searchString) {
98
+ e.preventDefault();
99
+ e.stopPropagation();
100
+ } else if (highlightedIndex !== null && highlightedIndex !== undefined) {
101
+ e.preventDefault();
102
+ e.stopPropagation();
103
+ selectItemAtIndex(highlightedIndex);
104
+ }
105
+ }
106
+ if ((e.keyCode < 48 || e.keyCode > 57) && (e.keyCode < 65 || e.keyCode > 90) && e.keyCode !== KEY_CODES.SPACE) {
107
+ return;
108
+ }
109
+ const character = String.fromCharCode(e.which || e.keyCode);
110
+ if (!character || character.length === 0) {
111
+ return;
112
+ }
113
+ if (!searchString) {
114
+ if (highlightedIndex === null || highlightedIndex === undefined) {
115
+ currentSearchIndexRef.current = -1;
116
+ } else {
117
+ currentSearchIndexRef.current = highlightedIndex;
118
+ }
119
+ }
120
+ const newSearchString = searchString + character;
121
+ setSearchString(newSearchString);
122
+ let matchingIndex = searchItems(newSearchString, currentSearchIndexRef.current + 1, itemSearchRegistry.current.length);
123
+ if (matchingIndex === undefined) {
124
+ matchingIndex = searchItems(newSearchString, 0, currentSearchIndexRef.current);
125
+ }
126
+ if (matchingIndex !== undefined) {
127
+ setHighlightedIndex(matchingIndex);
128
+ }
129
+ }, [searchString, searchItems, itemSearchRegistry, highlightedIndex, selectItemAtIndex, setHighlightedIndex]);
130
+ const renderChildren = popperRef => {
131
+ const {
132
+ ref: rootPropsRefCallback,
133
+ ...rootProps
134
+ } = getRootProps();
135
+ const listboxToggleProps = getToggleButtonProps({
136
+ ...rootProps,
137
+ role: null,
138
+ 'aria-labelledby': undefined,
139
+ ...triggerProps,
140
+ ...children.props
141
+ });
142
+ const menuToggleProps = {
143
+ ...listboxToggleProps,
144
+ 'aria-haspopup': 'true',
145
+ 'aria-controls': listboxToggleProps['aria-owns'],
146
+ 'aria-owns': null
147
+ };
148
+ const toggleButtonProps = hasMenuRef.current ? menuToggleProps : listboxToggleProps;
149
+ return React__default.cloneElement(React__default.Children.only(children), {
150
+ ...toggleButtonProps,
151
+ [refKey]: childRef => {
152
+ popperRef(childRef);
153
+ triggerRef.current = childRef;
154
+ rootPropsRefCallback(childRef);
155
+ }
156
+ });
157
+ };
158
+ return React__default.createElement(Reference, null, _ref2 => {
159
+ let {
160
+ ref: popperReference
161
+ } = _ref2;
162
+ return React__default.createElement(React__default.Fragment, null, renderChildren(popperReference), React__default.createElement(StyledInput, getInputProps({
163
+ readOnly: true,
164
+ isHidden: true,
165
+ tabIndex: -1,
166
+ ref: hiddenInputRef,
167
+ value: '',
168
+ onClick: e => {
169
+ if (isOpen) {
170
+ e.nativeEvent.preventDownshiftDefault = true;
171
+ }
172
+ },
173
+ onKeyDown: onInputKeyDown
174
+ })));
175
+ });
176
+ };
177
+ Trigger.propTypes = {
178
+ children: PropTypes.any,
179
+ refKey: PropTypes.string
180
+ };
181
+ Trigger.defaultProps = {
182
+ refKey: 'ref'
183
+ };
184
+
185
+ export { Trigger };