@servicetitan/anvil2 3.0.4 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/{AnvilProvider-JXEbeQjR.js → AnvilProvider-J9DjoJiB.js} +13 -2
  3. package/dist/AnvilProvider-J9DjoJiB.js.map +1 -0
  4. package/dist/AnvilProvider.js +1 -1
  5. package/dist/{Combobox-C9-3tlcP.js → Combobox-BXQFOwX4.js} +2 -2
  6. package/dist/{Combobox-C9-3tlcP.js.map → Combobox-BXQFOwX4.js.map} +1 -1
  7. package/dist/Combobox.js +1 -1
  8. package/dist/{DataTable-Dr5A6RDe.js → DataTable-BFzj8cZG.js} +3 -3
  9. package/dist/{DataTable-Dr5A6RDe.js.map → DataTable-BFzj8cZG.js.map} +1 -1
  10. package/dist/{FilterBar-BUfajdQU.js → FilterBar-GjMlLOME.js} +6 -6
  11. package/dist/FilterBar-GjMlLOME.js.map +1 -0
  12. package/dist/FilterBar.js +1 -1
  13. package/dist/MultiSelectField.js +1 -1
  14. package/dist/{MultiSelectFieldSync-DXIBQABc.js → MultiSelectFieldSync-BxyQiVPQ.js} +56 -26
  15. package/dist/MultiSelectFieldSync-BxyQiVPQ.js.map +1 -0
  16. package/dist/MultiSelectFieldSync.css +59 -57
  17. package/dist/MultiSelectMenu.js +1 -1
  18. package/dist/{MultiSelectMenuSync-CCINebbr.js → MultiSelectMenuSync-BJfvZGf3.js} +19 -6
  19. package/dist/MultiSelectMenuSync-BJfvZGf3.js.map +1 -0
  20. package/dist/SelectField.js +1 -1
  21. package/dist/{SelectFieldSync-DqIDTKqt.js → SelectFieldSync-z8b41JZz.js} +39 -9
  22. package/dist/SelectFieldSync-z8b41JZz.js.map +1 -0
  23. package/dist/SelectMenu.js +1 -1
  24. package/dist/{SelectMenuSync-DYPEX5wc.js → SelectMenuSync-Cf1Zradm.js} +19 -6
  25. package/dist/SelectMenuSync-Cf1Zradm.js.map +1 -0
  26. package/dist/{SelectOptions-DuMbmJjh.js → SelectOptions-BgT1V6qp.js} +2 -2
  27. package/dist/{SelectOptions-DuMbmJjh.js.map → SelectOptions-BgT1V6qp.js.map} +1 -1
  28. package/dist/{SelectTrigger-RiRjgwXd.js → SelectTrigger-37HzF-VI.js} +2 -2
  29. package/dist/{SelectTrigger-RiRjgwXd.js.map → SelectTrigger-37HzF-VI.js.map} +1 -1
  30. package/dist/SelectTrigger.js +1 -1
  31. package/dist/{SelectTriggerBase-CWxP7nPA.js → SelectTriggerBase-4o1cqEbR.js} +2 -2
  32. package/dist/{SelectTriggerBase-CWxP7nPA.js.map → SelectTriggerBase-4o1cqEbR.js.map} +1 -1
  33. package/dist/SelectTriggerBase.css +44 -42
  34. package/dist/SelectTriggerBase.module-BgZSNZfE.js +37 -0
  35. package/dist/SelectTriggerBase.module-BgZSNZfE.js.map +1 -0
  36. package/dist/Table.js +1 -1
  37. package/dist/beta.js +7 -7
  38. package/dist/index.js +3 -3
  39. package/dist/src/beta/components/MultiSelectField/MultiSelectField.d.ts +1 -0
  40. package/dist/src/beta/components/MultiSelectField/internal/MultiSelectFieldComboboxMode.d.ts +1 -1
  41. package/dist/src/beta/components/MultiSelectField/internal/MultiSelectFieldSelectMode.d.ts +1 -1
  42. package/dist/src/beta/components/MultiSelectField/internal/types.d.ts +2 -1
  43. package/dist/src/beta/components/MultiSelectField/types.d.ts +2 -1
  44. package/dist/src/beta/components/MultiSelectMenu/MultiSelectMenu.d.ts +1 -0
  45. package/dist/src/beta/components/MultiSelectMenu/types.d.ts +2 -1
  46. package/dist/src/beta/components/SelectField/SelectField.d.ts +21 -0
  47. package/dist/src/beta/components/SelectField/internal/SelectFieldComboboxMode.d.ts +1 -1
  48. package/dist/src/beta/components/SelectField/internal/SelectFieldSelectMode.d.ts +1 -1
  49. package/dist/src/beta/components/SelectField/types.d.ts +2 -1
  50. package/dist/src/beta/components/SelectMenu/SelectMenu.d.ts +1 -0
  51. package/dist/src/beta/components/SelectMenu/types.d.ts +2 -1
  52. package/dist/src/internal/components/AddNewItemButton.d.ts +40 -0
  53. package/dist/src/internal/components/OptionsDialog/OptionsDialog.d.ts +3 -1
  54. package/dist/src/internal/components/OptionsPopover/OptionsPopover.d.ts +13 -1
  55. package/dist/src/internal/types/addNewItemTypes.d.ts +12 -0
  56. package/dist/src/internal/types/selectFieldInternalTypes.d.ts +2 -1
  57. package/dist/{syncFilterUtils-hlw11Ud8.js → syncFilterUtils-Dpp7gt05.js} +96 -24
  58. package/dist/syncFilterUtils-Dpp7gt05.js.map +1 -0
  59. package/dist/syncFilterUtils.css +44 -23
  60. package/dist/{useMenuInteraction-BwZ2ORo9.js → useMenuInteraction-C4RU5Fdq.js} +1 -5
  61. package/dist/useMenuInteraction-C4RU5Fdq.js.map +1 -0
  62. package/dist/{useToggleSelection-VwlBkdZo.js → useToggleSelection-DBqr4HwT.js} +2 -2
  63. package/dist/{useToggleSelection-VwlBkdZo.js.map → useToggleSelection-DBqr4HwT.js.map} +1 -1
  64. package/package.json +3 -3
  65. package/dist/AnvilProvider-JXEbeQjR.js.map +0 -1
  66. package/dist/FilterBar-BUfajdQU.js.map +0 -1
  67. package/dist/MultiSelectFieldSync-DXIBQABc.js.map +0 -1
  68. package/dist/MultiSelectMenuSync-CCINebbr.js.map +0 -1
  69. package/dist/SelectFieldSync-DqIDTKqt.js.map +0 -1
  70. package/dist/SelectMenuSync-DYPEX5wc.js.map +0 -1
  71. package/dist/SelectTriggerBase.module-CCLOnHm1.js +0 -37
  72. package/dist/SelectTriggerBase.module-CCLOnHm1.js.map +0 -1
  73. package/dist/syncFilterUtils-hlw11Ud8.js.map +0 -1
  74. package/dist/useMenuInteraction-BwZ2ORo9.js.map +0 -1
@@ -1,2 +1,23 @@
1
1
  import { SelectFieldHandle, SelectFieldProps } from './types';
2
+ /**
3
+ * SelectField component for selecting a single option from a searchable dropdown.
4
+ *
5
+ * Features:
6
+ * - Async option loading with caching, lazy pagination (page, offset, group), and debounced search
7
+ * - Supports pinned option sections (static or dynamically loaded)
8
+ * - Grouped options with custom sort and label functions
9
+ * - Combobox mode (searchable) and select mode (non-searchable) via disableSearch
10
+ * - Dialog and popover display modes with auto-detection based on viewport
11
+ * - Imperative handle for cache management (clearCache, invalidate)
12
+ * - Optional "Add new item" affordance below the option list with consumer-owned dialog
13
+ * - Supports layout utilities for positioning and spacing
14
+ *
15
+ * @example
16
+ * <SelectField
17
+ * label="Technician"
18
+ * value={selected}
19
+ * onSelectedOptionChange={setSelected}
20
+ * loadOptions={async (search) => fetchTechnicians(search)}
21
+ * />
22
+ */
2
23
  export declare const SelectField: import('react').ForwardRefExoticComponent<SelectFieldProps & import('react').RefAttributes<SelectFieldHandle>>;
@@ -3,4 +3,4 @@ import { SelectFieldComboboxInternalProps } from '../../../../internal/types/sel
3
3
  * Combobox mode: searchable select using useCombobox.
4
4
  * This is the default behavior when disableSearch is false.
5
5
  */
6
- export declare const SelectFieldComboboxMode: ({ id, helperUid, selectedOption, onSelectedOptionChange, displayAs, isDisabledOrReadOnly, disabled, labelAiMark, readOnly, required, placeholder, size, error, hasHelperText, disableClearButton, prefix, suffix, label, hideLabel, hint, moreInfo, moreInfoId, errorMessages, warning, description, className, style, layoutStyles, pinned, groupToString, groupSorter, virtualize, options, loading, loadingMore, hasMore, loadOptions, loadMore, initialLoad, initialLoadPerformed, setInitialLoadPerformed, inputWrapperRef, debounceMs, }: SelectFieldComboboxInternalProps) => import("react/jsx-runtime").JSX.Element;
6
+ export declare const SelectFieldComboboxMode: ({ id, helperUid, selectedOption, onSelectedOptionChange, displayAs, isDisabledOrReadOnly, disabled, labelAiMark, readOnly, required, placeholder, size, error, hasHelperText, disableClearButton, prefix, suffix, label, hideLabel, hint, moreInfo, moreInfoId, errorMessages, warning, description, className, style, layoutStyles, pinned, groupToString, groupSorter, virtualize, options, loading, loadingMore, hasMore, loadOptions, loadMore, initialLoad, initialLoadPerformed, setInitialLoadPerformed, inputWrapperRef, debounceMs, addItemLabel, onAddNewItem, }: SelectFieldComboboxInternalProps) => import("react/jsx-runtime").JSX.Element;
@@ -3,4 +3,4 @@ import { SelectFieldBaseInternalProps } from '../../../../internal/types/selectF
3
3
  * Select mode: non-searchable select using useSelect.
4
4
  * Active when disableSearch is true.
5
5
  */
6
- export declare const SelectFieldSelectMode: ({ labelAiMark, id, helperUid, selectedOption, onSelectedOptionChange, displayAs, isDisabledOrReadOnly, disabled, readOnly, placeholder, size, error, hasHelperText, disableClearButton, prefix, suffix, label, hideLabel, hint, moreInfo, moreInfoId, errorMessages, warning, description, className, style, layoutStyles, pinned, groupToString, groupSorter, virtualize, options, loading, loadingMore, hasMore, loadOptions, loadMore, initialLoad, initialLoadPerformed, setInitialLoadPerformed, inputWrapperRef, required, }: SelectFieldBaseInternalProps) => import("react/jsx-runtime").JSX.Element;
6
+ export declare const SelectFieldSelectMode: ({ labelAiMark, id, helperUid, selectedOption, onSelectedOptionChange, displayAs, isDisabledOrReadOnly, disabled, readOnly, placeholder, size, error, hasHelperText, disableClearButton, prefix, suffix, label, hideLabel, hint, moreInfo, moreInfoId, errorMessages, warning, description, className, style, layoutStyles, pinned, groupToString, groupSorter, virtualize, options, loading, loadingMore, hasMore, loadOptions, loadMore, initialLoad, initialLoadPerformed, setInitialLoadPerformed, inputWrapperRef, required, addItemLabel, onAddNewItem, }: SelectFieldBaseInternalProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,7 @@
1
1
  import { AriaAttributes, CSSProperties, ReactElement, ReactNode } from 'react';
2
2
  import { AiMarkWithTooltipOrPopoverProps, LayoutUtilProps, Size } from '../../../types';
3
3
  import { AvatarProps, ChipProps, IconProps } from '../../..';
4
+ import { AddNewItemProps } from '../../../internal/types/addNewItemTypes';
4
5
  /**
5
6
  * Configuration options for the SelectField cache.
6
7
  * @property enabled - Whether caching is enabled. Defaults to true.
@@ -250,7 +251,7 @@ type SelectFieldCommonSelectFieldProps = {
250
251
  * @default false
251
252
  */
252
253
  disableSearch?: boolean;
253
- } & SelectFieldSearchProps & SelectFieldAddNewOptionProps & LayoutUtilProps;
254
+ } & SelectFieldSearchProps & SelectFieldAddNewOptionProps & LayoutUtilProps & AddNewItemProps;
254
255
  type SelectFieldGroupingProps = {
255
256
  /**
256
257
  * Function to convert a group value to a display label.
@@ -14,6 +14,7 @@ import { SelectMenuHandle, SelectMenuProps } from './types';
14
14
  * - Full accessibility support with ARIA attributes
15
15
  * - Controlled selection state
16
16
  * - Configurable popover width
17
+ * - Optional "Add new item" affordance below the option list with consumer-owned dialog
17
18
  *
18
19
  * @example
19
20
  * <SelectMenu
@@ -1,5 +1,6 @@
1
1
  import { KeyboardEvent, ReactElement, RefObject } from 'react';
2
2
  import { SelectFieldCacheOptions, SelectFieldEagerLoader, SelectFieldGroupByValue, SelectFieldGroupLazyLoader, SelectFieldGroupedOption, SelectFieldOffsetLazyLoader, SelectFieldOption, SelectFieldPageLazyLoader, SelectFieldPinnedOptions, SelectFieldSearchProps } from '../SelectField/types';
3
+ import { AddNewItemProps } from '../../../internal/types/addNewItemTypes';
3
4
  export type SelectMenuOption = SelectFieldOption;
4
5
  export type SelectMenuGroupByValue = SelectFieldGroupByValue;
5
6
  export type SelectMenuGroupedOption = SelectFieldGroupedOption;
@@ -124,7 +125,7 @@ type SelectMenuCommonProps = {
124
125
  * @default 320
125
126
  */
126
127
  popoverWidth?: "reference" | number | string;
127
- } & SelectMenuSearchProps;
128
+ } & SelectMenuSearchProps & AddNewItemProps;
128
129
  type SelectMenuGroupingProps = {
129
130
  /**
130
131
  * Function to convert a group value to a display label.
@@ -0,0 +1,40 @@
1
+ import { AddNewItemProps } from '../types/addNewItemTypes';
2
+ /**
3
+ * Props for the AddNewItemButton component.
4
+ *
5
+ * @property {string} searchText - The current search text, forwarded to the consumer's handler and label callback.
6
+ * @property {() => void} [onCloseMenu] - Internal hook for the host menu to close itself when the button is clicked. Fires before the consumer's handler.
7
+ * @extends AddNewItemProps
8
+ */
9
+ export type AddNewItemButtonProps = AddNewItemProps & {
10
+ /** The current search text, forwarded to the consumer's handler and label callback. */
11
+ searchText: string;
12
+ /** Internal hook for the host menu to close itself when the button is clicked. */
13
+ onCloseMenu?: () => void;
14
+ };
15
+ /**
16
+ * AddNewItemButton renders the consumer's "Add new item" affordance for the
17
+ * Select* / MultiSelect* components. Positioning, padding, background tint,
18
+ * and the visual separator from the option list are owned by the surrounding
19
+ * popover/dialog footer slot — this component is only responsible for the
20
+ * button itself.
21
+ *
22
+ * Features:
23
+ * - Renders nothing when no `onAddNewItem` handler is provided
24
+ * - Static or dynamic label resolved against the current search text
25
+ * - Closes the host menu via `onCloseMenu` when clicked, before invoking the consumer's handler
26
+ *
27
+ * @example
28
+ * <AddNewItemButton
29
+ * searchText={searchText}
30
+ * addItemLabel={(s) => `Add "${s}"`}
31
+ * onAddNewItem={(text) => openAddDialog(text)}
32
+ * onCloseMenu={closeMenu}
33
+ * />
34
+ *
35
+ * @returns The button, or null when the feature is not enabled.
36
+ */
37
+ export declare const AddNewItemButton: {
38
+ ({ searchText, addItemLabel, onAddNewItem, onCloseMenu, }: AddNewItemButtonProps): import("react/jsx-runtime").JSX.Element | null;
39
+ displayName: string;
40
+ };
@@ -1,5 +1,5 @@
1
1
  import { FocusableElement } from '../../..';
2
- export declare const OptionsDialog: ({ id, isOpen, onClose, field, children, title, initialFocusResolver, }: {
2
+ export declare const OptionsDialog: ({ id, isOpen, onClose, field, children, title, initialFocusResolver, footer, }: {
3
3
  id: string;
4
4
  isOpen: boolean;
5
5
  onClose: () => void;
@@ -7,4 +7,6 @@ export declare const OptionsDialog: ({ id, isOpen, onClose, field, children, tit
7
7
  field?: React.ReactNode;
8
8
  children: React.ReactNode;
9
9
  initialFocusResolver?: (focusables: FocusableElement[]) => FocusableElement;
10
+ /** Optional content rendered below the dialog body. */
11
+ footer?: React.ReactNode;
10
12
  }) => import("react/jsx-runtime").JSX.Element;
@@ -4,7 +4,13 @@ export type OptionsPopoverProps = {
4
4
  children: ReactNode;
5
5
  referenceElement: RefObject<HTMLElement>;
6
6
  open: boolean;
7
- onClickOutside: () => void;
7
+ /**
8
+ * Fired when the popover should close — either because the user clicked
9
+ * outside it, focus moved to an element outside the popover or its
10
+ * reference (trigger) element, or the user pressed Escape inside the
11
+ * popover.
12
+ */
13
+ onClose: () => void;
8
14
  /** Controls the popover width strategy.
9
15
  * - `"reference"` — match the reference element's width
10
16
  * - `number` — fixed width in px
@@ -12,5 +18,11 @@ export type OptionsPopoverProps = {
12
18
  * - `undefined` (default) — intrinsic sizing (`max-content`)
13
19
  */
14
20
  width?: "reference" | number | string;
21
+ /**
22
+ * Optional content rendered as a full-bleed footer region inside the popover
23
+ * shell, separated from the body by a top border. The popover suppresses its
24
+ * own bottom padding so the footer can hug the popover's lower edge.
25
+ */
26
+ footer?: ReactNode;
15
27
  };
16
28
  export declare const OptionsPopover: (props: OptionsPopoverProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { ReactNode } from 'react';
2
+ /**
3
+ * Shared prop set for the "Add new item" affordance available on
4
+ * SelectField, SelectMenu, MultiSelectField, and MultiSelectMenu.
5
+ *
6
+ * @property addItemLabel - Label rendered inside the "Add new item" button. May be a static node or a callback that receives the current search text and returns a dynamic label (e.g. `Add "${searchText}"`). When omitted but `onAddNewItem` is provided, falls back to `"Add new item"`.
7
+ * @property onAddNewItem - Click handler for the "Add new item" button. Presence of this prop is what gates rendering the button. Receives the current search text. The menu auto-closes synchronously on click; the handler runs immediately after — typically opening a Dialog or Drawer to collect the new item's details. Consumers manage their own overlay presentation, focus return, option list updates, and selection state.
8
+ */
9
+ export type AddNewItemProps = {
10
+ addItemLabel?: ReactNode | ((searchText: string) => ReactNode);
11
+ onAddNewItem?: (searchText: string) => void;
12
+ };
@@ -1,6 +1,7 @@
1
1
  import { ReactElement, ReactNode, RefObject } from 'react';
2
2
  import { AiMarkWithTooltipOrPopoverProps, CheckState } from '../../types';
3
3
  import { SelectFieldOption, SelectFieldProps } from '../../beta/components/SelectField/types';
4
+ import { AddNewItemProps } from './addNewItemTypes';
4
5
  /**
5
6
  * Metadata about list sections for rendering purposes.
6
7
  * Used by SelectOptions, MultiSelectOptions, and their Menu counterparts to render section headers and dividers.
@@ -62,7 +63,7 @@ export type SelectFieldBaseInternalProps = {
62
63
  initialLoadPerformed: boolean;
63
64
  setInitialLoadPerformed: (val: boolean) => void;
64
65
  inputWrapperRef: RefObject<HTMLDivElement>;
65
- };
66
+ } & AddNewItemProps;
66
67
  export type SelectFieldComboboxInternalProps = SelectFieldBaseInternalProps & {
67
68
  debounceMs: number;
68
69
  };
@@ -23,6 +23,7 @@ import { S as SvgClose } from './close-DZj38AEh.js';
23
23
  import { D as DialogContext } from './DialogContext-DBgtApl9.js';
24
24
  import { t as tabbable } from './usePopoverTransitionStates-CDXCdyKa.js';
25
25
  import { u as useDialogScrollLock } from './useDialogScrollLock-BW-aVboo.js';
26
+ import { S as SvgAdd } from './add-BcQkAUip.js';
26
27
  import { u as useBreakpoint } from './useBreakpoint-CeaUyHxh.js';
27
28
  import { m as matchSorter } from './match-sorter.esm-CGAauEiU.js';
28
29
 
@@ -177,18 +178,18 @@ function useSelectFieldLoading(config) {
177
178
  };
178
179
  }
179
180
 
180
- const empty = "_empty_x5qls_7";
181
- const scroller = "_scroller_x5qls_23";
182
- const divider = "_divider_x5qls_57";
181
+ const empty = "_empty_1stqb_7";
182
+ const scroller = "_scroller_1stqb_23";
183
+ const divider = "_divider_1stqb_59";
183
184
  const styles$3 = {
184
- "select-field-list": "_select-field-list_x5qls_2",
185
+ "select-field-list": "_select-field-list_1stqb_2",
185
186
  empty: empty,
186
- "load-more": "_load-more_x5qls_15",
187
+ "load-more": "_load-more_1stqb_15",
187
188
  scroller: scroller,
188
- "scroll-sentinel": "_scroll-sentinel_x5qls_32",
189
- "pinned-section": "_pinned-section_x5qls_40",
190
- "pinned-section-header": "_pinned-section-header_x5qls_44",
191
- "pinned-section-list": "_pinned-section-list_x5qls_51",
189
+ "scroll-sentinel": "_scroll-sentinel_1stqb_34",
190
+ "pinned-section": "_pinned-section_1stqb_42",
191
+ "pinned-section-header": "_pinned-section-header_1stqb_46",
192
+ "pinned-section-list": "_pinned-section-list_1stqb_53",
192
193
  divider: divider
193
194
  };
194
195
 
@@ -903,7 +904,9 @@ function useDebouncedCallback(callback, delay = 300) {
903
904
  }
904
905
 
905
906
  const styles$1 = {
906
- "options-popover": "_options-popover_12bid_2"
907
+ "options-popover": "_options-popover_1a8y0_2",
908
+ "options-popover-body": "_options-popover-body_1a8y0_39",
909
+ "options-popover-footer": "_options-popover-footer_1a8y0_48"
907
910
  };
908
911
 
909
912
  function resolveWidth(width, referenceWidth) {
@@ -914,10 +917,17 @@ function resolveWidth(width, referenceWidth) {
914
917
  }
915
918
  const OptionsPopover = (props) => {
916
919
  const ref = useRef(null);
917
- const { id, children, referenceElement, open, onClickOutside, width } = props;
920
+ const { id, children, referenceElement, open, onClose, width, footer } = props;
921
+ const lastCloseRef = useRef(0);
922
+ const safeClose = useCallback(() => {
923
+ const now = Date.now();
924
+ if (now - lastCloseRef.current < 50) return;
925
+ lastCloseRef.current = now;
926
+ onClose();
927
+ }, [onClose]);
918
928
  useOnClickOutside({
919
929
  targets: [ref.current ?? void 0, referenceElement.current ?? void 0],
920
- onClickOutside,
930
+ onClickOutside: safeClose,
921
931
  disable: !open
922
932
  });
923
933
  useEffect(() => {
@@ -927,6 +937,36 @@ const OptionsPopover = (props) => {
927
937
  ref.current?.hidePopover();
928
938
  }
929
939
  }, [open]);
940
+ useEffect(() => {
941
+ if (!open) return;
942
+ const popoverEl = ref.current;
943
+ if (!popoverEl) return;
944
+ const handleFocusOut = (e) => {
945
+ const next = e.relatedTarget;
946
+ if (!next) {
947
+ safeClose();
948
+ return;
949
+ }
950
+ if (popoverEl.contains(next)) return;
951
+ const trigger = referenceElement.current;
952
+ if (trigger && trigger.contains(next)) return;
953
+ safeClose();
954
+ };
955
+ popoverEl.addEventListener("focusout", handleFocusOut);
956
+ return () => popoverEl.removeEventListener("focusout", handleFocusOut);
957
+ }, [open, safeClose, referenceElement]);
958
+ useEffect(() => {
959
+ if (!open) return;
960
+ const popoverEl = ref.current;
961
+ if (!popoverEl) return;
962
+ const handleKeyDown = (e) => {
963
+ if (e.key !== "Escape" || e.defaultPrevented) return;
964
+ e.preventDefault();
965
+ safeClose();
966
+ };
967
+ popoverEl.addEventListener("keydown", handleKeyDown);
968
+ return () => popoverEl.removeEventListener("keydown", handleKeyDown);
969
+ }, [open, safeClose]);
930
970
  const updatePosition = useCallback(async () => {
931
971
  if (!ref.current || !referenceElement.current) return;
932
972
  const position = await computePosition(
@@ -973,27 +1013,32 @@ const OptionsPopover = (props) => {
973
1013
  cleanup();
974
1014
  };
975
1015
  }, [referenceElement, ref, updatePosition, open]);
976
- return /* @__PURE__ */ jsx(
1016
+ return /* @__PURE__ */ jsxs(
977
1017
  "div",
978
1018
  {
979
1019
  ref,
980
1020
  id,
981
1021
  className: styles$1["options-popover"],
982
1022
  popover: "manual",
983
- children
1023
+ children: [
1024
+ /* @__PURE__ */ jsx("div", { className: styles$1["options-popover-body"], children }),
1025
+ footer ? /* @__PURE__ */ jsx("div", { className: styles$1["options-popover-footer"], children: footer }) : null
1026
+ ]
984
1027
  }
985
1028
  );
986
1029
  };
987
1030
 
988
- const header = "_header_3g90c_40";
989
- const field = "_field_3g90c_2";
990
- const content = "_content_3g90c_53";
1031
+ const header = "_header_1v2a3_41";
1032
+ const field = "_field_1v2a3_2";
1033
+ const content = "_content_1v2a3_54";
1034
+ const footer = "_footer_1v2a3_60";
991
1035
  const styles = {
992
- "field-dialog": "_field-dialog_3g90c_2",
993
- "field-dialog-visual-viewport-height": "_field-dialog-visual-viewport-height_3g90c_20",
1036
+ "field-dialog": "_field-dialog_1v2a3_2",
1037
+ "field-dialog-visual-viewport-height": "_field-dialog-visual-viewport-height_1v2a3_21",
994
1038
  header: header,
995
1039
  field: field,
996
- content: content
1040
+ content: content,
1041
+ footer: footer
997
1042
  };
998
1043
 
999
1044
  const OptionsDialog = ({
@@ -1003,7 +1048,8 @@ const OptionsDialog = ({
1003
1048
  field,
1004
1049
  children,
1005
1050
  title,
1006
- initialFocusResolver
1051
+ initialFocusResolver,
1052
+ footer
1007
1053
  }) => {
1008
1054
  const elRef = useRef(null);
1009
1055
  const visualViewportHeightRef = useRef(null);
@@ -1080,7 +1126,8 @@ const OptionsDialog = ({
1080
1126
  ) }) })
1081
1127
  ] }),
1082
1128
  field ? /* @__PURE__ */ jsx(Flex, { direction: "column", className: styles["field"], children: field }) : null,
1083
- /* @__PURE__ */ jsx(Flex, { direction: "column", className: styles["content"], children })
1129
+ /* @__PURE__ */ jsx(Flex, { direction: "column", className: styles["content"], children }),
1130
+ footer ? /* @__PURE__ */ jsx("div", { className: styles["footer"], children: footer }) : null
1084
1131
  ]
1085
1132
  }
1086
1133
  )
@@ -1088,6 +1135,31 @@ const OptionsDialog = ({
1088
1135
  );
1089
1136
  };
1090
1137
 
1138
+ const AddNewItemButton = ({
1139
+ searchText,
1140
+ addItemLabel,
1141
+ onAddNewItem,
1142
+ onCloseMenu
1143
+ }) => {
1144
+ if (!onAddNewItem) return null;
1145
+ const resolvedLabel = typeof addItemLabel === "function" ? addItemLabel(searchText) : addItemLabel ?? "Add new item";
1146
+ return /* @__PURE__ */ jsx(
1147
+ Button,
1148
+ {
1149
+ appearance: "secondary",
1150
+ size: "small",
1151
+ icon: { before: SvgAdd },
1152
+ onClick: () => {
1153
+ onCloseMenu?.();
1154
+ onAddNewItem(searchText);
1155
+ },
1156
+ style: { width: "100%" },
1157
+ children: resolvedLabel
1158
+ }
1159
+ );
1160
+ };
1161
+ AddNewItemButton.displayName = "AddNewItemButton";
1162
+
1091
1163
  const DEFAULT_CACHE_SIZE = 15;
1092
1164
  const STATIC_CACHE_KEY = "__static__";
1093
1165
  function cacheSet(sectionCache, key, value, maxSize) {
@@ -1501,5 +1573,5 @@ const sortByGroup = (options, groupSorter) => {
1501
1573
  });
1502
1574
  };
1503
1575
 
1504
- export { OptionsPanel as O, VirtualizedOptionsPanel as V, useSelectOrchestration as a, useProcessedOptions as b, buildSelectItems as c, useDebouncedCallback as d, OptionsDialog as e, OptionsPopover as f, defaultSyncFilter as g, toSelectItem as h, sortByGroup as s, toSyncFilterFn as t, useAdaptiveView as u };
1505
- //# sourceMappingURL=syncFilterUtils-hlw11Ud8.js.map
1576
+ export { AddNewItemButton as A, OptionsPanel as O, VirtualizedOptionsPanel as V, useSelectOrchestration as a, useProcessedOptions as b, buildSelectItems as c, useDebouncedCallback as d, OptionsDialog as e, OptionsPopover as f, defaultSyncFilter as g, toSelectItem as h, sortByGroup as s, toSyncFilterFn as t, useAdaptiveView as u };
1577
+ //# sourceMappingURL=syncFilterUtils-Dpp7gt05.js.map