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