@allxsmith/bestax-bulma 5.34.1 → 5.34.2
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/esm/components/Dropdown.js +8 -3
- package/dist/esm/components/Dropdown.js.map +1 -1
- package/dist/esm/components/Loading.js +5 -4
- package/dist/esm/components/Loading.js.map +1 -1
- package/dist/esm/components/Modal.js +11 -4
- package/dist/esm/components/Modal.js.map +1 -1
- package/dist/esm/components/Sidebar.js +5 -4
- package/dist/esm/components/Sidebar.js.map +1 -1
- package/dist/esm/components/Toast.js +7 -5
- package/dist/esm/components/Toast.js.map +1 -1
- package/dist/esm/form/Autocomplete.js +17 -2
- package/dist/esm/form/Autocomplete.js.map +1 -1
- package/dist/esm/form/Taginput.js +4 -0
- package/dist/esm/form/Taginput.js.map +1 -1
- package/dist/esm/form/_pickerInternals/PickerPopover.js +11 -3
- package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -1
- package/dist/index.cjs +67 -25
- package/dist/index.cjs.map +1 -1
- package/dist/types/components/Loading.d.ts +5 -1
- package/dist/types/components/Modal.d.ts +4 -1
- package/dist/types/components/Sidebar.d.ts +5 -1
- package/dist/types/components/Toast.d.ts +4 -1
- package/dist/types/form/Autocomplete.d.ts +13 -2
- package/dist/types-cjs/components/Loading.d.ts +5 -1
- package/dist/types-cjs/components/Modal.d.ts +4 -1
- package/dist/types-cjs/components/Sidebar.d.ts +5 -1
- package/dist/types-cjs/components/Toast.d.ts +4 -1
- package/dist/types-cjs/form/Autocomplete.d.ts +13 -2
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { createPortal } from 'react-dom';
|
|
|
5
5
|
import { usePrefixedClassNames, classNames } from '../../helpers/classNames.js';
|
|
6
6
|
import { useFocusTrap } from '../../helpers/useFocusTrap.js';
|
|
7
7
|
import { usePortalContainer } from '../../helpers/portal.js';
|
|
8
|
+
import { isEventInside } from '../../helpers/shadowDom.js';
|
|
8
9
|
import { useAnchoredPosition, marginGap } from '../../helpers/useAnchoredPosition.js';
|
|
9
10
|
|
|
10
11
|
const isBrowser = typeof window !== 'undefined';
|
|
@@ -48,13 +49,20 @@ const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'botto
|
|
|
48
49
|
return () => document.removeEventListener('pointerdown', handler);
|
|
49
50
|
}, [isOpen, closeOnClickOutside, anchorRef, onClose]);
|
|
50
51
|
// Escape closes it, and so does Alt+ArrowUp, the combobox's key for it,
|
|
51
|
-
// which the calendar and the time wheels inside leave to it.
|
|
52
|
+
// which the calendar and the time wheels inside leave to it. Escape closes
|
|
53
|
+
// it from anywhere, but the key is only the picker's when it was pressed in
|
|
54
|
+
// the panel or the field: then it's marked used, so a Modal or Sidebar
|
|
55
|
+
// around the picker stays open. From anywhere else, such as a dialog opened
|
|
56
|
+
// over the picker, the key goes on to whatever has focus.
|
|
52
57
|
useEffect(() => {
|
|
53
58
|
if (!isOpen || !closeOnEscape || !isBrowser)
|
|
54
59
|
return undefined;
|
|
55
60
|
const handler = (e) => {
|
|
56
61
|
if (e.key === 'Escape') {
|
|
57
|
-
e.
|
|
62
|
+
if (isEventInside(e, panelRef.current) ||
|
|
63
|
+
isEventInside(e, anchorRef.current)) {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
}
|
|
58
66
|
onClose();
|
|
59
67
|
}
|
|
60
68
|
else if (e.altKey && e.key === 'ArrowUp') {
|
|
@@ -65,7 +73,7 @@ const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'botto
|
|
|
65
73
|
};
|
|
66
74
|
document.addEventListener('keydown', handler);
|
|
67
75
|
return () => document.removeEventListener('keydown', handler);
|
|
68
|
-
}, [isOpen, closeOnEscape, onClose]);
|
|
76
|
+
}, [isOpen, closeOnEscape, anchorRef, onClose]);
|
|
69
77
|
useFocusTrap(panelRef, {
|
|
70
78
|
active: isOpen && trapFocus,
|
|
71
79
|
initialFocusRef,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PickerPopover.js","sources":["../../../../src/form/_pickerInternals/PickerPopover.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { classNames, usePrefixedClassNames } from '../../helpers/classNames';\nimport { useFocusTrap } from '../../helpers/useFocusTrap';\nimport { usePortalContainer } from '../../helpers/portal';\nimport {\n marginGap,\n useAnchoredPosition,\n} from '../../helpers/useAnchoredPosition';\nimport { PickerPosition } from './pickerTypes';\n\nexport interface PickerPopoverProps {\n isOpen: boolean;\n onClose: () => void;\n anchorRef: React.RefObject<HTMLElement | null>;\n children: React.ReactNode;\n position?: PickerPosition;\n appendToBody?: boolean;\n className?: string;\n trapFocus?: boolean;\n closeOnClickOutside?: boolean;\n /** Close on Escape and on Alt+ArrowUp. Default `true`. */\n closeOnEscape?: boolean;\n ariaLabel?: string;\n ariaLabelledBy?: string;\n role?: 'dialog' | 'group';\n id?: string;\n /**\n * Where focus goes when the popover closes. Without it, focus goes back to\n * whatever had it when the focus trap turned on, which is read after the\n * panel's content mounts: a calendar that focuses a cell as it mounts\n * would be recorded instead, and that cell leaves with the panel.\n */\n restoreFocusRef?: React.RefObject<HTMLElement | null>;\n /**\n * Selects the element in the panel that takes focus as it opens, such as a\n * calendar's focused date rather than the first button in its header. When\n * nothing matches, or the match can't take focus, the first tab stop does.\n */\n initialFocusSelector?: string;\n}\n\nconst isBrowser = typeof window !== 'undefined';\n\nexport const PickerPopover: React.FC<PickerPopoverProps> = ({\n isOpen,\n onClose,\n anchorRef,\n children,\n position = 'bottom-left',\n appendToBody = false,\n className,\n trapFocus = true,\n closeOnClickOutside = true,\n closeOnEscape = true,\n ariaLabel,\n ariaLabelledBy,\n role = 'dialog',\n id,\n restoreFocusRef,\n initialFocusSelector,\n}) => {\n const panelRef = useRef<HTMLDivElement>(null);\n const resolveContainer = usePortalContainer();\n // Read as the trap turns on, once the panel's content has rendered.\n const initialFocusRef = useMemo(\n () =>\n initialFocusSelector === undefined\n ? undefined\n : {\n get current() {\n return (\n panelRef.current?.querySelector<HTMLElement>(\n initialFocusSelector\n ) ?? null\n );\n },\n },\n [initialFocusSelector]\n );\n const resolved = useAnchoredPosition(anchorRef, panelRef, {\n active: isOpen,\n position,\n fixed: appendToBody,\n // The gap comes from `--bulma-picker-popover-offset` as a margin, in place\n // or portaled, so the coordinates leave it out and `auto` reads it off\n // the panel.\n offset: 0,\n styledGap: marginGap,\n });\n\n useEffect(() => {\n if (!isOpen || !closeOnClickOutside || !isBrowser) return undefined;\n // Listen on `pointerdown` (rather than `mousedown`) so touch starts\n // outside the popover dismiss it on mobile — including swipe-from-outside\n // gestures, where the synthetic mouse event would otherwise lag a few\n // hundred ms behind the actual touch.\n const handler = (e: PointerEvent) => {\n const path = e.composedPath();\n if (panelRef.current && path.includes(panelRef.current)) return;\n if (anchorRef.current && path.includes(anchorRef.current)) return;\n onClose();\n };\n document.addEventListener('pointerdown', handler);\n return () => document.removeEventListener('pointerdown', handler);\n }, [isOpen, closeOnClickOutside, anchorRef, onClose]);\n\n // Escape closes it, and so does Alt+ArrowUp, the combobox's key for it,\n // which the calendar and the time wheels inside leave to it.\n useEffect(() => {\n if (!isOpen || !closeOnEscape || !isBrowser) return undefined;\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.
|
|
1
|
+
{"version":3,"file":"PickerPopover.js","sources":["../../../../src/form/_pickerInternals/PickerPopover.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { classNames, usePrefixedClassNames } from '../../helpers/classNames';\nimport { useFocusTrap } from '../../helpers/useFocusTrap';\nimport { usePortalContainer } from '../../helpers/portal';\nimport { isEventInside } from '../../helpers/shadowDom';\nimport {\n marginGap,\n useAnchoredPosition,\n} from '../../helpers/useAnchoredPosition';\nimport { PickerPosition } from './pickerTypes';\n\nexport interface PickerPopoverProps {\n isOpen: boolean;\n onClose: () => void;\n anchorRef: React.RefObject<HTMLElement | null>;\n children: React.ReactNode;\n position?: PickerPosition;\n appendToBody?: boolean;\n className?: string;\n trapFocus?: boolean;\n closeOnClickOutside?: boolean;\n /** Close on Escape and on Alt+ArrowUp. Default `true`. */\n closeOnEscape?: boolean;\n ariaLabel?: string;\n ariaLabelledBy?: string;\n role?: 'dialog' | 'group';\n id?: string;\n /**\n * Where focus goes when the popover closes. Without it, focus goes back to\n * whatever had it when the focus trap turned on, which is read after the\n * panel's content mounts: a calendar that focuses a cell as it mounts\n * would be recorded instead, and that cell leaves with the panel.\n */\n restoreFocusRef?: React.RefObject<HTMLElement | null>;\n /**\n * Selects the element in the panel that takes focus as it opens, such as a\n * calendar's focused date rather than the first button in its header. When\n * nothing matches, or the match can't take focus, the first tab stop does.\n */\n initialFocusSelector?: string;\n}\n\nconst isBrowser = typeof window !== 'undefined';\n\nexport const PickerPopover: React.FC<PickerPopoverProps> = ({\n isOpen,\n onClose,\n anchorRef,\n children,\n position = 'bottom-left',\n appendToBody = false,\n className,\n trapFocus = true,\n closeOnClickOutside = true,\n closeOnEscape = true,\n ariaLabel,\n ariaLabelledBy,\n role = 'dialog',\n id,\n restoreFocusRef,\n initialFocusSelector,\n}) => {\n const panelRef = useRef<HTMLDivElement>(null);\n const resolveContainer = usePortalContainer();\n // Read as the trap turns on, once the panel's content has rendered.\n const initialFocusRef = useMemo(\n () =>\n initialFocusSelector === undefined\n ? undefined\n : {\n get current() {\n return (\n panelRef.current?.querySelector<HTMLElement>(\n initialFocusSelector\n ) ?? null\n );\n },\n },\n [initialFocusSelector]\n );\n const resolved = useAnchoredPosition(anchorRef, panelRef, {\n active: isOpen,\n position,\n fixed: appendToBody,\n // The gap comes from `--bulma-picker-popover-offset` as a margin, in place\n // or portaled, so the coordinates leave it out and `auto` reads it off\n // the panel.\n offset: 0,\n styledGap: marginGap,\n });\n\n useEffect(() => {\n if (!isOpen || !closeOnClickOutside || !isBrowser) return undefined;\n // Listen on `pointerdown` (rather than `mousedown`) so touch starts\n // outside the popover dismiss it on mobile — including swipe-from-outside\n // gestures, where the synthetic mouse event would otherwise lag a few\n // hundred ms behind the actual touch.\n const handler = (e: PointerEvent) => {\n const path = e.composedPath();\n if (panelRef.current && path.includes(panelRef.current)) return;\n if (anchorRef.current && path.includes(anchorRef.current)) return;\n onClose();\n };\n document.addEventListener('pointerdown', handler);\n return () => document.removeEventListener('pointerdown', handler);\n }, [isOpen, closeOnClickOutside, anchorRef, onClose]);\n\n // Escape closes it, and so does Alt+ArrowUp, the combobox's key for it,\n // which the calendar and the time wheels inside leave to it. Escape closes\n // it from anywhere, but the key is only the picker's when it was pressed in\n // the panel or the field: then it's marked used, so a Modal or Sidebar\n // around the picker stays open. From anywhere else, such as a dialog opened\n // over the picker, the key goes on to whatever has focus.\n useEffect(() => {\n if (!isOpen || !closeOnEscape || !isBrowser) return undefined;\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n if (\n isEventInside(e, panelRef.current) ||\n isEventInside(e, anchorRef.current)\n ) {\n e.preventDefault();\n }\n onClose();\n } else if (e.altKey && e.key === 'ArrowUp') {\n e.preventDefault();\n e.stopPropagation();\n onClose();\n }\n };\n document.addEventListener('keydown', handler);\n return () => document.removeEventListener('keydown', handler);\n }, [isOpen, closeOnEscape, anchorRef, onClose]);\n\n useFocusTrap(panelRef, {\n active: isOpen && trapFocus,\n initialFocusRef,\n restoreFocus: restoreFocusRef ?? true,\n });\n\n const panelClass = usePrefixedClassNames('picker-popover', {\n 'is-active': isOpen,\n [`is-${resolved.position}`]: true,\n 'is-portal': appendToBody,\n });\n\n if (!isOpen) return null;\n\n const style: React.CSSProperties = appendToBody\n ? {\n position: 'fixed',\n top: resolved.top,\n left: resolved.left,\n }\n : {};\n\n const panel = (\n <div\n ref={panelRef}\n id={id}\n role={role}\n aria-modal={role === 'dialog' ? 'false' : undefined}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n tabIndex={-1}\n className={classNames(panelClass, className)}\n style={style}\n >\n {children}\n </div>\n );\n\n if (appendToBody && isBrowser) {\n return createPortal(panel, resolveContainer());\n }\n return panel;\n};\n\nPickerPopover.displayName = 'PickerPopover';\n\nexport default PickerPopover;\n"],"names":["_jsx"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -2563,8 +2563,9 @@ const isBrowser$1 = (win, doc) => typeof win !== 'undefined' && typeof doc !== '
|
|
|
2563
2563
|
* the menu, so a letter typed on the trigger does nothing. Escape closes the
|
|
2564
2564
|
* menu and focuses the trigger, and Tab closes it and lets focus move on.
|
|
2565
2565
|
* Anything focusable in the menu that isn't an item, such as a field, keeps
|
|
2566
|
-
* its own keys apart from Escape and Tab
|
|
2567
|
-
*
|
|
2566
|
+
* its own keys apart from Escape and Tab, though an Escape it calls
|
|
2567
|
+
* `preventDefault()` on in its own `onKeyDown` stays its own and leaves the
|
|
2568
|
+
* menu open. For a panel built around fields, use `Popover`.
|
|
2568
2569
|
*
|
|
2569
2570
|
* @function
|
|
2570
2571
|
* @param {DropdownProps} props - Props for the Dropdown component.
|
|
@@ -2741,8 +2742,12 @@ const DropdownComponent = React.forwardRef(function DropdownComponent({ label, c
|
|
|
2741
2742
|
// Closing is Dropdown's: Escape and Tab close the menu from anywhere in
|
|
2742
2743
|
// it, a field included, and an empty or all-disabled menu too. Moving
|
|
2743
2744
|
// between the items, running them and typeahead are the hook's, which
|
|
2744
|
-
// acts only while focus is on the menu or one of its items.
|
|
2745
|
+
// acts only while focus is on the menu or one of its items. An Escape
|
|
2746
|
+
// something in the menu already used, such as a field's own list
|
|
2747
|
+
// closing on it, leaves the menu open for the next one.
|
|
2745
2748
|
if (e.key === 'Escape') {
|
|
2749
|
+
if (e.defaultPrevented)
|
|
2750
|
+
return;
|
|
2746
2751
|
e.preventDefault();
|
|
2747
2752
|
setActive(false);
|
|
2748
2753
|
onActiveChange?.(false);
|
|
@@ -4056,6 +4061,9 @@ const ModalRoot = React.forwardRef(function ModalRoot({ active, isActive, onClos
|
|
|
4056
4061
|
};
|
|
4057
4062
|
}, [isModalActive]);
|
|
4058
4063
|
// Escape closes; Tab cycles inside the modal. Both only for the topmost one.
|
|
4064
|
+
// The listener is on `window`, where a keydown arrives last, so everything
|
|
4065
|
+
// inside the modal has had the key first: React's handlers, and `document`
|
|
4066
|
+
// listeners added after this one, such as a date picker's.
|
|
4059
4067
|
React.useEffect(() => {
|
|
4060
4068
|
if (!isModalActive)
|
|
4061
4069
|
return undefined;
|
|
@@ -4065,11 +4073,15 @@ const ModalRoot = React.forwardRef(function ModalRoot({ active, isActive, onClos
|
|
|
4065
4073
|
return;
|
|
4066
4074
|
}
|
|
4067
4075
|
if (e.key === 'Escape') {
|
|
4068
|
-
|
|
4076
|
+
// Something inside already used this Escape, such as a popup that
|
|
4077
|
+
// closed on it, so the modal waits for the next one.
|
|
4078
|
+
if (closeOnEscape && !e.defaultPrevented)
|
|
4069
4079
|
onClose?.();
|
|
4070
4080
|
return;
|
|
4071
4081
|
}
|
|
4072
|
-
|
|
4082
|
+
// A trap inside the modal, such as a portaled popup's, already moved
|
|
4083
|
+
// focus on this Tab, so the modal leaves it where it went.
|
|
4084
|
+
if (e.key !== 'Tab' || !node || e.defaultPrevented)
|
|
4073
4085
|
return;
|
|
4074
4086
|
// Keep Tab within the modal — `aria-modal` hides the rest of the page
|
|
4075
4087
|
// from assistive technology, so the keyboard order has to agree.
|
|
@@ -4094,8 +4106,8 @@ const ModalRoot = React.forwardRef(function ModalRoot({ active, isActive, onClos
|
|
|
4094
4106
|
first.focus();
|
|
4095
4107
|
}
|
|
4096
4108
|
};
|
|
4097
|
-
|
|
4098
|
-
return () =>
|
|
4109
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
4110
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
4099
4111
|
}, [isModalActive, closeOnEscape, onClose]);
|
|
4100
4112
|
// Move focus into the modal on open, restore it on close
|
|
4101
4113
|
React.useEffect(() => {
|
|
@@ -5375,17 +5387,18 @@ const Loading = ({ active = false, isFullPage = false, size, color, canCancel =
|
|
|
5375
5387
|
onCancel();
|
|
5376
5388
|
}
|
|
5377
5389
|
};
|
|
5378
|
-
// Handle escape key
|
|
5390
|
+
// Handle escape key. On `window`, as in `Modal`, so a popup on the page has
|
|
5391
|
+
// had the key first, and an Escape it closed on doesn't cancel as well.
|
|
5379
5392
|
React.useEffect(() => {
|
|
5380
5393
|
if (!active || !canCancel || !onCancel)
|
|
5381
5394
|
return;
|
|
5382
5395
|
const handleKeyDown = (e) => {
|
|
5383
|
-
if (e.key === 'Escape') {
|
|
5396
|
+
if (e.key === 'Escape' && !e.defaultPrevented) {
|
|
5384
5397
|
onCancel();
|
|
5385
5398
|
}
|
|
5386
5399
|
};
|
|
5387
|
-
|
|
5388
|
-
return () =>
|
|
5400
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
5401
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
5389
5402
|
}, [active, canCancel, onCancel]);
|
|
5390
5403
|
// Prevent body scroll when full page loading is active. Ref-counted through
|
|
5391
5404
|
// the shared helper so an overlapping Modal/Dialog/Sidebar doesn't have its
|
|
@@ -7795,17 +7808,18 @@ const SidebarComponent = React.forwardRef(({ isOpen, onClose, position = 'left',
|
|
|
7795
7808
|
handleClose();
|
|
7796
7809
|
}
|
|
7797
7810
|
}, [overlayClose, handleClose]);
|
|
7798
|
-
// Handle escape key
|
|
7811
|
+
// Handle escape key. On `window`, as in `Modal`, so everything inside the
|
|
7812
|
+
// sidebar has had the key first, and an Escape it used is left to it.
|
|
7799
7813
|
React.useEffect(() => {
|
|
7800
7814
|
if (!isOpen || !escapeClose)
|
|
7801
7815
|
return;
|
|
7802
7816
|
const handleKeyDown = (e) => {
|
|
7803
|
-
if (e.key === 'Escape') {
|
|
7817
|
+
if (e.key === 'Escape' && !e.defaultPrevented) {
|
|
7804
7818
|
handleClose();
|
|
7805
7819
|
}
|
|
7806
7820
|
};
|
|
7807
|
-
|
|
7808
|
-
return () =>
|
|
7821
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
7822
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
7809
7823
|
}, [isOpen, escapeClose, handleClose]);
|
|
7810
7824
|
// Prevent body scroll when sidebar is open. Ref-counted through the shared
|
|
7811
7825
|
// helper so an overlapping Modal/Dialog/Loading doesn't unlock underneath.
|
|
@@ -8060,17 +8074,19 @@ const Toast = React.forwardRef(({ message, type = 'default', actionType, positio
|
|
|
8060
8074
|
document.removeEventListener('click', handleDocumentClick);
|
|
8061
8075
|
};
|
|
8062
8076
|
}, [dismissible, isVisible, handleClose]);
|
|
8077
|
+
// On `window`, as in `Modal`, so a popup on the page has had the key
|
|
8078
|
+
// first, and an Escape it closed on doesn't dismiss the toast as well.
|
|
8063
8079
|
React.useEffect(() => {
|
|
8064
|
-
if (!cancelable)
|
|
8080
|
+
if (!cancelable || !isVisible)
|
|
8065
8081
|
return undefined;
|
|
8066
8082
|
const handleKeyDown = (e) => {
|
|
8067
|
-
if (e.key === 'Escape') {
|
|
8083
|
+
if (e.key === 'Escape' && !e.defaultPrevented) {
|
|
8068
8084
|
handleClose();
|
|
8069
8085
|
}
|
|
8070
8086
|
};
|
|
8071
|
-
|
|
8072
|
-
return () =>
|
|
8073
|
-
}, [cancelable, handleClose]);
|
|
8087
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
8088
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
8089
|
+
}, [cancelable, isVisible, handleClose]);
|
|
8074
8090
|
const containerClasses = usePrefixedClassNames('toast-container', {
|
|
8075
8091
|
[`is-${position}`]: true,
|
|
8076
8092
|
});
|
|
@@ -12655,6 +12671,11 @@ const nothingToAnnounce = [];
|
|
|
12655
12671
|
* />
|
|
12656
12672
|
*/
|
|
12657
12673
|
const Autocomplete = React.forwardRef(({ data = [], value: controlledValue, placeholder, field = 'label', clearable = false, openOnFocus = false, keepFirst = false, keepOpen = false, selectOnClickOutside = false, maxHeight = 200, loading = false, disabled = false, checkInfiniteScroll = false, infiniteScrollDistance = 50, color, size, onInput, onSelect, onActiveChange, onInfiniteScroll, itemTemplate, header, footer, empty, name, form, required, id,
|
|
12674
|
+
// Deprecated no-ops, taken here so they stay off the wrapper.
|
|
12675
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- deprecated no-op
|
|
12676
|
+
selected,
|
|
12677
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- deprecated no-op
|
|
12678
|
+
dropdown,
|
|
12658
12679
|
// Field props
|
|
12659
12680
|
label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, className, ...props }, ref) => {
|
|
12660
12681
|
const insideField = useInsideField();
|
|
@@ -12807,7 +12828,10 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12807
12828
|
}
|
|
12808
12829
|
break;
|
|
12809
12830
|
case 'Escape':
|
|
12810
|
-
|
|
12831
|
+
// Only closing the dropdown claims the key. With it closed, the
|
|
12832
|
+
// key goes on to a Modal around the field.
|
|
12833
|
+
if (menuOpen)
|
|
12834
|
+
e.preventDefault();
|
|
12811
12835
|
setIsActive(false);
|
|
12812
12836
|
setHighlightedIndex(-1);
|
|
12813
12837
|
break;
|
|
@@ -12818,7 +12842,14 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
12818
12842
|
setIsActive(false);
|
|
12819
12843
|
break;
|
|
12820
12844
|
}
|
|
12821
|
-
}, [
|
|
12845
|
+
}, [
|
|
12846
|
+
disabled,
|
|
12847
|
+
isActive,
|
|
12848
|
+
menuOpen,
|
|
12849
|
+
highlightedIndex,
|
|
12850
|
+
filteredData,
|
|
12851
|
+
handleSelect,
|
|
12852
|
+
]);
|
|
12822
12853
|
// Handle dropdown scroll for infinite scroll
|
|
12823
12854
|
const handleDropdownScroll = React.useCallback(() => {
|
|
12824
12855
|
if (!checkInfiniteScroll || !dropdownRef.current)
|
|
@@ -13192,6 +13223,10 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
13192
13223
|
}
|
|
13193
13224
|
break;
|
|
13194
13225
|
case 'Escape':
|
|
13226
|
+
// Closing the suggestions claims the key, so a Modal around the
|
|
13227
|
+
// field stays open. With none showing, the key goes on to it.
|
|
13228
|
+
if (isActive && filteredData.length > 0)
|
|
13229
|
+
e.preventDefault();
|
|
13195
13230
|
setIsActive(false);
|
|
13196
13231
|
setHighlightedIndex(-1);
|
|
13197
13232
|
break;
|
|
@@ -15053,13 +15088,20 @@ const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'botto
|
|
|
15053
15088
|
return () => document.removeEventListener('pointerdown', handler);
|
|
15054
15089
|
}, [isOpen, closeOnClickOutside, anchorRef, onClose]);
|
|
15055
15090
|
// Escape closes it, and so does Alt+ArrowUp, the combobox's key for it,
|
|
15056
|
-
// which the calendar and the time wheels inside leave to it.
|
|
15091
|
+
// which the calendar and the time wheels inside leave to it. Escape closes
|
|
15092
|
+
// it from anywhere, but the key is only the picker's when it was pressed in
|
|
15093
|
+
// the panel or the field: then it's marked used, so a Modal or Sidebar
|
|
15094
|
+
// around the picker stays open. From anywhere else, such as a dialog opened
|
|
15095
|
+
// over the picker, the key goes on to whatever has focus.
|
|
15057
15096
|
React.useEffect(() => {
|
|
15058
15097
|
if (!isOpen || !closeOnEscape || !isBrowser)
|
|
15059
15098
|
return undefined;
|
|
15060
15099
|
const handler = (e) => {
|
|
15061
15100
|
if (e.key === 'Escape') {
|
|
15062
|
-
e.
|
|
15101
|
+
if (isEventInside(e, panelRef.current) ||
|
|
15102
|
+
isEventInside(e, anchorRef.current)) {
|
|
15103
|
+
e.preventDefault();
|
|
15104
|
+
}
|
|
15063
15105
|
onClose();
|
|
15064
15106
|
}
|
|
15065
15107
|
else if (e.altKey && e.key === 'ArrowUp') {
|
|
@@ -15070,7 +15112,7 @@ const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'botto
|
|
|
15070
15112
|
};
|
|
15071
15113
|
document.addEventListener('keydown', handler);
|
|
15072
15114
|
return () => document.removeEventListener('keydown', handler);
|
|
15073
|
-
}, [isOpen, closeOnEscape, onClose]);
|
|
15115
|
+
}, [isOpen, closeOnEscape, anchorRef, onClose]);
|
|
15074
15116
|
useFocusTrap(panelRef, {
|
|
15075
15117
|
active: isOpen && trapFocus,
|
|
15076
15118
|
initialFocusRef,
|