@acusti/dropdown 0.38.7 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Dropdown.js CHANGED
@@ -2,57 +2,16 @@
2
2
  import InputText from '@acusti/input-text';
3
3
  import { Style } from '@acusti/styling';
4
4
  import useIsOutOfBounds from '@acusti/use-is-out-of-bounds';
5
- import useKeyboardEvents, {
6
- isEventTargetUsingKeyEvent,
7
- } from '@acusti/use-keyboard-events';
5
+ import useKeyboardEvents, { isEventTargetUsingKeyEvent, } from '@acusti/use-keyboard-events';
8
6
  import clsx from 'clsx';
9
7
  import * as React from 'react';
10
- import {
11
- getActiveItemElement,
12
- getItemElements,
13
- ITEM_SELECTOR,
14
- setActiveItem,
15
- } from './helpers.js';
16
- import {
17
- BODY_CLASS_NAME,
18
- BODY_MAX_HEIGHT_VAR,
19
- BODY_MAX_WIDTH_VAR,
20
- BODY_SELECTOR,
21
- LABEL_CLASS_NAME,
22
- LABEL_TEXT_CLASS_NAME,
23
- ROOT_CLASS_NAME,
24
- STYLES,
25
- TRIGGER_CLASS_NAME,
26
- } from './styles.js';
8
+ import { getActiveItemElement, getItemElements, ITEM_SELECTOR, setActiveItem, } from './helpers.js';
9
+ import { BODY_CLASS_NAME, BODY_MAX_HEIGHT_VAR, BODY_MAX_WIDTH_VAR, BODY_SELECTOR, LABEL_CLASS_NAME, LABEL_TEXT_CLASS_NAME, ROOT_CLASS_NAME, STYLES, TRIGGER_CLASS_NAME, } from './styles.js';
27
10
  const { Children, Fragment, useCallback, useEffect, useMemo, useRef, useState } = React;
28
- const noop = () => {}; // eslint-disable-line @typescript-eslint/no-empty-function
29
- const CHILDREN_ERROR =
30
- '@acusti/dropdown requires either 1 child (the dropdown body) or 2 children: the dropdown trigger and the dropdown body.';
31
- const TEXT_INPUT_SELECTOR =
32
- 'input:not([type=radio]):not([type=checkbox]):not([type=range]),textarea';
33
- export default function Dropdown({
34
- allowCreate,
35
- allowEmpty = true,
36
- children,
37
- className,
38
- disabled,
39
- hasItems = true,
40
- isOpenOnMount,
41
- isSearchable,
42
- keepOpenOnSubmit = !hasItems,
43
- label,
44
- name,
45
- onClick,
46
- onClose,
47
- onMouseDown,
48
- onMouseUp,
49
- onOpen,
50
- onSubmitItem,
51
- placeholder,
52
- style: styleFromProps,
53
- tabIndex,
54
- value,
55
- }) {
11
+ const noop = () => { }; // eslint-disable-line @typescript-eslint/no-empty-function
12
+ const CHILDREN_ERROR = '@acusti/dropdown requires either 1 child (the dropdown body) or 2 children: the dropdown trigger and the dropdown body.';
13
+ const TEXT_INPUT_SELECTOR = 'input:not([type=radio]):not([type=checkbox]):not([type=range]),textarea';
14
+ export default function Dropdown({ allowCreate, allowEmpty = true, children, className, disabled, hasItems = true, isOpenOnMount, isSearchable, keepOpenOnSubmit = !hasItems, label, name, onClick, onClose, onMouseDown, onMouseUp, onOpen, onSubmitItem, placeholder, style: styleFromProps, tabIndex, value, }) {
56
15
  const childrenCount = Children.count(children);
57
16
  if (childrenCount !== 1 && childrenCount !== 2) {
58
17
  if (childrenCount === 0) {
@@ -65,9 +24,7 @@ export default function Dropdown({
65
24
  trigger = children[0];
66
25
  }
67
26
  const isTriggerFromProps = React.isValidElement(trigger);
68
- const [isOpen, setIsOpen] = useState(
69
- isOpenOnMount !== null && isOpenOnMount !== void 0 ? isOpenOnMount : false,
70
- );
27
+ const [isOpen, setIsOpen] = useState(isOpenOnMount !== null && isOpenOnMount !== void 0 ? isOpenOnMount : false);
71
28
  const [isOpening, setIsOpening] = useState(!isOpenOnMount);
72
29
  const [dropdownBodyElement, setDropdownBodyElement] = useState(null);
73
30
  const dropdownElementRef = useRef(null);
@@ -125,7 +82,8 @@ export default function Dropdown({
125
82
  }
126
83
  if (isOpen && onOpenRef.current) {
127
84
  onOpenRef.current();
128
- } else if (!isOpen && onCloseRef.current) {
85
+ }
86
+ else if (!isOpen && onCloseRef.current) {
129
87
  onCloseRef.current();
130
88
  }
131
89
  }, [isOpen]);
@@ -138,94 +96,76 @@ export default function Dropdown({
138
96
  closingTimerRef.current = null;
139
97
  }
140
98
  }, []);
141
- const handleSubmitItem = useCallback(
142
- (event) => {
143
- var _a, _b, _c;
144
- const eventTarget = event.target;
145
- if (isOpenRef.current && !keepOpenOnSubmitRef.current) {
146
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
147
- const keepOpen = eventTarget.closest('[data-ukt-keep-open]');
148
- // Don’t close dropdown if event occurs w/in data-ukt-keep-open element
149
- if (
150
- !(keepOpen === null || keepOpen === void 0
151
- ? void 0
152
- : keepOpen.dataset.uktKeepOpen) ||
153
- keepOpen.dataset.uktKeepOpen === 'false'
154
- ) {
155
- // A short timeout before closing is better UX when user selects an item so dropdown
156
- // doesn’t close before expected. It also enables using <Link />s in the dropdown body.
157
- closingTimerRef.current = setTimeout(closeDropdown, 90);
158
- }
99
+ const handleSubmitItem = useCallback((event) => {
100
+ var _a, _b, _c;
101
+ const eventTarget = event.target;
102
+ if (isOpenRef.current && !keepOpenOnSubmitRef.current) {
103
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
104
+ const keepOpen = eventTarget.closest('[data-ukt-keep-open]');
105
+ // Don’t close dropdown if event occurs w/in data-ukt-keep-open element
106
+ if (!(keepOpen === null || keepOpen === void 0 ? void 0 : keepOpen.dataset.uktKeepOpen) ||
107
+ keepOpen.dataset.uktKeepOpen === 'false') {
108
+ // A short timeout before closing is better UX when user selects an item so dropdown
109
+ // doesn’t close before expected. It also enables using <Link />s in the dropdown body.
110
+ closingTimerRef.current = setTimeout(closeDropdown, 90);
159
111
  }
160
- if (!hasItemsRef.current) return;
161
- const element = getActiveItemElement(dropdownElementRef.current);
162
- if (!element && !allowCreateRef.current) {
163
- // If not allowEmpty, don’t allow submitting an empty item
164
- if (!allowEmptyRef.current) return;
165
- // If we have an input element as trigger & the user didn’t clear the text, do nothing
166
- if (
167
- (_a = inputElementRef.current) === null || _a === void 0
168
- ? void 0
169
- : _a.value
170
- )
171
- return;
112
+ }
113
+ if (!hasItemsRef.current)
114
+ return;
115
+ const element = getActiveItemElement(dropdownElementRef.current);
116
+ if (!element && !allowCreateRef.current) {
117
+ // If not allowEmpty, don’t allow submitting an empty item
118
+ if (!allowEmptyRef.current)
119
+ return;
120
+ // If we have an input element as trigger & the user didn’t clear the text, do nothing
121
+ if ((_a = inputElementRef.current) === null || _a === void 0 ? void 0 : _a.value)
122
+ return;
123
+ }
124
+ let label = (_b = element === null || element === void 0 ? void 0 : element.innerText) !== null && _b !== void 0 ? _b : '';
125
+ if (inputElementRef.current) {
126
+ if (!element) {
127
+ label = inputElementRef.current.value;
172
128
  }
173
- let label =
174
- (_b =
175
- element === null || element === void 0
176
- ? void 0
177
- : element.innerText) !== null && _b !== void 0
178
- ? _b
179
- : '';
180
- if (inputElementRef.current) {
181
- if (!element) {
182
- label = inputElementRef.current.value;
183
- } else {
184
- inputElementRef.current.value = label;
185
- }
186
- if (
187
- inputElementRef.current ===
188
- inputElementRef.current.ownerDocument.activeElement
189
- ) {
190
- inputElementRef.current.blur();
191
- }
129
+ else {
130
+ inputElementRef.current.value = label;
192
131
  }
193
- const nextValue =
194
- (_c =
195
- element === null || element === void 0
196
- ? void 0
197
- : element.dataset.uktValue) !== null && _c !== void 0
198
- ? _c
199
- : label;
200
- // If parent is controlling Dropdown via props.value and nextValue is the same, do nothing
201
- if (valueRef.current && valueRef.current === nextValue) return;
202
- if (onSubmitItemRef.current) {
203
- onSubmitItemRef.current({ element, event, label, value: nextValue });
132
+ if (inputElementRef.current ===
133
+ inputElementRef.current.ownerDocument.activeElement) {
134
+ inputElementRef.current.blur();
204
135
  }
205
- },
206
- [closeDropdown],
207
- );
136
+ }
137
+ const nextValue = (_c = element === null || element === void 0 ? void 0 : element.dataset.uktValue) !== null && _c !== void 0 ? _c : label;
138
+ // If parent is controlling Dropdown via props.value and nextValue is the same, do nothing
139
+ if (valueRef.current && valueRef.current === nextValue)
140
+ return;
141
+ if (onSubmitItemRef.current) {
142
+ onSubmitItemRef.current({ element, event, label, value: nextValue });
143
+ }
144
+ }, [closeDropdown]);
208
145
  const handleMouseMove = useCallback(({ clientX, clientY }) => {
209
146
  currentInputMethodRef.current = 'mouse';
210
147
  const initialPosition = mouseDownPositionRef.current;
211
- if (!initialPosition) return;
212
- if (
213
- Math.abs(initialPosition.clientX - clientX) < 12 &&
214
- Math.abs(initialPosition.clientY - clientY) < 12
215
- ) {
148
+ if (!initialPosition)
149
+ return;
150
+ if (Math.abs(initialPosition.clientX - clientX) < 12 &&
151
+ Math.abs(initialPosition.clientY - clientY) < 12) {
216
152
  return;
217
153
  }
218
154
  setIsOpening(false);
219
155
  }, []);
220
156
  const handleMouseOver = useCallback((event) => {
221
- if (!hasItemsRef.current) return;
157
+ if (!hasItemsRef.current)
158
+ return;
222
159
  // If user isn’t currently using the mouse to navigate the dropdown, do nothing
223
- if (currentInputMethodRef.current !== 'mouse') return;
160
+ if (currentInputMethodRef.current !== 'mouse')
161
+ return;
224
162
  // Ensure we have the dropdown root HTMLElement
225
163
  const dropdownElement = dropdownElementRef.current;
226
- if (!dropdownElement) return;
164
+ if (!dropdownElement)
165
+ return;
227
166
  const itemElements = getItemElements(dropdownElement);
228
- if (!itemElements) return;
167
+ if (!itemElements)
168
+ return;
229
169
  const eventTarget = event.target;
230
170
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
231
171
  const item = eventTarget.closest(ITEM_SELECTOR);
@@ -238,9 +178,11 @@ export default function Dropdown({
238
178
  }
239
179
  }, []);
240
180
  const handleMouseOut = useCallback((event) => {
241
- if (!hasItemsRef.current) return;
181
+ if (!hasItemsRef.current)
182
+ return;
242
183
  const activeItem = getActiveItemElement(dropdownElementRef.current);
243
- if (!activeItem) return;
184
+ if (!activeItem)
185
+ return;
244
186
  const eventRelatedTarget = event.relatedTarget;
245
187
  if (activeItem !== event.target || activeItem.contains(eventRelatedTarget)) {
246
188
  return;
@@ -248,371 +190,296 @@ export default function Dropdown({
248
190
  // If user moused out of activeItem (not into a descendant), it’s no longer active
249
191
  delete activeItem.dataset.uktActive;
250
192
  }, []);
251
- const handleMouseDown = useCallback(
252
- (event) => {
253
- if (onMouseDown) onMouseDown(event);
254
- if (isOpenRef.current) return;
255
- setIsOpen(true);
256
- setIsOpening(true);
257
- mouseDownPositionRef.current = {
258
- clientX: event.clientX,
259
- clientY: event.clientY,
260
- };
261
- isOpeningTimerRef.current = setTimeout(() => {
262
- setIsOpening(false);
263
- isOpeningTimerRef.current = null;
264
- }, 1000);
265
- },
266
- [onMouseDown],
267
- );
268
- const handleMouseUp = useCallback(
269
- (event) => {
270
- if (onMouseUp) onMouseUp(event);
271
- // If dropdown is still opening or isn’t open or is closing, do nothing
272
- if (isOpeningRef.current || !isOpenRef.current || closingTimerRef.current) {
273
- return;
193
+ const handleMouseDown = useCallback((event) => {
194
+ if (onMouseDown)
195
+ onMouseDown(event);
196
+ if (isOpenRef.current)
197
+ return;
198
+ setIsOpen(true);
199
+ setIsOpening(true);
200
+ mouseDownPositionRef.current = {
201
+ clientX: event.clientX,
202
+ clientY: event.clientY,
203
+ };
204
+ isOpeningTimerRef.current = setTimeout(() => {
205
+ setIsOpening(false);
206
+ isOpeningTimerRef.current = null;
207
+ }, 1000);
208
+ }, [onMouseDown]);
209
+ const handleMouseUp = useCallback((event) => {
210
+ if (onMouseUp)
211
+ onMouseUp(event);
212
+ // If dropdown is still opening or isn’t open or is closing, do nothing
213
+ if (isOpeningRef.current || !isOpenRef.current || closingTimerRef.current) {
214
+ return;
215
+ }
216
+ const eventTarget = event.target;
217
+ // If click was outside dropdown body, don’t trigger submit
218
+ if (!eventTarget.closest(BODY_SELECTOR)) {
219
+ // Don’t close dropdown if isOpening or search input is focused
220
+ if (!isOpeningRef.current &&
221
+ inputElementRef.current !== eventTarget.ownerDocument.activeElement) {
222
+ closeDropdown();
274
223
  }
275
- const eventTarget = event.target;
276
- // If click was outside dropdown body, don’t trigger submit
277
- if (!eventTarget.closest(BODY_SELECTOR)) {
278
- // Don’t close dropdown if isOpening or search input is focused
279
- if (
280
- !isOpeningRef.current &&
281
- inputElementRef.current !== eventTarget.ownerDocument.activeElement
282
- ) {
283
- closeDropdown();
284
- }
224
+ return;
225
+ }
226
+ // If dropdown has no items and click was within dropdown body, do nothing
227
+ if (!hasItemsRef.current)
228
+ return;
229
+ handleSubmitItem(event);
230
+ }, [closeDropdown, handleSubmitItem, onMouseUp]);
231
+ const handleKeyDown = useCallback((event) => {
232
+ const { altKey, ctrlKey, key, metaKey } = event;
233
+ const eventTarget = event.target;
234
+ const dropdownElement = dropdownElementRef.current;
235
+ if (!dropdownElement)
236
+ return;
237
+ const onEventHandled = () => {
238
+ event.stopPropagation();
239
+ event.preventDefault();
240
+ currentInputMethodRef.current = 'keyboard';
241
+ };
242
+ const isEventTargetingDropdown = dropdownElement.contains(eventTarget);
243
+ if (!isOpenRef.current) {
244
+ // If dropdown is closed, don’t handle key events if event target isn’t within dropdown
245
+ if (!isEventTargetingDropdown)
285
246
  return;
247
+ // Open the dropdown on spacebar, enter, or if isSearchable and user hits the ↑/↓ arrows
248
+ if (key === ' ' ||
249
+ key === 'Enter' ||
250
+ (hasItemsRef.current && (key === 'ArrowUp' || key === 'ArrowDown'))) {
251
+ onEventHandled();
252
+ setIsOpen(true);
286
253
  }
287
- // If dropdown has no items and click was within dropdown body, do nothing
288
- if (!hasItemsRef.current) return;
289
- handleSubmitItem(event);
290
- },
291
- [closeDropdown, handleSubmitItem, onMouseUp],
292
- );
293
- const handleKeyDown = useCallback(
294
- (event) => {
295
- const { altKey, ctrlKey, key, metaKey } = event;
296
- const eventTarget = event.target;
297
- const dropdownElement = dropdownElementRef.current;
298
- if (!dropdownElement) return;
299
- const onEventHandled = () => {
300
- event.stopPropagation();
301
- event.preventDefault();
302
- currentInputMethodRef.current = 'keyboard';
303
- };
304
- const isEventTargetingDropdown = dropdownElement.contains(eventTarget);
305
- if (!isOpenRef.current) {
306
- // If dropdown is closed, don’t handle key events if event target isn’t within dropdown
307
- if (!isEventTargetingDropdown) return;
308
- // Open the dropdown on spacebar, enter, or if isSearchable and user hits the ↑/↓ arrows
309
- if (
310
- key === ' ' ||
311
- key === 'Enter' ||
312
- (hasItemsRef.current && (key === 'ArrowUp' || key === 'ArrowDown'))
313
- ) {
314
- onEventHandled();
315
- setIsOpen(true);
316
- }
317
- return;
254
+ return;
255
+ }
256
+ const isTargetUsingKeyEvents = isEventTargetUsingKeyEvent(event);
257
+ // If dropdown isOpen + hasItems & eventTargetNotUsingKeyEvents, handle characters
258
+ if (hasItemsRef.current && !isTargetUsingKeyEvents) {
259
+ let isEditingCharacters = !ctrlKey && !metaKey && /^[A-Za-z0-9]$/.test(key);
260
+ // User could also be editing characters if there are already characters entered
261
+ // and they are hitting delete or spacebar
262
+ if (!isEditingCharacters && enteredCharactersRef.current) {
263
+ isEditingCharacters = key === ' ' || key === 'Backspace';
318
264
  }
319
- const isTargetUsingKeyEvents = isEventTargetUsingKeyEvent(event);
320
- // If dropdown isOpen + hasItems & eventTargetNotUsingKeyEvents, handle characters
321
- if (hasItemsRef.current && !isTargetUsingKeyEvents) {
322
- let isEditingCharacters =
323
- !ctrlKey && !metaKey && /^[A-Za-z0-9]$/.test(key);
324
- // User could also be editing characters if there are already characters entered
325
- // and they are hitting delete or spacebar
326
- if (!isEditingCharacters && enteredCharactersRef.current) {
327
- isEditingCharacters = key === ' ' || key === 'Backspace';
265
+ if (isEditingCharacters) {
266
+ onEventHandled();
267
+ if (key === 'Backspace') {
268
+ enteredCharactersRef.current = enteredCharactersRef.current.slice(0, -1);
328
269
  }
329
- if (isEditingCharacters) {
330
- onEventHandled();
331
- if (key === 'Backspace') {
332
- enteredCharactersRef.current = enteredCharactersRef.current.slice(
333
- 0,
334
- -1,
335
- );
336
- } else {
337
- enteredCharactersRef.current += key;
338
- }
339
- setActiveItem({
340
- dropdownElement,
341
- // If props.allowCreate, only override the input’s value with an
342
- // exact text match so user can enter a value not in items
343
- isExactMatch: allowCreateRef.current,
344
- text: enteredCharactersRef.current,
345
- });
346
- if (clearEnteredCharactersTimerRef.current) {
347
- clearTimeout(clearEnteredCharactersTimerRef.current);
348
- }
349
- clearEnteredCharactersTimerRef.current = setTimeout(() => {
350
- enteredCharactersRef.current = '';
351
- clearEnteredCharactersTimerRef.current = null;
352
- }, 1500);
353
- return;
270
+ else {
271
+ enteredCharactersRef.current += key;
354
272
  }
355
- }
356
- // If dropdown isOpen, handle submitting the value
357
- if (key === 'Enter' || (key === ' ' && !inputElementRef.current)) {
358
- onEventHandled();
359
- handleSubmitItem(event);
273
+ setActiveItem({
274
+ dropdownElement,
275
+ // If props.allowCreate, only override the input’s value with an
276
+ // exact text match so user can enter a value not in items
277
+ isExactMatch: allowCreateRef.current,
278
+ text: enteredCharactersRef.current,
279
+ });
280
+ if (clearEnteredCharactersTimerRef.current) {
281
+ clearTimeout(clearEnteredCharactersTimerRef.current);
282
+ }
283
+ clearEnteredCharactersTimerRef.current = setTimeout(() => {
284
+ enteredCharactersRef.current = '';
285
+ clearEnteredCharactersTimerRef.current = null;
286
+ }, 1500);
360
287
  return;
361
288
  }
362
- // If dropdown isOpen, handle closing it on escape or spacebar if !hasItems
363
- if (
364
- key === 'Escape' ||
365
- (isEventTargetingDropdown && key === ' ' && !hasItemsRef.current)
366
- ) {
367
- // Close dropdown if hasItems or event target not using key events
368
- if (hasItemsRef.current || !isTargetUsingKeyEvents) {
369
- closeDropdown();
289
+ }
290
+ // If dropdown isOpen, handle submitting the value
291
+ if (key === 'Enter' || (key === ' ' && !inputElementRef.current)) {
292
+ onEventHandled();
293
+ handleSubmitItem(event);
294
+ return;
295
+ }
296
+ // If dropdown isOpen, handle closing it on escape or spacebar if !hasItems
297
+ if (key === 'Escape' ||
298
+ (isEventTargetingDropdown && key === ' ' && !hasItemsRef.current)) {
299
+ // Close dropdown if hasItems or event target not using key events
300
+ if (hasItemsRef.current || !isTargetUsingKeyEvents) {
301
+ closeDropdown();
302
+ }
303
+ return;
304
+ }
305
+ // Handle ↑/↓ arrows
306
+ if (hasItemsRef.current) {
307
+ if (key === 'ArrowUp') {
308
+ onEventHandled();
309
+ if (altKey || metaKey) {
310
+ setActiveItem({ dropdownElement, index: 0 });
311
+ }
312
+ else {
313
+ setActiveItem({ dropdownElement, indexAddend: -1 });
370
314
  }
371
315
  return;
372
316
  }
373
- // Handle ↑/↓ arrows
374
- if (hasItemsRef.current) {
375
- if (key === 'ArrowUp') {
376
- onEventHandled();
377
- if (altKey || metaKey) {
378
- setActiveItem({ dropdownElement, index: 0 });
379
- } else {
380
- setActiveItem({ dropdownElement, indexAddend: -1 });
381
- }
382
- return;
317
+ if (key === 'ArrowDown') {
318
+ onEventHandled();
319
+ if (altKey || metaKey) {
320
+ // Using a negative index counts back from the end
321
+ setActiveItem({ dropdownElement, index: -1 });
383
322
  }
384
- if (key === 'ArrowDown') {
385
- onEventHandled();
386
- if (altKey || metaKey) {
387
- // Using a negative index counts back from the end
388
- setActiveItem({ dropdownElement, index: -1 });
389
- } else {
390
- setActiveItem({ dropdownElement, indexAddend: 1 });
391
- }
392
- return;
323
+ else {
324
+ setActiveItem({ dropdownElement, indexAddend: 1 });
393
325
  }
326
+ return;
394
327
  }
395
- },
396
- [closeDropdown, handleSubmitItem],
397
- );
328
+ }
329
+ }, [closeDropdown, handleSubmitItem]);
398
330
  useKeyboardEvents({ ignoreUsedKeyboardEvents: false, onKeyDown: handleKeyDown });
399
331
  const cleanupEventListenersRef = useRef(noop);
400
- const handleRef = useCallback(
401
- (ref) => {
402
- dropdownElementRef.current = ref;
403
- if (!ref) {
404
- // If component was unmounted, cleanup handlers
405
- cleanupEventListenersRef.current();
406
- cleanupEventListenersRef.current = noop;
407
- return;
332
+ const handleRef = useCallback((ref) => {
333
+ dropdownElementRef.current = ref;
334
+ if (!ref) {
335
+ // If component was unmounted, cleanup handlers
336
+ cleanupEventListenersRef.current();
337
+ cleanupEventListenersRef.current = noop;
338
+ return;
339
+ }
340
+ const { ownerDocument } = ref;
341
+ let inputElement = inputElementRef.current;
342
+ // Check if trigger from props is a textual input or textarea element
343
+ if (isTriggerFromProps && !inputElement && ref.firstElementChild) {
344
+ if (ref.firstElementChild.matches(TEXT_INPUT_SELECTOR)) {
345
+ inputElement = ref.firstElementChild;
408
346
  }
409
- const { ownerDocument } = ref;
410
- let inputElement = inputElementRef.current;
411
- // Check if trigger from props is a textual input or textarea element
412
- if (isTriggerFromProps && !inputElement && ref.firstElementChild) {
413
- if (ref.firstElementChild.matches(TEXT_INPUT_SELECTOR)) {
414
- inputElement = ref.firstElementChild;
415
- } else {
416
- inputElement =
417
- ref.firstElementChild.querySelector(TEXT_INPUT_SELECTOR);
418
- }
419
- inputElementRef.current = inputElement;
347
+ else {
348
+ inputElement =
349
+ ref.firstElementChild.querySelector(TEXT_INPUT_SELECTOR);
420
350
  }
421
- const handleGlobalMouseDown = ({ target }) => {
422
- const eventTarget = target;
423
- if (
424
- dropdownElementRef.current &&
425
- !dropdownElementRef.current.contains(eventTarget)
426
- ) {
427
- // Close dropdown on an outside click
428
- closeDropdown();
429
- }
430
- };
431
- const handleGlobalMouseUp = ({ target }) => {
432
- var _a;
433
- if (!isOpenRef.current || closingTimerRef.current) return;
434
- // If still isOpening (gets set false 1s after open triggers), set it to false onMouseUp
435
- if (isOpeningRef.current) {
436
- setIsOpening(false);
437
- if (isOpeningTimerRef.current) {
438
- clearTimeout(isOpeningTimerRef.current);
439
- isOpeningTimerRef.current = null;
440
- }
441
- return;
442
- }
443
- const eventTarget = target;
444
- // Only handle mouseup events from outside the dropdown here
445
- if (
446
- !((_a = dropdownElementRef.current) === null || _a === void 0
447
- ? void 0
448
- : _a.contains(eventTarget))
449
- ) {
450
- closeDropdown();
451
- }
452
- };
453
- // Close dropdown if any element is focused outside of this dropdown
454
- const handleGlobalFocusIn = ({ target }) => {
455
- if (!isOpenRef.current) return;
456
- const eventTarget = target;
457
- // If focused element is a descendant or a parent of the dropdown, do nothing
458
- if (
459
- !dropdownElementRef.current ||
460
- dropdownElementRef.current.contains(eventTarget) ||
461
- eventTarget.contains(dropdownElementRef.current)
462
- ) {
463
- return;
351
+ inputElementRef.current = inputElement;
352
+ }
353
+ const handleGlobalMouseDown = ({ target }) => {
354
+ const eventTarget = target;
355
+ if (dropdownElementRef.current &&
356
+ !dropdownElementRef.current.contains(eventTarget)) {
357
+ // Close dropdown on an outside click
358
+ closeDropdown();
359
+ }
360
+ };
361
+ const handleGlobalMouseUp = ({ target }) => {
362
+ var _a;
363
+ if (!isOpenRef.current || closingTimerRef.current)
364
+ return;
365
+ // If still isOpening (gets set false 1s after open triggers), set it to false onMouseUp
366
+ if (isOpeningRef.current) {
367
+ setIsOpening(false);
368
+ if (isOpeningTimerRef.current) {
369
+ clearTimeout(isOpeningTimerRef.current);
370
+ isOpeningTimerRef.current = null;
464
371
  }
372
+ return;
373
+ }
374
+ const eventTarget = target;
375
+ // Only handle mouseup events from outside the dropdown here
376
+ if (!((_a = dropdownElementRef.current) === null || _a === void 0 ? void 0 : _a.contains(eventTarget))) {
465
377
  closeDropdown();
466
- };
467
- document.addEventListener('focusin', handleGlobalFocusIn);
468
- document.addEventListener('mousedown', handleGlobalMouseDown);
469
- document.addEventListener('mouseup', handleGlobalMouseUp);
470
- if (ownerDocument !== document) {
471
- ownerDocument.addEventListener('focusin', handleGlobalFocusIn);
472
- ownerDocument.addEventListener('mousedown', handleGlobalMouseDown);
473
- ownerDocument.addEventListener('mouseup', handleGlobalMouseUp);
474
378
  }
475
- // If dropdown should be open on mount, focus it
476
- if (isOpenOnMount) {
477
- ref.focus();
379
+ };
380
+ // Close dropdown if any element is focused outside of this dropdown
381
+ const handleGlobalFocusIn = ({ target }) => {
382
+ if (!isOpenRef.current)
383
+ return;
384
+ const eventTarget = target;
385
+ // If focused element is a descendant or a parent of the dropdown, do nothing
386
+ if (!dropdownElementRef.current ||
387
+ dropdownElementRef.current.contains(eventTarget) ||
388
+ eventTarget.contains(dropdownElementRef.current)) {
389
+ return;
390
+ }
391
+ closeDropdown();
392
+ };
393
+ document.addEventListener('focusin', handleGlobalFocusIn);
394
+ document.addEventListener('mousedown', handleGlobalMouseDown);
395
+ document.addEventListener('mouseup', handleGlobalMouseUp);
396
+ if (ownerDocument !== document) {
397
+ ownerDocument.addEventListener('focusin', handleGlobalFocusIn);
398
+ ownerDocument.addEventListener('mousedown', handleGlobalMouseDown);
399
+ ownerDocument.addEventListener('mouseup', handleGlobalMouseUp);
400
+ }
401
+ // If dropdown should be open on mount, focus it
402
+ if (isOpenOnMount) {
403
+ ref.focus();
404
+ }
405
+ const handleInput = (event) => {
406
+ const dropdownElement = dropdownElementRef.current;
407
+ if (!dropdownElement)
408
+ return;
409
+ if (!isOpenRef.current)
410
+ setIsOpen(true);
411
+ const input = event.target;
412
+ const isDeleting = enteredCharactersRef.current.length > input.value.length;
413
+ enteredCharactersRef.current = input.value;
414
+ // When deleting text, if there’s already an active item and
415
+ // input isn’t empty, preserve the active item, else update it
416
+ if (isDeleting &&
417
+ input.value.length &&
418
+ getActiveItemElement(dropdownElement)) {
419
+ return;
420
+ }
421
+ setActiveItem({
422
+ dropdownElement,
423
+ // If props.allowCreate, only override the input’s value with an
424
+ // exact text match so user can enter a value not in items
425
+ isExactMatch: allowCreateRef.current,
426
+ text: enteredCharactersRef.current,
427
+ });
428
+ };
429
+ if (inputElement) {
430
+ inputElement.addEventListener('input', handleInput);
431
+ }
432
+ cleanupEventListenersRef.current = () => {
433
+ document.removeEventListener('focusin', handleGlobalFocusIn);
434
+ document.removeEventListener('mousedown', handleGlobalMouseDown);
435
+ document.removeEventListener('mouseup', handleGlobalMouseUp);
436
+ if (ownerDocument !== document) {
437
+ ownerDocument.removeEventListener('focusin', handleGlobalFocusIn);
438
+ ownerDocument.removeEventListener('mousedown', handleGlobalMouseDown);
439
+ ownerDocument.removeEventListener('mouseup', handleGlobalMouseUp);
478
440
  }
479
- const handleInput = (event) => {
480
- const dropdownElement = dropdownElementRef.current;
481
- if (!dropdownElement) return;
482
- if (!isOpenRef.current) setIsOpen(true);
483
- const input = event.target;
484
- const isDeleting =
485
- enteredCharactersRef.current.length > input.value.length;
486
- enteredCharactersRef.current = input.value;
487
- // When deleting text, if there’s already an active item and
488
- // input isn’t empty, preserve the active item, else update it
489
- if (
490
- isDeleting &&
491
- input.value.length &&
492
- getActiveItemElement(dropdownElement)
493
- ) {
494
- return;
495
- }
496
- setActiveItem({
497
- dropdownElement,
498
- // If props.allowCreate, only override the input’s value with an
499
- // exact text match so user can enter a value not in items
500
- isExactMatch: allowCreateRef.current,
501
- text: enteredCharactersRef.current,
502
- });
503
- };
504
441
  if (inputElement) {
505
- inputElement.addEventListener('input', handleInput);
442
+ inputElement.removeEventListener('input', handleInput);
506
443
  }
507
- cleanupEventListenersRef.current = () => {
508
- document.removeEventListener('focusin', handleGlobalFocusIn);
509
- document.removeEventListener('mousedown', handleGlobalMouseDown);
510
- document.removeEventListener('mouseup', handleGlobalMouseUp);
511
- if (ownerDocument !== document) {
512
- ownerDocument.removeEventListener('focusin', handleGlobalFocusIn);
513
- ownerDocument.removeEventListener('mousedown', handleGlobalMouseDown);
514
- ownerDocument.removeEventListener('mouseup', handleGlobalMouseUp);
515
- }
516
- if (inputElement) {
517
- inputElement.removeEventListener('input', handleInput);
518
- }
519
- };
520
- },
521
- [closeDropdown, isOpenOnMount, isTriggerFromProps],
522
- );
444
+ };
445
+ }, [closeDropdown, isOpenOnMount, isTriggerFromProps]);
523
446
  if (!isTriggerFromProps) {
524
447
  if (isSearchable) {
525
- trigger = React.createElement(InputText, {
526
- autoComplete: 'off',
527
- className: TRIGGER_CLASS_NAME,
528
- disabled: disabled,
529
- initialValue: value !== null && value !== void 0 ? value : '',
530
- name: name,
531
- onFocus: setDropdownOpenRef.current,
532
- placeholder: placeholder,
533
- ref: inputElementRef,
534
- selectTextOnFocus: true,
535
- tabIndex: tabIndex,
536
- type: 'text',
537
- });
538
- } else {
539
- trigger = React.createElement(
540
- 'button',
541
- { className: TRIGGER_CLASS_NAME, tabIndex: 0 },
542
- trigger,
543
- );
448
+ trigger = (React.createElement(InputText, { autoComplete: "off", className: TRIGGER_CLASS_NAME, disabled: disabled, initialValue: value !== null && value !== void 0 ? value : '', name: name, onFocus: setDropdownOpenRef.current, placeholder: placeholder, ref: inputElementRef, selectTextOnFocus: true, tabIndex: tabIndex, type: "text" }));
449
+ }
450
+ else {
451
+ trigger = (React.createElement("button", { className: TRIGGER_CLASS_NAME, tabIndex: 0 }, trigger));
544
452
  }
545
453
  }
546
454
  if (label) {
547
- trigger = React.createElement(
548
- 'label',
549
- { className: LABEL_CLASS_NAME },
550
- React.createElement('div', { className: LABEL_TEXT_CLASS_NAME }, label),
551
- trigger,
552
- );
455
+ trigger = (React.createElement("label", { className: LABEL_CLASS_NAME },
456
+ React.createElement("div", { className: LABEL_TEXT_CLASS_NAME }, label),
457
+ trigger));
553
458
  }
554
- const style = useMemo(
555
- () =>
556
- Object.assign(
557
- Object.assign(
558
- Object.assign({}, styleFromProps),
559
- outOfBounds.maxHeight
560
- ? {
561
- [BODY_MAX_HEIGHT_VAR]: `calc(${outOfBounds.maxHeight}px - var(--uktdd-body-buffer))`,
562
- }
563
- : null,
564
- ),
565
- outOfBounds.maxWidth
566
- ? {
567
- [BODY_MAX_WIDTH_VAR]: `calc(${outOfBounds.maxWidth}px - var(--uktdd-body-buffer))`,
568
- }
569
- : null,
570
- ),
571
- [outOfBounds.maxHeight, outOfBounds.maxWidth, styleFromProps],
572
- );
573
- return React.createElement(
574
- Fragment,
575
- null,
459
+ const style = useMemo(() => (Object.assign(Object.assign(Object.assign({}, styleFromProps), (outOfBounds.maxHeight
460
+ ? {
461
+ [BODY_MAX_HEIGHT_VAR]: `calc(${outOfBounds.maxHeight}px - var(--uktdd-body-buffer))`,
462
+ }
463
+ : null)), (outOfBounds.maxWidth
464
+ ? {
465
+ [BODY_MAX_WIDTH_VAR]: `calc(${outOfBounds.maxWidth}px - var(--uktdd-body-buffer))`,
466
+ }
467
+ : null))), [outOfBounds.maxHeight, outOfBounds.maxWidth, styleFromProps]);
468
+ return (React.createElement(Fragment, null,
576
469
  React.createElement(Style, null, STYLES),
577
- React.createElement(
578
- 'div',
579
- {
580
- className: clsx(ROOT_CLASS_NAME, className, {
581
- disabled,
582
- 'is-open': isOpen,
583
- 'is-searchable': isSearchable,
584
- }),
585
- onClick: onClick,
586
- onMouseDown: handleMouseDown,
587
- onMouseMove: handleMouseMove,
588
- onMouseOut: handleMouseOut,
589
- onMouseOver: handleMouseOver,
590
- onMouseUp: handleMouseUp,
591
- ref: handleRef,
592
- style: style,
593
- },
470
+ React.createElement("div", { className: clsx(ROOT_CLASS_NAME, className, {
471
+ disabled,
472
+ 'is-open': isOpen,
473
+ 'is-searchable': isSearchable,
474
+ }), onClick: onClick, onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseOut: handleMouseOut, onMouseOver: handleMouseOver, onMouseUp: handleMouseUp, ref: handleRef, style: style },
594
475
  trigger,
595
- isOpen
596
- ? React.createElement(
597
- 'div',
598
- {
599
- className: clsx(BODY_CLASS_NAME, {
600
- 'calculating-position': !outOfBounds.hasLayout,
601
- 'has-items': hasItems,
602
- 'out-of-bounds-bottom':
603
- outOfBounds.bottom && !outOfBounds.top,
604
- 'out-of-bounds-left':
605
- outOfBounds.left && !outOfBounds.right,
606
- 'out-of-bounds-right':
607
- outOfBounds.right && !outOfBounds.left,
608
- 'out-of-bounds-top': outOfBounds.top && !outOfBounds.bottom,
609
- }),
610
- ref: setDropdownBodyElement,
611
- },
612
- childrenCount > 1 ? children[1] : children,
613
- )
614
- : null,
615
- ),
616
- );
476
+ isOpen ? (React.createElement("div", { className: clsx(BODY_CLASS_NAME, {
477
+ 'calculating-position': !outOfBounds.hasLayout,
478
+ 'has-items': hasItems,
479
+ 'out-of-bounds-bottom': outOfBounds.bottom && !outOfBounds.top,
480
+ 'out-of-bounds-left': outOfBounds.left && !outOfBounds.right,
481
+ 'out-of-bounds-right': outOfBounds.right && !outOfBounds.left,
482
+ 'out-of-bounds-top': outOfBounds.top && !outOfBounds.bottom,
483
+ }), ref: setDropdownBodyElement }, childrenCount > 1 ? children[1] : children)) : null)));
617
484
  }
618
- //# sourceMappingURL=Dropdown.js.map
485
+ //# sourceMappingURL=Dropdown.js.map