@servicetitan/anvil2 3.15.1 → 3.16.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/{Announcement-Cnz80WGx.js → Announcement-PfnsVVz1.js} +5 -2
  3. package/dist/Announcement-PfnsVVz1.js.map +1 -0
  4. package/dist/Announcement.js +1 -1
  5. package/dist/{DataTable-DkEpJ_Op.js → DataTable-DIXP9-Bx.js} +4 -4
  6. package/dist/{DataTable-DkEpJ_Op.js.map → DataTable-DIXP9-Bx.js.map} +1 -1
  7. package/dist/{Dialog-B9llUKJz.js → Dialog-_tHqOsbr.js} +2 -2
  8. package/dist/{Dialog-B9llUKJz.js.map → Dialog-_tHqOsbr.js.map} +1 -1
  9. package/dist/Dialog.js +1 -1
  10. package/dist/{Drawer-Bjd2XLUX.js → Drawer-D-ThWNp0.js} +2 -2
  11. package/dist/{Drawer-Bjd2XLUX.js.map → Drawer-D-ThWNp0.js.map} +1 -1
  12. package/dist/Drawer.js +1 -1
  13. package/dist/{FilterBar-ecqJDraA.js → FilterBar-jFn8O7v7.js} +17 -15
  14. package/dist/FilterBar-jFn8O7v7.js.map +1 -0
  15. package/dist/FilterBar.js +1 -1
  16. package/dist/MultiSelectField.js +1 -1
  17. package/dist/{MultiSelectFieldSync-DpPO0cya.js → MultiSelectFieldSync-DuImKXuj.js} +3 -3
  18. package/dist/{MultiSelectFieldSync-DpPO0cya.js.map → MultiSelectFieldSync-DuImKXuj.js.map} +1 -1
  19. package/dist/MultiSelectMenu.js +1 -1
  20. package/dist/{MultiSelectMenuSync-BN_3moW_.js → MultiSelectMenuSync-BhNXsgAN.js} +3 -3
  21. package/dist/{MultiSelectMenuSync-BN_3moW_.js.map → MultiSelectMenuSync-BhNXsgAN.js.map} +1 -1
  22. package/dist/{Page-IZ4p51iJ.js → Page-ZSHNJs7z.js} +2 -2
  23. package/dist/{Page-IZ4p51iJ.js.map → Page-ZSHNJs7z.js.map} +1 -1
  24. package/dist/Page.js +1 -1
  25. package/dist/{Pagination-Su4VBAhT.js → Pagination-Bsl99RRI.js} +4 -2
  26. package/dist/{Pagination-Su4VBAhT.js.map → Pagination-Bsl99RRI.js.map} +1 -1
  27. package/dist/Pagination.js +1 -1
  28. package/dist/{ProgressBar-TQ0fPmn9.js → ProgressBar-zPKYv4Il.js} +10 -13
  29. package/dist/ProgressBar-zPKYv4Il.js.map +1 -0
  30. package/dist/ProgressBar.css +10 -14
  31. package/dist/ProgressBar.js +1 -1
  32. package/dist/{RichTextEditor-BpzUo8dB.js → RichTextEditor-DcAVqd2d.js} +18821 -20223
  33. package/dist/RichTextEditor-DcAVqd2d.js.map +1 -0
  34. package/dist/RichTextEditor.js +1 -1
  35. package/dist/{SavedFiltersButton-D9GJbJQu.js → SavedFiltersButton-Bl8mr-LB.js} +4 -4
  36. package/dist/{SavedFiltersButton-D9GJbJQu.js.map → SavedFiltersButton-Bl8mr-LB.js.map} +1 -1
  37. package/dist/SavedFiltersButton.js +1 -1
  38. package/dist/SelectField.js +1 -1
  39. package/dist/{SelectFieldSync-BN2ebLWT.js → SelectFieldSync-SS7FELT3.js} +3 -3
  40. package/dist/{SelectFieldSync-BN2ebLWT.js.map → SelectFieldSync-SS7FELT3.js.map} +1 -1
  41. package/dist/SelectMenu.js +1 -1
  42. package/dist/{SelectMenuSync-DuPf8KCc.js → SelectMenuSync-CPpE6vUG.js} +3 -3
  43. package/dist/{SelectMenuSync-DuPf8KCc.js.map → SelectMenuSync-CPpE6vUG.js.map} +1 -1
  44. package/dist/{SelectOptions-BK7kNjYM.js → SelectOptions-BDAu_9dv.js} +2 -2
  45. package/dist/{SelectOptions-BK7kNjYM.js.map → SelectOptions-BDAu_9dv.js.map} +1 -1
  46. package/dist/Table.js +1 -1
  47. package/dist/Toast.js +2 -2
  48. package/dist/{Toaster-BryQmKe1.js → Toaster-SqkX0Kes.js} +2 -2
  49. package/dist/{Toaster-BryQmKe1.js.map → Toaster-SqkX0Kes.js.map} +1 -1
  50. package/dist/{Toaster-BtP_HezK.js → Toaster-dVGVVcat.js} +2 -2
  51. package/dist/{Toaster-BtP_HezK.js.map → Toaster-dVGVVcat.js.map} +1 -1
  52. package/dist/assets/icons/material.ts +184 -184
  53. package/dist/assets/icons/st.ts +9 -9
  54. package/dist/beta.js +9 -9
  55. package/dist/{bulkActionTypes-D5y3Acn1.js → bulkActionTypes-SukoplGV.js} +2 -2
  56. package/dist/{bulkActionTypes-D5y3Acn1.js.map → bulkActionTypes-SukoplGV.js.map} +1 -1
  57. package/dist/downshift.esm-U4VMqrF-.js.map +1 -1
  58. package/dist/extends-Bg2s_OKl.js.map +1 -1
  59. package/dist/{filter-state-Do862BNr.js → filter-state-hDtCUggM.js} +6 -6
  60. package/dist/{filter-state-Do862BNr.js.map → filter-state-hDtCUggM.js.map} +1 -1
  61. package/dist/{index-Dt0zG4YB.js → index-C2dOrf4N.js} +1549 -1582
  62. package/dist/index-C2dOrf4N.js.map +1 -0
  63. package/dist/index.js +11 -11
  64. package/dist/src/components/Pagination/Pagination.d.ts +28 -0
  65. package/dist/src/internal/components/LoadMoreSlot.d.ts +13 -0
  66. package/dist/src/internal/hooks/useAutoLoadMore.d.ts +40 -0
  67. package/dist/src/internal/hooks/useFocusReturnOnUnmount.d.ts +10 -0
  68. package/dist/{syncFilterUtils-ClEj4Oeg.js → syncFilterUtils-SMop8ckR.js} +177 -62
  69. package/dist/syncFilterUtils-SMop8ckR.js.map +1 -0
  70. package/package.json +21 -21
  71. package/dist/Announcement-Cnz80WGx.js.map +0 -1
  72. package/dist/FilterBar-ecqJDraA.js.map +0 -1
  73. package/dist/ProgressBar-TQ0fPmn9.js.map +0 -1
  74. package/dist/RichTextEditor-BpzUo8dB.js.map +0 -1
  75. package/dist/check_circle-C3DDoVXp.js +0 -8
  76. package/dist/check_circle-C3DDoVXp.js.map +0 -1
  77. package/dist/index-Dt0zG4YB.js.map +0 -1
  78. package/dist/src/internal/components/InView.d.ts +0 -14
  79. package/dist/syncFilterUtils-ClEj4Oeg.js.map +0 -1
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import './index2.css';export { DndSort, DndSortCard, DndSortZone } from './DndSo
2
2
  export { A as Alert } from './Alert-lLo1N7Tj.js';
3
3
  import { P as Popover, a as usePopoverContext } from './AiMark-BfmK30vL.js';
4
4
  export { A as AiMark } from './AiMark-BfmK30vL.js';
5
- export { A as Announcement } from './Announcement-Cnz80WGx.js';
5
+ export { A as Announcement } from './Announcement-PfnsVVz1.js';
6
6
  export { A as Avatar } from './Avatar-Ba662b3z.js';
7
7
  export { A as AvatarGroup } from './Avatar-CwnLhw9m.js';
8
8
  export { B as Badge } from './Badge-BNrYBFiU.js';
@@ -22,10 +22,10 @@ export { D as DateFieldYearless } from './DateFieldYearless-CMm8vtz_.js';
22
22
  export { D as DateFieldYearlessRange } from './DateFieldYearlessRange-BHHs-Z1Q.js';
23
23
  export { D as DaysOfTheWeek } from './DaysOfTheWeek-BHXxs0Bo.js';
24
24
  export { D as Details } from './Details-BxqP-5vp.js';
25
- import { D as Dialog } from './Dialog-B9llUKJz.js';
25
+ import { D as Dialog } from './Dialog-_tHqOsbr.js';
26
26
  export { D as Divider } from './Divider-C7fp5Xb6.js';
27
27
  export { D as Dnd } from './index-Bldaza5t.js';
28
- export { D as Drawer } from './Drawer-Bjd2XLUX.js';
28
+ export { D as Drawer } from './Drawer-D-ThWNp0.js';
29
29
  export { D as DrillDown, b as DrillDownContent, c as DrillDownFooter, a as DrillDownHeader, d as DrillDownNextButton, e as DrillDownPrevButton, u as useDrillDown } from './useDrilldown-BOeeNUsy.js';
30
30
  export { E as EditCard } from './EditCard-B6l2vhBS.js';
31
31
  export { F as FieldLabel } from './FieldLabel-ThEmUiAq.js';
@@ -49,17 +49,17 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
49
49
  import { createContext, forwardRef, useContext, useState, useLayoutEffect, useRef, useEffect, useMemo, useCallback } from 'react';
50
50
  import { c as cx } from './index-De1g9FRV.js';
51
51
  import { u as useMergeRefs } from './useMergeRefs-Dfmtq9cI.js';
52
- export { P as Pagination } from './Pagination-Su4VBAhT.js';
52
+ export { P as Pagination } from './Pagination-Bsl99RRI.js';
53
53
  export { P as Popover } from './Popover-CIh1CsoJ.js';
54
- export { P as ProgressBar } from './ProgressBar-TQ0fPmn9.js';
54
+ export { P as ProgressBar } from './ProgressBar-zPKYv4Il.js';
55
55
  export { R as Radio } from './Radio-C1VnatcL.js';
56
56
  export { S as SearchField } from './SearchField-ChoNtw8w.js';
57
57
  export { S as SegmentedControl, a as SegmentedControlSegment } from './SegmentedControl-BHVJmmTH.js';
58
58
  export { S as SelectCard } from './SelectCard-DrmMU_GD.js';
59
- export { S as SelectField, a as SelectFieldSync } from './SelectFieldSync-BN2ebLWT.js';
60
- export { M as MultiSelectField, a as MultiSelectFieldSync } from './MultiSelectFieldSync-DpPO0cya.js';
61
- export { S as SelectMenu, a as SelectMenuSync } from './SelectMenuSync-DuPf8KCc.js';
62
- export { M as MultiSelectMenu, a as MultiSelectMenuSync } from './MultiSelectMenuSync-BN_3moW_.js';
59
+ export { S as SelectField, a as SelectFieldSync } from './SelectFieldSync-SS7FELT3.js';
60
+ export { M as MultiSelectField, a as MultiSelectFieldSync } from './MultiSelectFieldSync-DuImKXuj.js';
61
+ export { S as SelectMenu, a as SelectMenuSync } from './SelectMenuSync-CPpE6vUG.js';
62
+ export { M as MultiSelectMenu, a as MultiSelectMenuSync } from './MultiSelectMenuSync-BhNXsgAN.js';
63
63
  export { S as SelectTrigger } from './SelectTrigger-B5h-aqhl.js';
64
64
  export { S as SideNav } from './SideNav-CHG7i8Dy.js';
65
65
  export { Skeleton, SkeletonCircle, SkeletonPill, SkeletonRectangle, SkeletonText } from './Skeleton.js';
@@ -72,8 +72,8 @@ import { T as Text } from './Text-rihxXHSe.js';
72
72
  export { T as Textarea } from './Textarea-ppapJUwh.js';
73
73
  import { T as TextField } from './TextField-Bxd-Qzoi.js';
74
74
  export { T as TimeField } from './TimeField-Hwt_oRbg.js';
75
- export { T as Toaster } from './Toaster-BtP_HezK.js';
76
- export { t as toast } from './Toaster-BryQmKe1.js';
75
+ export { T as Toaster } from './Toaster-dVGVVcat.js';
76
+ export { t as toast } from './Toaster-SqkX0Kes.js';
77
77
  export { T as Toolbar } from './Toolbar-CMulzswf.js';
78
78
  import { T as Tooltip } from './Tooltip-C_VcYS9P.js';
79
79
  export { u as useAccessibleColor } from './useAccessibleColor-BYKjkGRg.js';
@@ -35,6 +35,20 @@ export type PaginationProps = Omit<BasePaginationProps, "children"> & DataTracki
35
35
  onItemsPerPageChange?: (itemsPerPage: number) => void;
36
36
  /** Label before the page-size menu. Defaults to `"Rows per page"`. */
37
37
  itemsPerPageLabel?: string;
38
+ /**
39
+ * Full item-range string, shown in the counter and after the live region's
40
+ * `Page {page} of {totalPages}` prefix. Called with the first visible item
41
+ * (1-based), the last visible item, and the total. When the total is `0`,
42
+ * all three arguments are `0`. Not called when `totalItemCount` is omitted.
43
+ * When this prop is omitted, the range reads `"1 - 10 of 100 items"`
44
+ * (or `"0 items"`).
45
+ *
46
+ * @example
47
+ * itemRangeFormatter={(first, last, total) =>
48
+ * total === 0 ? "0 files" : `${first} - ${last} of ${total} files`
49
+ * }
50
+ */
51
+ itemRangeFormatter?: (firstItem: number, lastItem: number, totalItemCount: number) => string;
38
52
  };
39
53
  /**
40
54
  * Paged navigation for a result set. Owns page-list generation, overflow,
@@ -70,4 +84,18 @@ export declare const Pagination: import('react').ForwardRefExoticComponent<Omit<
70
84
  onItemsPerPageChange?: (itemsPerPage: number) => void;
71
85
  /** Label before the page-size menu. Defaults to `"Rows per page"`. */
72
86
  itemsPerPageLabel?: string;
87
+ /**
88
+ * Full item-range string, shown in the counter and after the live region's
89
+ * `Page {page} of {totalPages}` prefix. Called with the first visible item
90
+ * (1-based), the last visible item, and the total. When the total is `0`,
91
+ * all three arguments are `0`. Not called when `totalItemCount` is omitted.
92
+ * When this prop is omitted, the range reads `"1 - 10 of 100 items"`
93
+ * (or `"0 items"`).
94
+ *
95
+ * @example
96
+ * itemRangeFormatter={(first, last, total) =>
97
+ * total === 0 ? "0 files" : `${first} - ${last} of ${total} files`
98
+ * }
99
+ */
100
+ itemRangeFormatter?: (firstItem: number, lastItem: number, totalItemCount: number) => string;
73
101
  } & import('react').RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,13 @@
1
+ type LoadMoreSlotProps = {
2
+ /** Shows the spinner in place of the button. */
3
+ loadingMore: boolean;
4
+ onLoadMore: () => void;
5
+ /** Reports whether the button has focus, including `false` when it unmounts. */
6
+ onFocusChange: (focused: boolean) => void;
7
+ };
8
+ /**
9
+ * Content of the option panels' load-more slot: the Load more button, or a
10
+ * spinner while a page loads.
11
+ */
12
+ export declare const LoadMoreSlot: ({ loadingMore, ...buttonProps }: LoadMoreSlotProps) => import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -0,0 +1,40 @@
1
+ type Keyed = {
2
+ id: string | number;
3
+ };
4
+ type AutoLoadMoreState = {
5
+ isOpen: boolean;
6
+ hasMore: boolean;
7
+ loading: boolean;
8
+ loadingMore: boolean;
9
+ disableAutoLoadMore?: boolean;
10
+ items: readonly Keyed[];
11
+ };
12
+ /**
13
+ * Decides when a lazy option list may request its next page on its own.
14
+ *
15
+ * `canAutoLoad` is false while the list is closed, out of pages, loading, or
16
+ * opted out, and while Load more has focus: focusing it scrolls it into view,
17
+ * and auto-loading then would swap it for the spinner under a keyboard user
18
+ * and send their Enter to the input. Feed `onLoadMoreFocusChange` from the
19
+ * button.
20
+ *
21
+ * `isStalled` reports that the last load-more page added no rows, as a
22
+ * rejected or empty load does. Auto-load stops while it holds so a failing
23
+ * loader is not retried in a loop; the Load more button still retries, and
24
+ * closing the list clears it, so opening it again retries too. It is keyed to
25
+ * the rows (count and last id) rather than to request flags: any new rows, from
26
+ * a successful retry or a new search, re-arm auto-load, even when a loader that
27
+ * resolves in a microtask batches `loadingMore` true and false into one render.
28
+ *
29
+ * Call it before the effect that auto-loads: effects run in call order, so the
30
+ * stall state is current by the time that effect reads it in the same commit.
31
+ * `rowsKey` changes whenever the rows do, for effects that must re-run on a
32
+ * new page or result set.
33
+ */
34
+ export declare function useAutoLoadMore({ isOpen, hasMore, loading, loadingMore, disableAutoLoadMore, items, }: AutoLoadMoreState): {
35
+ canAutoLoad: boolean;
36
+ isStalled: () => boolean;
37
+ rowsKey: string;
38
+ onLoadMoreFocusChange: import('react').Dispatch<import('react').SetStateAction<boolean>>;
39
+ };
40
+ export {};
@@ -0,0 +1,10 @@
1
+ import { FocusEvent } from 'react';
2
+ /**
3
+ * Returns focus to where it came from when the element unmounts while focused.
4
+ * Chromium fires focusout with a null `relatedTarget` when a focused node is
5
+ * removed, which a popover reads as focus leaving it.
6
+ */
7
+ export declare function useFocusReturnOnUnmount<T extends HTMLElement>(): {
8
+ ref: (node: T | null) => void;
9
+ onFocus: (event: FocusEvent<T>) => void;
10
+ };
@@ -1,17 +1,16 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { S as Spinner } from './Spinner-B7tTWcP6.js';
3
- import { B as Button } from './Button-Dn6-01xl.js';
4
3
  import { c as cx } from './index-De1g9FRV.js';
5
4
  import { useState, useRef, useCallback, useEffect, useMemo, forwardRef, useId, useImperativeHandle } from 'react';
6
5
  import { C as COMMON_PART, A as ANV_PART_ATTR } from './anvParts-DFVq7-Cm.js';
7
- import { u as useIntersectionObserver } from './useIntersectionObserver-BEmMDO3P.js';
8
6
  import { T as Text } from './Text-rihxXHSe.js';
9
7
  import { D as Divider } from './Divider-C7fp5Xb6.js';
10
8
  import { u as useMergeRefs } from './useMergeRefs-Dfmtq9cI.js';
11
- import { S as SrOnly } from './SrOnly-pnf8ajnh.js';
12
9
  import { I as Icon } from './Icon-B5zwN7GD.js';
13
10
  import { S as SvgCheck } from './check-Cf67OWrZ.js';
14
11
  import { i as isThenable, c as OptionRow, d as OptionRowSideContent, e as OptionCheckbox, f as OptionContentArea, b as useAdaptiveView } from './useInvalidateOnOptionsChange-BD2VBpbK.js';
12
+ import { B as Button } from './Button-Dn6-01xl.js';
13
+ import { S as SrOnly } from './SrOnly-pnf8ajnh.js';
15
14
  import { d as defaultRangeExtractor, u as useVirtualizer } from './index-DXeGMe23.js';
16
15
  import { S as SvgAdd } from './add-BcQkAUip.js';
17
16
  import { m as matchSorter } from './match-sorter.esm-CGAauEiU.js';
@@ -261,20 +260,106 @@ const SelectOption = ({
261
260
  );
262
261
  };
263
262
 
264
- const InView = ({
265
- children,
266
- onVisibilityChange,
267
- rootMargin
263
+ function useFocusReturnOnUnmount() {
264
+ const nodeRef = useRef(null);
265
+ const returnToRef = useRef(null);
266
+ const ref = useCallback((node) => {
267
+ const previous = nodeRef.current;
268
+ nodeRef.current = node;
269
+ if (node || !previous) return;
270
+ const root = previous.getRootNode();
271
+ if (previous !== root.activeElement) return;
272
+ const returnTo = returnToRef.current;
273
+ if (returnTo?.isConnected) returnTo.focus({ preventScroll: true });
274
+ }, []);
275
+ const onFocus = useCallback((event) => {
276
+ if (event.relatedTarget instanceof HTMLElement) {
277
+ returnToRef.current = event.relatedTarget;
278
+ }
279
+ }, []);
280
+ return { ref, onFocus };
281
+ }
282
+
283
+ const LoadMoreSlot = ({
284
+ loadingMore,
285
+ ...buttonProps
286
+ }) => loadingMore ? /* @__PURE__ */ jsxs(Fragment, { children: [
287
+ /* @__PURE__ */ jsx(Spinner, { size: "small" }),
288
+ /* @__PURE__ */ jsx(SrOnly, { children: "Loading more..." })
289
+ ] }) : /* @__PURE__ */ jsx(LoadMoreButton, { ...buttonProps });
290
+ const LoadMoreButton = ({
291
+ onLoadMore,
292
+ onFocusChange
268
293
  }) => {
269
- const { ref } = useIntersectionObserver({
270
- onChange: (isIntersecting) => {
271
- onVisibilityChange?.(isIntersecting);
272
- },
273
- rootMargin
274
- });
275
- return /* @__PURE__ */ jsx("div", { ref, children });
294
+ const focus = useFocusReturnOnUnmount();
295
+ const onFocusChangeRef = useRef(onFocusChange);
296
+ onFocusChangeRef.current = onFocusChange;
297
+ useEffect(() => () => onFocusChangeRef.current(false), []);
298
+ return /* @__PURE__ */ jsx(
299
+ Button,
300
+ {
301
+ appearance: "secondary",
302
+ size: "small",
303
+ onMouseDown: (e) => e.preventDefault(),
304
+ ref: focus.ref,
305
+ onFocus: (e) => {
306
+ focus.onFocus(e);
307
+ onFocusChange(true);
308
+ },
309
+ onBlur: () => onFocusChange(false),
310
+ onClick: onLoadMore,
311
+ ...{ [ANV_PART_ATTR]: COMMON_PART.loadMoreButton },
312
+ children: "Load more"
313
+ }
314
+ );
276
315
  };
277
316
 
317
+ function useAutoLoadMore({
318
+ isOpen,
319
+ hasMore,
320
+ loading,
321
+ loadingMore,
322
+ disableAutoLoadMore,
323
+ items
324
+ }) {
325
+ const rowsKey = `${items.length}:${items[items.length - 1]?.id ?? ""}`;
326
+ const rowsKeyRef = useRef(rowsKey);
327
+ rowsKeyRef.current = rowsKey;
328
+ const stalledAtRef = useRef(null);
329
+ const keyAtStartRef = useRef(null);
330
+ useEffect(() => {
331
+ if (loading) {
332
+ stalledAtRef.current = null;
333
+ keyAtStartRef.current = null;
334
+ return;
335
+ }
336
+ if (loadingMore) {
337
+ keyAtStartRef.current ??= rowsKey;
338
+ return;
339
+ }
340
+ if (keyAtStartRef.current === null) return;
341
+ stalledAtRef.current = rowsKey === keyAtStartRef.current ? rowsKey : null;
342
+ keyAtStartRef.current = null;
343
+ }, [loading, loadingMore, rowsKey]);
344
+ useEffect(() => {
345
+ if (isOpen) return;
346
+ stalledAtRef.current = null;
347
+ keyAtStartRef.current = null;
348
+ }, [isOpen]);
349
+ const isStalled = useCallback(
350
+ () => stalledAtRef.current === rowsKeyRef.current,
351
+ []
352
+ );
353
+ const [loadMoreFocused, setLoadMoreFocused] = useState(false);
354
+ const canAutoLoad = isOpen && hasMore && !loading && !loadingMore && !disableAutoLoadMore && !loadMoreFocused;
355
+ return {
356
+ canAutoLoad,
357
+ isStalled,
358
+ rowsKey,
359
+ onLoadMoreFocusChange: setLoadMoreFocused
360
+ };
361
+ }
362
+
278
363
  const useOptionsSections = (items, sectionsMeta) => {
279
364
  return useMemo(() => {
280
365
  if (sectionsMeta.length === 0) {
@@ -436,11 +521,43 @@ const OptionsPanel = forwardRef(
436
521
  const anyPinnedLoading = sectionsMeta.some(
437
522
  (s) => s.type === "pinned" && s.loading
438
523
  );
439
- const handleLoaderVisibilityChange = (visible) => {
440
- if (visible && !disableAutoLoadMore) {
441
- onLoadMore();
442
- }
443
- };
524
+ const loadMoreSentinelRef = useRef(null);
525
+ const { canAutoLoad, isStalled, rowsKey, onLoadMoreFocusChange } = useAutoLoadMore({
526
+ isOpen,
527
+ hasMore,
528
+ loading,
529
+ loadingMore,
530
+ disableAutoLoadMore,
531
+ items
532
+ });
533
+ const onLoadMoreRef = useRef(onLoadMore);
534
+ onLoadMoreRef.current = onLoadMore;
535
+ const canAutoLoadRef = useRef(canAutoLoad);
536
+ canAutoLoadRef.current = canAutoLoad;
537
+ useEffect(() => {
538
+ if (!canAutoLoad || isStalled()) return;
539
+ const sentinel = loadMoreSentinelRef.current;
540
+ const scroller = scrollerRef.current;
541
+ if (!sentinel || !scroller) return;
542
+ let active = true;
543
+ const observer = new IntersectionObserver(
544
+ (entries) => {
545
+ if (!active) return;
546
+ const visible = entries.some((entry) => entry.isIntersecting);
547
+ if (visible && canAutoLoadRef.current && !isStalled()) {
548
+ active = false;
549
+ observer.disconnect();
550
+ onLoadMoreRef.current();
551
+ }
552
+ },
553
+ { threshold: 0, root: scroller }
554
+ );
555
+ observer.observe(sentinel);
556
+ return () => {
557
+ active = false;
558
+ observer.disconnect();
559
+ };
560
+ }, [canAutoLoad, rowsKey, isStalled]);
444
561
  const sections = useOptionsSections(items, sectionsMeta);
445
562
  const renderItem = (item, index) => /* @__PURE__ */ jsx(
446
563
  SelectOption,
@@ -519,26 +636,25 @@ const OptionsPanel = forwardRef(
519
636
  }
520
637
  ),
521
638
  isOpen && /* @__PURE__ */ jsx(Fragment, { children: loading || anyPinnedLoading ? /* @__PURE__ */ jsx("div", { role: "presentation", className: styles$1["empty"], children: /* @__PURE__ */ jsx(Spinner, { size: "medium" }) }) : items.length > 0 ? renderSections() : /* @__PURE__ */ jsx("div", { role: "presentation", className: styles$1["empty"], children: /* @__PURE__ */ jsx(Text, { subdued: true, size: "small", children: "No match found" }) }) }),
522
- loadingMore ? /* @__PURE__ */ jsxs("div", { role: "presentation", className: styles$1["load-more"], children: [
523
- /* @__PURE__ */ jsx(Spinner, { size: "small" }),
524
- /* @__PURE__ */ jsx(SrOnly, { children: "Loading more..." })
525
- ] }) : hasMore && !loading ? /* @__PURE__ */ jsx("div", { role: "presentation", children: /* @__PURE__ */ jsx(
526
- InView,
527
- {
528
- onVisibilityChange: handleLoaderVisibilityChange,
529
- rootMargin: "0px 0px -100px 0px",
530
- children: /* @__PURE__ */ jsx("div", { className: styles$1["load-more"], children: /* @__PURE__ */ jsx(
531
- Button,
532
- {
533
- appearance: "secondary",
534
- size: "small",
535
- onClick: onLoadMore,
536
- ...{ [ANV_PART_ATTR]: COMMON_PART.loadMoreButton },
537
- children: "Load more"
538
- }
539
- ) })
540
- }
541
- ) }) : null
639
+ hasMore && !loading ? /* @__PURE__ */ jsxs("div", { role: "presentation", children: [
640
+ /* @__PURE__ */ jsx(
641
+ "div",
642
+ {
643
+ ref: loadMoreSentinelRef,
644
+ className: styles$1["scroll-sentinel"],
645
+ role: "presentation",
646
+ "aria-hidden": "true"
647
+ }
648
+ ),
649
+ /* @__PURE__ */ jsx("div", { className: styles$1["load-more"], children: /* @__PURE__ */ jsx(
650
+ LoadMoreSlot,
651
+ {
652
+ loadingMore,
653
+ onLoadMore,
654
+ onFocusChange: onLoadMoreFocusChange
655
+ }
656
+ ) })
657
+ ] }) : null
542
658
  ] }) });
543
659
  }
544
660
  );
@@ -679,20 +795,27 @@ const VirtualizedOptionsPanel = forwardRef(
679
795
  }));
680
796
  const onLoadMoreRef = useRef(onLoadMore);
681
797
  onLoadMoreRef.current = onLoadMore;
798
+ const { canAutoLoad, isStalled, rowsKey, onLoadMoreFocusChange } = useAutoLoadMore({
799
+ isOpen,
800
+ hasMore,
801
+ loading,
802
+ loadingMore,
803
+ disableAutoLoadMore,
804
+ items
805
+ });
806
+ const loadMoreInFlightRef = useRef(false);
807
+ useEffect(() => {
808
+ if (!loadingMore) loadMoreInFlightRef.current = false;
809
+ }, [isOpen, loadingMore, rowsKey]);
682
810
  useEffect(() => {
683
- if (!hasMore || loadingMore || disableAutoLoadMore) return;
811
+ if (!canAutoLoad || loadMoreInFlightRef.current || isStalled()) return;
684
812
  const lastItem = virtualItems[virtualItems.length - 1];
685
813
  if (!lastItem) return;
686
814
  if (lastItem.index >= virtualRows.length - 1) {
815
+ loadMoreInFlightRef.current = true;
687
816
  onLoadMoreRef.current();
688
817
  }
689
- }, [
690
- hasMore,
691
- loadingMore,
692
- disableAutoLoadMore,
693
- virtualItems,
694
- virtualRows.length
695
- ]);
818
+ }, [canAutoLoad, virtualItems, virtualRows.length]);
696
819
  const isSticky = useCallback(
697
820
  (index) => stickyIndices.includes(index),
698
821
  [stickyIndices]
@@ -740,22 +863,14 @@ const VirtualizedOptionsPanel = forwardRef(
740
863
  );
741
864
  }
742
865
  if (row.type === "load-more") {
743
- if (loadingMore) {
744
- return /* @__PURE__ */ jsxs("div", { role: "presentation", className: styles$1["load-more"], children: [
745
- /* @__PURE__ */ jsx(Spinner, { size: "small" }),
746
- /* @__PURE__ */ jsx(SrOnly, { children: "Loading more..." })
747
- ] });
748
- }
749
- return /* @__PURE__ */ jsx("div", { role: "presentation", children: /* @__PURE__ */ jsx("div", { className: styles$1["load-more"], children: /* @__PURE__ */ jsx(
750
- Button,
866
+ return /* @__PURE__ */ jsx("div", { role: "presentation", className: styles$1["load-more"], children: /* @__PURE__ */ jsx(
867
+ LoadMoreSlot,
751
868
  {
752
- appearance: "secondary",
753
- size: "small",
754
- onClick: () => onLoadMoreRef.current(),
755
- ...{ [ANV_PART_ATTR]: COMMON_PART.loadMoreButton },
756
- children: "Load more"
869
+ loadingMore,
870
+ onLoadMore,
871
+ onFocusChange: onLoadMoreFocusChange
757
872
  }
758
- ) }) });
873
+ ) });
759
874
  }
760
875
  return null;
761
876
  };
@@ -1241,4 +1356,4 @@ const sortByGroup = (options, groupSorter) => {
1241
1356
  };
1242
1357
 
1243
1358
  export { AddNewItemButton as A, OptionsPanel as O, VirtualizedOptionsPanel as V, useProcessedOptions as a, buildSelectItems as b, sortByGroup as c, defaultSyncFilter as d, toSelectItem as e, shouldShowAddNewItem as s, toSyncFilterFn as t, useSelectOrchestration as u };
1244
- //# sourceMappingURL=syncFilterUtils-ClEj4Oeg.js.map
1359
+ //# sourceMappingURL=syncFilterUtils-SMop8ckR.js.map