@servicetitan/anvil2 1.45.1 → 1.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/CHANGELOG.md +34 -0
- package/dist/{Calendar-Dq_of5A3.js → Calendar-BQ5F2ENO.js} +1173 -166
- package/dist/Calendar-BQ5F2ENO.js.map +1 -0
- package/dist/Calendar.css +83 -47
- package/dist/Calendar.js +1 -1
- package/dist/{Checkbox-3y2d9mBd.js → Checkbox-DDrmVC-u.js} +2 -2
- package/dist/{Checkbox-3y2d9mBd.js.map → Checkbox-DDrmVC-u.js.map} +1 -1
- package/dist/{Checkbox-BiOzGpgJ.js → Checkbox-Dl4KTwEJ.js} +3 -3
- package/dist/{Checkbox-BiOzGpgJ.js.map → Checkbox-Dl4KTwEJ.js.map} +1 -1
- package/dist/Checkbox.js +2 -2
- package/dist/{useInfiniteCombobox-BN2uGhBh.js → Combobox-B9nesJuc.js} +19 -186
- package/dist/Combobox-B9nesJuc.js.map +1 -0
- package/dist/Combobox.js +2 -1
- package/dist/Combobox.js.map +1 -1
- package/dist/{DateField-B2mnasH5.js → DateField-DXxPsRtf.js} +4 -4
- package/dist/{DateField-B2mnasH5.js.map → DateField-DXxPsRtf.js.map} +1 -1
- package/dist/DateField.js +1 -1
- package/dist/{DateFieldRange-DPbyzuHb.js → DateFieldRange-Xauviu1w.js} +34 -28
- package/dist/DateFieldRange-Xauviu1w.js.map +1 -0
- package/dist/DateFieldRange.js +1 -1
- package/dist/{DateFieldSingle-CfAJDcx9.js → DateFieldSingle-yLnwpVzd.js} +27 -27
- package/dist/DateFieldSingle-yLnwpVzd.js.map +1 -0
- package/dist/DateFieldSingle.js +1 -1
- package/dist/{DateFieldYearless-ydxcsmRV.js → DateFieldYearless-Ba7HiTiI.js} +2 -2
- package/dist/{DateFieldYearless-ydxcsmRV.js.map → DateFieldYearless-Ba7HiTiI.js.map} +1 -1
- package/dist/DateFieldYearless.js +1 -1
- package/dist/{DaysOfTheWeek-BYvE9QK5.js → DaysOfTheWeek-BYSYZySH.js} +2 -2
- package/dist/{DaysOfTheWeek-BYvE9QK5.js.map → DaysOfTheWeek-BYSYZySH.js.map} +1 -1
- package/dist/DaysOfTheWeek.js +1 -1
- package/dist/{Dialog-DpH2Qvbd.js → Dialog-CgkrvrQu.js} +2 -2
- package/dist/{Dialog-DpH2Qvbd.js.map → Dialog-CgkrvrQu.js.map} +1 -1
- package/dist/Dialog.js +1 -1
- package/dist/Dnd.js +1 -1
- package/dist/DndSort.js +1 -1
- package/dist/{Drawer-DI_k6W2k.js → Drawer-CM4ZbAro.js} +2 -2
- package/dist/{Drawer-DI_k6W2k.js.map → Drawer-CM4ZbAro.js.map} +1 -1
- package/dist/Drawer.js +1 -1
- package/dist/{FieldLabel-BfnCXung.js → FieldLabel-BsbTTyom.js} +3 -14
- package/dist/FieldLabel-BsbTTyom.js.map +1 -0
- package/dist/FieldLabel.js +1 -1
- package/dist/{InputMask-O_qi1p_3.js → InputMask-_F139qFu.js} +2 -2
- package/dist/{InputMask-O_qi1p_3.js.map → InputMask-_F139qFu.js.map} +1 -1
- package/dist/InputMask.js +1 -1
- package/dist/{ListView-jR2ZkBB7.js → ListView-pb3rIcze.js} +2 -2
- package/dist/{ListView-jR2ZkBB7.js.map → ListView-pb3rIcze.js.map} +1 -1
- package/dist/ListView.js +1 -1
- package/dist/{NumberField-1_gRsWT4.js → NumberField-CkZZrkYC.js} +5 -5
- package/dist/{NumberField-1_gRsWT4.js.map → NumberField-CkZZrkYC.js.map} +1 -1
- package/dist/NumberField.js +1 -1
- package/dist/{Page-CY6QRlvb.js → Page-cKXkjMmd.js} +2 -2
- package/dist/{Page-CY6QRlvb.js.map → Page-cKXkjMmd.js.map} +1 -1
- package/dist/Page.js +1 -1
- package/dist/{ProgressBar-BnXuQ6Bx.js → ProgressBar-DXcXZEJ2.js} +2 -2
- package/dist/{ProgressBar-BnXuQ6Bx.js.map → ProgressBar-DXcXZEJ2.js.map} +1 -1
- package/dist/ProgressBar.js +1 -1
- package/dist/{Radio-CwmRUIzo.js → Radio-C89VCMXd.js} +2 -2
- package/dist/{Radio-CwmRUIzo.js.map → Radio-C89VCMXd.js.map} +1 -1
- package/dist/Radio.js +2 -2
- package/dist/{RadioGroup-BNCWIHOG.js → RadioGroup-C_4buUtG.js} +2 -2
- package/dist/{RadioGroup-BNCWIHOG.js.map → RadioGroup-C_4buUtG.js.map} +1 -1
- package/dist/{SearchField-BKXkoWPs.js → SearchField-Bb0uObwG.js} +2 -2
- package/dist/{SearchField-BKXkoWPs.js.map → SearchField-Bb0uObwG.js.map} +1 -1
- package/dist/SearchField.js +1 -1
- package/dist/{SelectCard-DOGCG0zr.js → SelectCard-BTYZg9TG.js} +3 -3
- package/dist/{SelectCard-DOGCG0zr.js.map → SelectCard-BTYZg9TG.js.map} +1 -1
- package/dist/SelectCard.js +1 -1
- package/dist/{SelectTrigger-BMo0e-F7.js → SelectTrigger-f1hvRrSC.js} +2 -2
- package/dist/{SelectTrigger-BMo0e-F7.js.map → SelectTrigger-f1hvRrSC.js.map} +1 -1
- package/dist/SelectTrigger.js +1 -1
- package/dist/{SelectTriggerBase-BMMxnXrw.js → SelectTriggerBase-DP9fmRSo.js} +2 -2
- package/dist/{SelectTriggerBase-BMMxnXrw.js.map → SelectTriggerBase-DP9fmRSo.js.map} +1 -1
- package/dist/{TextField-CwgE_JJU.js → TextField-BiHxlzE3.js} +2 -2
- package/dist/{TextField-CwgE_JJU.js.map → TextField-BiHxlzE3.js.map} +1 -1
- package/dist/{TextField-DlsZEkS0.js → TextField-D8fow9j7.js} +2 -2
- package/dist/{TextField-DlsZEkS0.js.map → TextField-D8fow9j7.js.map} +1 -1
- package/dist/TextField.js +1 -1
- package/dist/{Textarea-DU-SpoDL.js → Textarea-BdVJJlbP.js} +2 -2
- package/dist/{Textarea-DU-SpoDL.js.map → Textarea-BdVJJlbP.js.map} +1 -1
- package/dist/Textarea.js +1 -1
- package/dist/{TimeField-CfhyRfX9.js → TimeField-CmbErrsZ.js} +2 -2
- package/dist/{TimeField-CfhyRfX9.js.map → TimeField-CmbErrsZ.js.map} +1 -1
- package/dist/TimeField.js +1 -1
- package/dist/Toast.js +2 -2
- package/dist/{Toaster-BgjT0p8b.js → Toaster-9cpG_tWR.js} +2 -2
- package/dist/{Toaster-BgjT0p8b.js.map → Toaster-9cpG_tWR.js.map} +1 -1
- package/dist/{Toaster-CfC9wod0.js → Toaster-B7zUwJOt.js} +2 -2
- package/dist/{Toaster-CfC9wod0.js.map → Toaster-B7zUwJOt.js.map} +1 -1
- package/dist/Toolbar-D4zuUFhb.js +2077 -0
- package/dist/Toolbar-D4zuUFhb.js.map +1 -0
- package/dist/Toolbar.css +139 -28
- package/dist/Toolbar.d.ts +3 -3
- package/dist/Toolbar.js +1 -1
- package/dist/beta/components/Toolbar/Filters/FilterButton.d.ts +30 -0
- package/dist/beta/components/Toolbar/Filters/FilterDateRange.d.ts +37 -0
- package/dist/beta/components/Toolbar/Filters/FilterDateSingle.d.ts +30 -0
- package/dist/beta/components/Toolbar/Filters/FilterDrawer.d.ts +15 -0
- package/dist/beta/components/Toolbar/Filters/FilterGroup.d.ts +25 -0
- package/dist/beta/components/Toolbar/Filters/FilterItemWrapper.d.ts +24 -0
- package/dist/beta/components/Toolbar/Filters/FilterSelect.d.ts +29 -0
- package/dist/beta/components/Toolbar/Filters/FilterToggleButton.d.ts +24 -0
- package/dist/beta/components/Toolbar/Filters/internal/FilterGroupContext.d.ts +40 -0
- package/dist/beta/components/Toolbar/Filters/internal/types.d.ts +130 -0
- package/dist/beta/components/Toolbar/Filters/internal/utils/filter-state.d.ts +40 -0
- package/dist/beta/components/Toolbar/Filters/internal/utils/test.d.ts +57 -0
- package/dist/beta/components/Toolbar/Toolbar.d.ts +302 -0
- package/dist/beta/components/Toolbar/ToolbarButton.d.ts +41 -0
- package/dist/beta/components/Toolbar/ToolbarButtonLink.d.ts +43 -0
- package/dist/beta/components/Toolbar/ToolbarButtonToggle.d.ts +42 -0
- package/dist/beta/components/Toolbar/ToolbarControlGroup.d.ts +20 -0
- package/dist/beta/components/Toolbar/ToolbarSearchField.d.ts +20 -0
- package/dist/beta/components/Toolbar/ToolbarSelect.d.ts +108 -0
- package/dist/beta/components/Toolbar/index.d.ts +9 -0
- package/dist/beta/components/Toolbar/internal/ToolbarItemOverflowContext.d.ts +19 -0
- package/dist/beta/components/Toolbar/internal/ToolbarItemWrapper.d.ts +40 -0
- package/dist/beta/components/Toolbar/internal/ToolbarOverflowContext.d.ts +35 -0
- package/dist/beta/components/Toolbar/internal/ToolbarOverflowMenu.d.ts +29 -0
- package/dist/beta/components/Toolbar/internal/utils/accessibility.d.ts +26 -0
- package/dist/beta/components/Toolbar/internal/utils/test.d.ts +29 -0
- package/dist/beta/components/Toolbar/types.d.ts +50 -0
- package/dist/beta/components/index.d.ts +1 -0
- package/dist/beta/index.d.ts +1 -0
- package/dist/beta.d.ts +2 -0
- package/dist/beta.js +2 -0
- package/dist/beta.js.map +1 -0
- package/dist/components/Combobox/ComboboxTypes.d.ts +8 -0
- package/dist/components/DateFieldRange/DateFieldRange.d.ts +2 -2
- package/dist/components/DateFieldRange/internal/DateFieldRangeCalendar.d.ts +1 -1
- package/dist/components/DateFieldRange/internal/useDateFieldRangeState.d.ts +2 -7
- package/dist/components/DateFieldSingle/DateFieldSingle.d.ts +2 -2
- package/dist/components/DateFieldSingle/internal/useDateFieldSingleState.d.ts +2 -7
- package/dist/components/Dialog/index.d.ts +1 -1
- package/dist/components/NumberField/NumberField.d.ts +4 -4
- package/dist/{indeterminate_check_box-Bg24oeHy.js → indeterminate_check_box-RY9zr3xS.js} +17 -17
- package/dist/{indeterminate_check_box-Bg24oeHy.js.map → indeterminate_check_box-RY9zr3xS.js.map} +1 -1
- package/dist/indeterminate_check_box.css +72 -66
- package/dist/{index-CqdP5W00.js → index-V5Ez2gq_.js} +2 -2
- package/dist/{index-CqdP5W00.js.map → index-V5Ez2gq_.js.map} +1 -1
- package/dist/index.css +125 -26
- package/dist/index.js +773 -41
- package/dist/index.js.map +1 -1
- package/dist/index2.css +88 -105
- package/dist/internal/components/YearSelector/YearSelector.d.ts +67 -0
- package/dist/internal/components/YearSelector/index.d.ts +1 -0
- package/dist/internal/components/YearSelector/internal/YearItem.d.ts +10 -0
- package/dist/internal/components/YearSelector/internal/useYearSelectorKeys.d.ts +19 -0
- package/dist/internal/hooks/index.d.ts +1 -0
- package/dist/internal/hooks/useContainerQuery/index.d.ts +1 -0
- package/dist/internal/hooks/useContainerQuery/useContainerQuery.d.ts +46 -0
- package/dist/internal/hooks/useFocusWithin/useFocusWithin.d.ts +11 -16
- package/dist/types/PassThroughProps.d.ts +4 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/{useDateFieldOrchestration-Cqa7dxtr.js → useDateFieldOrchestration-BNJCsRkS.js} +2 -2
- package/dist/{useDateFieldOrchestration-Cqa7dxtr.js.map → useDateFieldOrchestration-BNJCsRkS.js.map} +1 -1
- package/dist/useFocusWithin-BhhgRXdZ.js +32 -0
- package/dist/useFocusWithin-BhhgRXdZ.js.map +1 -0
- package/dist/useInfiniteCombobox-WcRgC9p6.js +179 -0
- package/dist/useInfiniteCombobox-WcRgC9p6.js.map +1 -0
- package/dist/useIntersectionObserver-BEmMDO3P.js +70 -0
- package/dist/useIntersectionObserver-BEmMDO3P.js.map +1 -0
- package/package.json +3 -1
- package/dist/Calendar-Dq_of5A3.js.map +0 -1
- package/dist/DateFieldRange-DPbyzuHb.js.map +0 -1
- package/dist/DateFieldSingle-CfAJDcx9.js.map +0 -1
- package/dist/FieldLabel-BfnCXung.js.map +0 -1
- package/dist/Toolbar-DK7tXy_W.js +0 -807
- package/dist/Toolbar-DK7tXy_W.js.map +0 -1
- package/dist/components/Calendar/internal/CalendarYearSelection.d.ts +0 -25
- package/dist/useFocusWithin-BhU7hoAD.js +0 -56
- package/dist/useFocusWithin-BhU7hoAD.js.map +0 -1
- package/dist/useInfiniteCombobox-BN2uGhBh.js.map +0 -1
- /package/dist/{useInfiniteCombobox.css → Combobox.css} +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { PassThroughProps } from '../../../types';
|
|
2
|
+
export interface YearSelectorProps {
|
|
3
|
+
/**
|
|
4
|
+
* The minimum selectable year
|
|
5
|
+
* Defaults to 1900.
|
|
6
|
+
*/
|
|
7
|
+
min?: number;
|
|
8
|
+
/**
|
|
9
|
+
* The maximum selectable year.
|
|
10
|
+
* Defaults to 2200.
|
|
11
|
+
*/
|
|
12
|
+
max?: number;
|
|
13
|
+
/**
|
|
14
|
+
* The currently selected year
|
|
15
|
+
*/
|
|
16
|
+
value?: number | null;
|
|
17
|
+
/**
|
|
18
|
+
* The default value of the selected year
|
|
19
|
+
*/
|
|
20
|
+
defaultValue?: number;
|
|
21
|
+
/**
|
|
22
|
+
* The function to call when the selected year changes
|
|
23
|
+
*
|
|
24
|
+
* @param year - The year that is selected.
|
|
25
|
+
*/
|
|
26
|
+
onChange?: (year: number | null) => void;
|
|
27
|
+
/**
|
|
28
|
+
* The function to call when the selected year is affirmatively changed.
|
|
29
|
+
* This is called when the user confirms the selection by pressing Enter, pressing Space, or clicking.
|
|
30
|
+
*
|
|
31
|
+
* @param year - The year that is selected.
|
|
32
|
+
*/
|
|
33
|
+
onConfirm?: (year: number | null) => void;
|
|
34
|
+
/**
|
|
35
|
+
* If a `value` or `defaultValue` is not provided, this will be used as the starting scroll position (and initial focus for accessibility).
|
|
36
|
+
*/
|
|
37
|
+
startingYear?: number;
|
|
38
|
+
/**
|
|
39
|
+
* The number of columns to display.
|
|
40
|
+
* @default 2
|
|
41
|
+
*/
|
|
42
|
+
columns?: number;
|
|
43
|
+
/**
|
|
44
|
+
* The class name to apply to the component.
|
|
45
|
+
*/
|
|
46
|
+
className?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Controls whether a year can be deselected, i.e. set to null.
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
51
|
+
required?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Controls whether the component allows the user to select a year.
|
|
54
|
+
* @default false
|
|
55
|
+
*/
|
|
56
|
+
disabled?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* The aria-label for the component.
|
|
59
|
+
* @default "Year selection"
|
|
60
|
+
*/
|
|
61
|
+
ariaLabel?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Props forwarded onto the root element of the component.
|
|
64
|
+
*/
|
|
65
|
+
rootProps?: PassThroughProps<"div">;
|
|
66
|
+
}
|
|
67
|
+
export declare const YearSelector: import('react').ForwardRefExoticComponent<YearSelectorProps & import('react').RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { YearSelector, type YearSelectorProps } from './YearSelector';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef } from 'react';
|
|
2
|
+
type YearItemProps = Omit<ComponentPropsWithoutRef<"div">, "onChange"> & {
|
|
3
|
+
year: number;
|
|
4
|
+
onChange: (year: number) => void;
|
|
5
|
+
selected: boolean;
|
|
6
|
+
pseudoFocused: boolean;
|
|
7
|
+
disabled: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare const YearItem: ({ year, onChange, selected, pseudoFocused, disabled, onFocus, ...rest }: YearItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type UseYearSelectorKeysParams = {
|
|
2
|
+
columns: number;
|
|
3
|
+
scrollToYear: (year: number, { focus }: {
|
|
4
|
+
focus?: boolean;
|
|
5
|
+
}) => void;
|
|
6
|
+
initialFocus: number;
|
|
7
|
+
onSelect: (year: number | null) => void;
|
|
8
|
+
onConfirm: () => void;
|
|
9
|
+
min: number;
|
|
10
|
+
max: number;
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
pseudoFocused: number;
|
|
13
|
+
setPseudoFocused: (year: number) => void;
|
|
14
|
+
required: boolean;
|
|
15
|
+
};
|
|
16
|
+
export type UseYearSelectorKeysReturn = {
|
|
17
|
+
listRef: React.RefObject<HTMLDivElement>;
|
|
18
|
+
};
|
|
19
|
+
export declare const useYearSelectorKeys: ({ columns, scrollToYear, initialFocus, onSelect, onConfirm, pseudoFocused, setPseudoFocused, min, max, disabled, required, }: UseYearSelectorKeysParams) => UseYearSelectorKeysReturn;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useContainerQuery, type ContainerQueryReturnProps, } from './useContainerQuery';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Return type for the useContainerQuery hook
|
|
4
|
+
*/
|
|
5
|
+
export type ContainerQueryReturnProps = {
|
|
6
|
+
/**
|
|
7
|
+
* The breakpoint name
|
|
8
|
+
*/
|
|
9
|
+
name: "xs" | "sm" | "md" | "lg" | "xl" | "xxl";
|
|
10
|
+
/**
|
|
11
|
+
* Minimum width for this breakpoint
|
|
12
|
+
*/
|
|
13
|
+
min: number | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Maximum width for this breakpoint
|
|
16
|
+
*/
|
|
17
|
+
max: number | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Current container width
|
|
20
|
+
*/
|
|
21
|
+
containerWidth: number;
|
|
22
|
+
/**
|
|
23
|
+
* Current container height
|
|
24
|
+
*/
|
|
25
|
+
containerHeight: number;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Custom hook for detecting current breakpoint based on container dimensions.
|
|
29
|
+
*
|
|
30
|
+
* Features:
|
|
31
|
+
* - Detects current breakpoint based on container width instead of viewport width
|
|
32
|
+
* - Supports all standard breakpoints (xs, sm, md, lg, xl, xxl)
|
|
33
|
+
* - Provides container dimensions information
|
|
34
|
+
* - Uses hammer-token breakpoint values for consistency
|
|
35
|
+
* - Listens for container resize events using ResizeObserver
|
|
36
|
+
* - Supports optional disable functionality
|
|
37
|
+
* - Uses container element dimensions for accurate measurements
|
|
38
|
+
*
|
|
39
|
+
* @param containerRef - React ref to the container element to observe
|
|
40
|
+
* @param props - Optional configuration object
|
|
41
|
+
* @param props.disable - Whether to disable the hook
|
|
42
|
+
* @returns Current breakpoint information or undefined if disabled
|
|
43
|
+
*/
|
|
44
|
+
export declare const useContainerQuery: (containerRef: RefObject<HTMLElement>, props?: {
|
|
45
|
+
disable?: boolean;
|
|
46
|
+
}) => ContainerQueryReturnProps | undefined;
|
|
@@ -1,33 +1,28 @@
|
|
|
1
|
+
import { FocusEventHandler } from 'react';
|
|
1
2
|
export interface UseFocusWithinOptions {
|
|
2
|
-
onFocus?: (event: FocusEvent) => void;
|
|
3
|
-
onBlur?: (event: FocusEvent) => void;
|
|
3
|
+
onFocus?: (event: React.FocusEvent) => void;
|
|
4
|
+
onBlur?: (event: React.FocusEvent) => void;
|
|
4
5
|
}
|
|
5
|
-
export interface UseFocusWithinReturnValue<T extends HTMLElement =
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
export interface UseFocusWithinReturnValue<T extends HTMLElement = HTMLElement> {
|
|
7
|
+
props: {
|
|
8
|
+
onFocus: FocusEventHandler<T>;
|
|
9
|
+
onBlur: FocusEventHandler<T>;
|
|
10
|
+
};
|
|
8
11
|
}
|
|
9
12
|
/**
|
|
10
13
|
* Custom hook for tracking focus state within a DOM element.
|
|
11
14
|
*
|
|
12
|
-
* Features:
|
|
13
|
-
* - Tracks whether any child element has focus
|
|
14
|
-
* - Provides a ref object to attach to the target element
|
|
15
|
-
* - Handles focus events with proper cleanup
|
|
16
|
-
* - Supports custom onFocus and onBlur callbacks
|
|
17
|
-
* - Prevents false blur events when focus moves between children
|
|
18
|
-
* - Automatically cleans up event listeners on unmount
|
|
19
|
-
*
|
|
20
15
|
* @param options Configuration options for the hook
|
|
21
16
|
* @param options.onFocus Optional callback when focus enters the element
|
|
22
17
|
* @param options.onBlur Optional callback when focus leaves the element
|
|
23
|
-
* @returns Object containing
|
|
18
|
+
* @returns Object containing props for the element
|
|
24
19
|
*
|
|
25
20
|
* @example
|
|
26
|
-
* const {
|
|
21
|
+
* const { props } = useFocusWithin({
|
|
27
22
|
* onFocus: () => console.log('Focus entered'),
|
|
28
23
|
* onBlur: () => console.log('Focus left')
|
|
29
24
|
* });
|
|
30
25
|
*
|
|
31
|
-
* return <div
|
|
26
|
+
* return <div {...props}>Content</div>;
|
|
32
27
|
*/
|
|
33
28
|
export declare function useFocusWithin<T extends HTMLElement = any>({ onBlur, onFocus, }?: UseFocusWithinOptions): UseFocusWithinReturnValue<T>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, ComponentPropsWithRef } from 'react';
|
|
2
|
+
export type DataAttributes = Record<`data-${string}`, string>;
|
|
3
|
+
export type PassThroughProps<T extends keyof JSX.IntrinsicElements> = ComponentPropsWithoutRef<T> & DataAttributes;
|
|
4
|
+
export type PassThroughPropsWithRef<T extends keyof JSX.IntrinsicElements> = ComponentPropsWithRef<T> & DataAttributes;
|
package/dist/types/index.d.ts
CHANGED
package/dist/{useDateFieldOrchestration-Cqa7dxtr.js → useDateFieldOrchestration-BNJCsRkS.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as DateTime } from './Calendar-
|
|
1
|
+
import { D as DateTime } from './Calendar-BQ5F2ENO.js';
|
|
2
2
|
import { useRef, useState } from 'react';
|
|
3
3
|
import { a as useKeyboardFocusables } from './useOnClickOutside-BHEWMLa9.js';
|
|
4
4
|
|
|
@@ -135,4 +135,4 @@ const useDateFieldOrchestration = ({
|
|
|
135
135
|
};
|
|
136
136
|
|
|
137
137
|
export { DateModeToPlaceholderMap as D, DateModeToFormatMap as a, convertStringToDate as c, useDateFieldOrchestration as u, validateDate as v };
|
|
138
|
-
//# sourceMappingURL=useDateFieldOrchestration-
|
|
138
|
+
//# sourceMappingURL=useDateFieldOrchestration-BNJCsRkS.js.map
|
package/dist/{useDateFieldOrchestration-Cqa7dxtr.js.map → useDateFieldOrchestration-BNJCsRkS.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDateFieldOrchestration-
|
|
1
|
+
{"version":3,"file":"useDateFieldOrchestration-BNJCsRkS.js","sources":["../src/components/DateFieldSingle/internal/constants.ts","../src/components/DateFieldSingle/internal/utils.ts","../src/components/DateFieldSingle/internal/useDateFieldOrchestration.ts"],"sourcesContent":["import { DateMode } from \"../types\";\n\nexport const DateModeToFormatMap: Record<DateMode, string> = {\n \"mm/dd/yyyy\": \"MM/dd/yyyy\",\n \"dd/mm/yyyy\": \"dd/MM/yyyy\",\n \"yyyy/mm/dd\": \"yyyy/MM/dd\",\n};\n\nexport const DateModeToPlaceholderMap: Record<DateMode, string> = {\n \"dd/mm/yyyy\": \"__/__/____\",\n \"mm/dd/yyyy\": \"__/__/____\",\n \"yyyy/mm/dd\": \"____/__/__\",\n};\n","import { DateTime } from \"luxon\";\n\nexport function convertStringToDate(\n v: string | null | undefined,\n): DateTime | null | undefined {\n if (v === undefined || v === null) {\n return v;\n }\n const date = DateTime.fromISO(v, { setZone: true, zone: \"utc\" }).startOf(\n \"day\",\n );\n if (date.isValid) {\n return date;\n }\n return null;\n}\nexport function validateDate({\n date,\n constraints,\n}: {\n date: DateTime | null;\n constraints: {\n required?: boolean;\n unavailable?: {\n dates?: DateTime[];\n daysOfWeek?: number[];\n };\n minDate?: DateTime;\n maxDate?: DateTime;\n };\n}) {\n const { required, unavailable, minDate, maxDate } = constraints;\n if (!date) {\n return required ? false : true;\n }\n if (unavailable?.dates?.some((d) => d.equals(date))) {\n return false;\n }\n if (unavailable?.daysOfWeek?.includes(date.weekday)) {\n return false;\n }\n if (minDate && date < minDate) {\n return false;\n }\n if (maxDate && date > maxDate) {\n return false;\n }\n return true;\n}\n","import { KeyboardEventHandler, RefObject, useRef, useState } from \"react\";\nimport { useKeyboardFocusables } from \"../../../internal/hooks\";\n\n/**\n * Responsible for orchestrating the input and calendar popover states\n * @param inputRef - The ref to the input element\n * @returns calendarOpen, setCalendarOpen, handleCalendarKeyDown, handleInputKeyDown\n */\nexport const useDateFieldOrchestration = ({\n inputRef,\n calendarDefaultOpen,\n popoverContentRef,\n disableCalendar = false,\n}: {\n inputRef: RefObject<HTMLInputElement>;\n calendarDefaultOpen: boolean;\n popoverContentRef: RefObject<HTMLDivElement>;\n disableCalendar?: boolean;\n}) => {\n const documentRef = useRef<HTMLElement>(document.body);\n const [calendarOpen, setCalendarOpen] = useState(calendarDefaultOpen);\n\n const { focusables } = useKeyboardFocusables(documentRef, {\n observeChange: true,\n });\n\n const { focusables: popoverFocusables } = useKeyboardFocusables(\n popoverContentRef,\n {\n observeChange: true,\n },\n );\n\n const pageFocusables = focusables?.filter(\n (item) => !popoverFocusables?.includes(item),\n );\n\n const handleCalendarKeyDown = (\n event: React.KeyboardEvent<HTMLDivElement>,\n ) => {\n if (event.key === \"Escape\") {\n inputRef.current?.focus();\n }\n };\n\n const focusToCalendar = () => {\n if (popoverContentRef.current) {\n const currentFocusable =\n popoverContentRef.current.querySelectorAll('[tabindex = \"0\"]')[0];\n if (currentFocusable) {\n (currentFocusable as HTMLButtonElement).focus();\n }\n }\n };\n\n const handleInputKeyDown: KeyboardEventHandler<HTMLInputElement> = (ev) => {\n if (disableCalendar) {\n return;\n }\n let currentFocusIndex = 0;\n switch (ev.key) {\n case \"Escape\":\n setCalendarOpen(false);\n break;\n case \"Tab\":\n if (!calendarOpen || !pageFocusables?.length) {\n break;\n }\n ev.preventDefault();\n currentFocusIndex = pageFocusables.indexOf(inputRef.current!) || 0;\n setCalendarOpen(false);\n if (ev.shiftKey) {\n if (currentFocusIndex === 0) {\n requestAnimationFrame(() =>\n pageFocusables[pageFocusables.length - 1].focus(),\n );\n } else {\n requestAnimationFrame(() =>\n pageFocusables[currentFocusIndex - 1].focus(),\n );\n }\n break;\n }\n if (pageFocusables.length > currentFocusIndex + 1) {\n requestAnimationFrame(() =>\n pageFocusables[currentFocusIndex + 1].focus(),\n );\n } else {\n requestAnimationFrame(() => pageFocusables[0].focus());\n }\n break;\n case \"ArrowDown\":\n if (!calendarOpen) {\n setCalendarOpen(true);\n setTimeout(focusToCalendar, 200);\n } else {\n focusToCalendar();\n }\n }\n };\n\n return {\n calendarOpen,\n setCalendarOpen,\n handleCalendarKeyDown,\n handleInputKeyDown,\n };\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
function useFocusWithin({
|
|
4
|
+
onBlur,
|
|
5
|
+
onFocus
|
|
6
|
+
} = {}) {
|
|
7
|
+
const isFocusedRef = useRef(false);
|
|
8
|
+
const handleFocus = (event) => {
|
|
9
|
+
if (!isFocusedRef.current) {
|
|
10
|
+
isFocusedRef.current = true;
|
|
11
|
+
onFocus?.(event);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const handleBlur = (event) => {
|
|
15
|
+
if (isFocusedRef.current && !containsRelatedTarget(event)) {
|
|
16
|
+
isFocusedRef.current = false;
|
|
17
|
+
onBlur?.(event);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
props: { onFocus: handleFocus, onBlur: handleBlur }
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function containsRelatedTarget(event) {
|
|
25
|
+
if (event.currentTarget instanceof HTMLElement && event.relatedTarget instanceof HTMLElement) {
|
|
26
|
+
return event.currentTarget.contains(event.relatedTarget);
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { useFocusWithin as u };
|
|
32
|
+
//# sourceMappingURL=useFocusWithin-BhhgRXdZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFocusWithin-BhhgRXdZ.js","sources":["../src/internal/hooks/useFocusWithin/useFocusWithin.ts"],"sourcesContent":["import { FocusEventHandler, useRef } from \"react\";\nexport interface UseFocusWithinOptions {\n onFocus?: (event: React.FocusEvent) => void;\n onBlur?: (event: React.FocusEvent) => void;\n}\n\nexport interface UseFocusWithinReturnValue<\n T extends HTMLElement = HTMLElement,\n> {\n props: {\n onFocus: FocusEventHandler<T>;\n onBlur: FocusEventHandler<T>;\n };\n}\n\n/**\n * Custom hook for tracking focus state within a DOM element.\n *\n * @param options Configuration options for the hook\n * @param options.onFocus Optional callback when focus enters the element\n * @param options.onBlur Optional callback when focus leaves the element\n * @returns Object containing props for the element\n *\n * @example\n * const { props } = useFocusWithin({\n * onFocus: () => console.log('Focus entered'),\n * onBlur: () => console.log('Focus left')\n * });\n *\n * return <div {...props}>Content</div>;\n */\nexport function useFocusWithin<T extends HTMLElement = any>({\n onBlur,\n onFocus,\n}: UseFocusWithinOptions = {}): UseFocusWithinReturnValue<T> {\n const isFocusedRef = useRef(false);\n\n const handleFocus: FocusEventHandler<T> = (event) => {\n if (!isFocusedRef.current) {\n isFocusedRef.current = true;\n onFocus?.(event);\n }\n };\n\n const handleBlur: FocusEventHandler<T> = (event) => {\n if (isFocusedRef.current && !containsRelatedTarget(event)) {\n isFocusedRef.current = false;\n onBlur?.(event);\n }\n };\n\n return {\n props: { onFocus: handleFocus, onBlur: handleBlur },\n };\n}\n\nfunction containsRelatedTarget(event: React.FocusEvent) {\n if (\n event.currentTarget instanceof HTMLElement &&\n event.relatedTarget instanceof HTMLElement\n ) {\n return event.currentTarget.contains(event.relatedTarget);\n }\n\n return false;\n}\n"],"names":[],"mappings":";;AA+BO,SAAS,cAA4C,CAAA;AAAA,EAC1D,MAAA;AAAA,EACA;AACF,CAAA,GAA2B,EAAkC,EAAA;AAC3D,EAAM,MAAA,YAAA,GAAe,OAAO,KAAK,CAAA;AAEjC,EAAM,MAAA,WAAA,GAAoC,CAAC,KAAU,KAAA;AACnD,IAAI,IAAA,CAAC,aAAa,OAAS,EAAA;AACzB,MAAA,YAAA,CAAa,OAAU,GAAA,IAAA;AACvB,MAAA,OAAA,GAAU,KAAK,CAAA;AAAA;AACjB,GACF;AAEA,EAAM,MAAA,UAAA,GAAmC,CAAC,KAAU,KAAA;AAClD,IAAA,IAAI,YAAa,CAAA,OAAA,IAAW,CAAC,qBAAA,CAAsB,KAAK,CAAG,EAAA;AACzD,MAAA,YAAA,CAAa,OAAU,GAAA,KAAA;AACvB,MAAA,MAAA,GAAS,KAAK,CAAA;AAAA;AAChB,GACF;AAEA,EAAO,OAAA;AAAA,IACL,KAAO,EAAA,EAAE,OAAS,EAAA,WAAA,EAAa,QAAQ,UAAW;AAAA,GACpD;AACF;AAEA,SAAS,sBAAsB,KAAyB,EAAA;AACtD,EAAA,IACE,KAAM,CAAA,aAAA,YAAyB,WAC/B,IAAA,KAAA,CAAM,yBAAyB,WAC/B,EAAA;AACA,IAAA,OAAO,KAAM,CAAA,aAAA,CAAc,QAAS,CAAA,KAAA,CAAM,aAAa,CAAA;AAAA;AAGzD,EAAO,OAAA,KAAA;AACT;;;;"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { useState, useDeferredValue, useRef, useEffect, useLayoutEffect, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
const SCROLL_THRESHOLD = 0.64;
|
|
4
|
+
function useInfiniteCombobox({
|
|
5
|
+
query,
|
|
6
|
+
queryInitialItems,
|
|
7
|
+
initialItems = [],
|
|
8
|
+
initialPage = 0,
|
|
9
|
+
initialLoading = false,
|
|
10
|
+
initialInputValue,
|
|
11
|
+
defaultInputValue,
|
|
12
|
+
initialSelectedItem,
|
|
13
|
+
defaultSelectedItem,
|
|
14
|
+
initialSelectedItems,
|
|
15
|
+
defaultSelectedItems,
|
|
16
|
+
updateOnInputValueChange = true,
|
|
17
|
+
updateOnSelectedItemChange = true,
|
|
18
|
+
updateOnSelectedItemsChange = true,
|
|
19
|
+
queryOnFirstRender = false,
|
|
20
|
+
shouldTriggerQuery: shouldTriggerQueryProp
|
|
21
|
+
}) {
|
|
22
|
+
const [items, setItems] = useState(initialItems);
|
|
23
|
+
const deferredItems = useDeferredValue(items);
|
|
24
|
+
const [page, setPage] = useState(initialPage);
|
|
25
|
+
const [loading, setLoading] = useState(initialLoading);
|
|
26
|
+
const deferredLoading = useDeferredValue(loading);
|
|
27
|
+
const [inputValue, setInputValue] = useState(
|
|
28
|
+
initialInputValue ?? defaultInputValue ?? ""
|
|
29
|
+
);
|
|
30
|
+
const [selectedItem, setSelectedItem] = useState(
|
|
31
|
+
initialSelectedItem ?? defaultSelectedItem ?? null
|
|
32
|
+
);
|
|
33
|
+
const [selectedItems, setSelectedItems] = useState(
|
|
34
|
+
initialSelectedItems ?? defaultSelectedItems ?? []
|
|
35
|
+
);
|
|
36
|
+
const scrollerRef = useRef(null);
|
|
37
|
+
const defaultShouldTriggerQuery = (element) => {
|
|
38
|
+
const totalScrollHeight = element.scrollHeight - element.clientHeight;
|
|
39
|
+
return element.scrollTop / totalScrollHeight > SCROLL_THRESHOLD;
|
|
40
|
+
};
|
|
41
|
+
const [triggerQueryCount, setTriggerQueryCount] = useState(0);
|
|
42
|
+
const triggerQueryCountRef = useRef(0);
|
|
43
|
+
const triggerQuery = () => {
|
|
44
|
+
setTriggerQueryCount((x) => x + 1);
|
|
45
|
+
triggerQueryCountRef.current += 1;
|
|
46
|
+
};
|
|
47
|
+
const [done, setDone] = useState(false);
|
|
48
|
+
const firstRenderRef = useRef(true);
|
|
49
|
+
const scrollTimeoutRef = useRef(false);
|
|
50
|
+
const loadingRef = useRef(false);
|
|
51
|
+
const setLoadingStateAndRef = (isLoading) => {
|
|
52
|
+
setLoading(isLoading);
|
|
53
|
+
loadingRef.current = isLoading;
|
|
54
|
+
};
|
|
55
|
+
const queryRetryCountRef = useRef(0);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (firstRenderRef.current && queryInitialItems) {
|
|
58
|
+
setItems(queryInitialItems());
|
|
59
|
+
}
|
|
60
|
+
if (firstRenderRef.current && !queryOnFirstRender) {
|
|
61
|
+
firstRenderRef.current = false;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
setLoadingStateAndRef(true);
|
|
65
|
+
const timeoutID = setTimeout(() => {
|
|
66
|
+
if (triggerQueryCount < triggerQueryCountRef.current) return;
|
|
67
|
+
query({
|
|
68
|
+
page,
|
|
69
|
+
inputValue,
|
|
70
|
+
selectedItem,
|
|
71
|
+
selectedItems
|
|
72
|
+
}).then((res) => {
|
|
73
|
+
queryRetryCountRef.current = 0;
|
|
74
|
+
if (triggerQueryCount < triggerQueryCountRef.current) return;
|
|
75
|
+
setLoadingStateAndRef(false);
|
|
76
|
+
setPage((x) => x + 1);
|
|
77
|
+
if (res == null || page > 0 && res.length === 0) {
|
|
78
|
+
setDone(true);
|
|
79
|
+
} else if (page === 0) {
|
|
80
|
+
setItems(() => [...res]);
|
|
81
|
+
} else {
|
|
82
|
+
setItems((prevItems) => [...prevItems, ...res]);
|
|
83
|
+
}
|
|
84
|
+
}).catch(() => {
|
|
85
|
+
queryRetryCountRef.current += 1;
|
|
86
|
+
if (queryRetryCountRef.current > 7) {
|
|
87
|
+
queryRetryCountRef.current = 0;
|
|
88
|
+
return;
|
|
89
|
+
} else {
|
|
90
|
+
setTimeout(
|
|
91
|
+
() => {
|
|
92
|
+
triggerQuery();
|
|
93
|
+
},
|
|
94
|
+
2 ** queryRetryCountRef.current * 160
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}, 160);
|
|
99
|
+
return () => {
|
|
100
|
+
clearTimeout(timeoutID);
|
|
101
|
+
};
|
|
102
|
+
}, [triggerQueryCount]);
|
|
103
|
+
useLayoutEffect(() => {
|
|
104
|
+
setTimeout(() => {
|
|
105
|
+
if (scrollerRef.current && scrollerRef.current.scrollHeight > 0 && scrollerRef.current.clientHeight > 0 && scrollerRef.current.scrollHeight <= scrollerRef.current.clientHeight) {
|
|
106
|
+
triggerQuery();
|
|
107
|
+
}
|
|
108
|
+
}, 160);
|
|
109
|
+
}, [items]);
|
|
110
|
+
const isEmpty = items.length === 0 && page === 0;
|
|
111
|
+
const onStateChange = useCallback(
|
|
112
|
+
(changes) => {
|
|
113
|
+
if (isEmpty && "isOpen" in changes && changes.isOpen === true) {
|
|
114
|
+
triggerQuery();
|
|
115
|
+
}
|
|
116
|
+
const inputValueShouldUpdate = updateOnInputValueChange && "inputValue" in changes;
|
|
117
|
+
const selectedItemsShouldUpdate = updateOnSelectedItemsChange && "selectedItems" in changes && changes.selectedItems;
|
|
118
|
+
const selectedItemShouldUpdate = updateOnSelectedItemChange && "selectedItem" in changes && changes.selectedItem !== void 0;
|
|
119
|
+
if (inputValueShouldUpdate) {
|
|
120
|
+
setInputValue(changes.inputValue ?? "");
|
|
121
|
+
}
|
|
122
|
+
if (selectedItemsShouldUpdate) {
|
|
123
|
+
setSelectedItems(changes.selectedItems ?? []);
|
|
124
|
+
}
|
|
125
|
+
if (selectedItemShouldUpdate) {
|
|
126
|
+
setSelectedItem(changes.selectedItem);
|
|
127
|
+
}
|
|
128
|
+
if (inputValueShouldUpdate || selectedItemsShouldUpdate || selectedItemShouldUpdate) {
|
|
129
|
+
setPage(0);
|
|
130
|
+
setDone(false);
|
|
131
|
+
triggerQuery();
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
[
|
|
135
|
+
isEmpty,
|
|
136
|
+
updateOnInputValueChange,
|
|
137
|
+
updateOnSelectedItemsChange,
|
|
138
|
+
updateOnSelectedItemChange
|
|
139
|
+
]
|
|
140
|
+
);
|
|
141
|
+
const onScroll = useCallback(
|
|
142
|
+
(e) => {
|
|
143
|
+
const element = e.currentTarget;
|
|
144
|
+
if (!scrollTimeoutRef.current && !loadingRef.current) {
|
|
145
|
+
setTimeout(() => {
|
|
146
|
+
const shouldTriggerQuery = shouldTriggerQueryProp ?? defaultShouldTriggerQuery;
|
|
147
|
+
if (shouldTriggerQuery(element) && !done) {
|
|
148
|
+
triggerQuery();
|
|
149
|
+
}
|
|
150
|
+
scrollTimeoutRef.current = false;
|
|
151
|
+
}, 40);
|
|
152
|
+
scrollTimeoutRef.current = true;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
[done, shouldTriggerQueryProp]
|
|
156
|
+
);
|
|
157
|
+
return {
|
|
158
|
+
comboboxProps: {
|
|
159
|
+
items: deferredItems,
|
|
160
|
+
loading: deferredLoading,
|
|
161
|
+
initialInputValue,
|
|
162
|
+
defaultInputValue,
|
|
163
|
+
initialSelectedItem,
|
|
164
|
+
defaultSelectedItem,
|
|
165
|
+
initialSelectedItems,
|
|
166
|
+
defaultSelectedItems,
|
|
167
|
+
disableFilter: true,
|
|
168
|
+
// assume the query will return a filtered/sorted list
|
|
169
|
+
onStateChange
|
|
170
|
+
},
|
|
171
|
+
contentProps: {
|
|
172
|
+
onScroll,
|
|
173
|
+
scrollerRef
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export { useInfiniteCombobox as u };
|
|
179
|
+
//# sourceMappingURL=useInfiniteCombobox-WcRgC9p6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInfiniteCombobox-WcRgC9p6.js","sources":["../src/components/Combobox/useInfiniteCombobox.ts"],"sourcesContent":["import {\n useState,\n useEffect,\n useLayoutEffect,\n useRef,\n useDeferredValue,\n type UIEvent,\n useCallback,\n} from \"react\";\nimport { UseInfiniteComboboxProps, type ComboboxProps } from \"./ComboboxTypes\";\n\nconst SCROLL_THRESHOLD = 0.64; // arbitrary number\n\n/**\n * Hook for implementing infinite scroll functionality in combobox components.\n *\n * Features:\n * - Automatic pagination with scroll-based loading\n * - Configurable query triggers (input change, selection change, scroll)\n * - Debounced query execution to prevent excessive API calls\n * - Automatic retry logic with exponential backoff\n * - Support for both single and multiple selection modes\n * - Customizable scroll threshold for triggering new queries\n * - Loading state management with deferred updates\n * - Automatic detection of pagination end\n * - Support for initial items or query-based initialization\n *\n * @param options Configuration options for the infinite combobox\n * @returns Object containing comboboxProps and contentProps for use with Combobox component\n *\n * @example\n * const { comboboxProps, contentProps } = useInfiniteCombobox({\n * initialItems: [],\n * initialPage: 0,\n * initialLoading: true,\n * query: async ({ page, inputValue, selectedItems }) => {\n * return await fetchItems({\n * page,\n * pageSize: 25,\n * searchTerm: inputValue,\n * excludeIds: selectedItems.map(item => item.id),\n * });\n * },\n * });\n *\n * return (\n * <Combobox {...comboboxProps}>\n * <Combobox.SearchField label=\"Search items\" />\n * <Combobox.Content {...contentProps}>\n * {({ items }) => (\n * <Combobox.List>\n * {items.map((item, i) => (\n * <Combobox.Item key={item.id} item={item} index={i}>\n * {item.name}\n * </Combobox.Item>\n * ))}\n * </Combobox.List>\n * )}\n * </Combobox.Content>\n * </Combobox>\n * );\n */\nexport function useInfiniteCombobox<Item = any>({\n query,\n queryInitialItems,\n initialItems = [],\n initialPage = 0,\n initialLoading = false,\n initialInputValue,\n defaultInputValue,\n initialSelectedItem,\n defaultSelectedItem,\n initialSelectedItems,\n defaultSelectedItems,\n updateOnInputValueChange = true,\n updateOnSelectedItemChange = true,\n updateOnSelectedItemsChange = true,\n queryOnFirstRender = false,\n shouldTriggerQuery: shouldTriggerQueryProp,\n}: UseInfiniteComboboxProps<Item>) {\n const [items, setItems] = useState(initialItems);\n const deferredItems = useDeferredValue(items);\n const [page, setPage] = useState(initialPage);\n const [loading, setLoading] = useState(initialLoading);\n const deferredLoading = useDeferredValue(loading);\n const [inputValue, setInputValue] = useState(\n initialInputValue ?? defaultInputValue ?? \"\",\n );\n const [selectedItem, setSelectedItem] = useState<Item | null>(\n initialSelectedItem ?? defaultSelectedItem ?? null,\n );\n const [selectedItems, setSelectedItems] = useState<Item[]>(\n initialSelectedItems ?? defaultSelectedItems ?? [],\n );\n\n const scrollerRef = useRef<HTMLDivElement>(null);\n\n const defaultShouldTriggerQuery = (element: EventTarget & HTMLElement) => {\n const totalScrollHeight = element.scrollHeight - element.clientHeight;\n return element.scrollTop / totalScrollHeight > SCROLL_THRESHOLD;\n };\n\n // using both state and ref here since the state will be \"behind\" the ref\n // if another query has been triggered while the current one is in flight\n const [triggerQueryCount, setTriggerQueryCount] = useState(0);\n const triggerQueryCountRef = useRef(0);\n const triggerQuery = () => {\n setTriggerQueryCount((x) => x + 1);\n triggerQueryCountRef.current += 1;\n };\n\n const [done, setDone] = useState(false);\n\n const firstRenderRef = useRef(true);\n const scrollTimeoutRef = useRef(false);\n\n // using a similar state/ref idea for managing loading as well, since we\n // need to know immediately if queries are in flight, but state won't be\n // updated in the onScroll handler fast enough\n const loadingRef = useRef(false);\n const setLoadingStateAndRef = (isLoading: boolean) => {\n setLoading(isLoading);\n loadingRef.current = isLoading;\n };\n\n const queryRetryCountRef = useRef(0);\n\n useEffect(() => {\n if (firstRenderRef.current && queryInitialItems) {\n setItems(queryInitialItems());\n }\n\n // don't run on initial render unless we explicitly choose to\n if (firstRenderRef.current && !queryOnFirstRender) {\n firstRenderRef.current = false;\n return;\n }\n\n setLoadingStateAndRef(true);\n\n // using a short debounce delay to batch updates\n const timeoutID = setTimeout(() => {\n // if this query is outdated, don't call it\n if (triggerQueryCount < triggerQueryCountRef.current) return;\n\n query({\n page,\n inputValue,\n selectedItem,\n selectedItems,\n })\n .then((res) => {\n queryRetryCountRef.current = 0;\n\n // if this query is outdated, throw away it's results\n if (triggerQueryCount < triggerQueryCountRef.current) return;\n\n setLoadingStateAndRef(false);\n setPage((x) => x + 1);\n\n // if the result is nullish or empty\n // assume we've reached the end of the pagination\n if (res == null || (page > 0 && res.length === 0)) {\n setDone(true);\n } else if (page === 0) {\n setItems(() => [...res]);\n } else {\n setItems((prevItems) => [...prevItems, ...res]);\n }\n })\n .catch(() => {\n // if we fail, retry up to 8 times with exponential back-off\n queryRetryCountRef.current += 1;\n\n if (queryRetryCountRef.current > 7) {\n queryRetryCountRef.current = 0;\n return;\n } else {\n setTimeout(\n () => {\n triggerQuery();\n },\n 2 ** queryRetryCountRef.current * 160,\n );\n }\n });\n }, 160);\n\n return () => {\n clearTimeout(timeoutID);\n };\n\n // only re-query when we manually call triggerQuery\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [triggerQueryCount]);\n\n // if the popover isn't scrollable, keep firing more queries\n useLayoutEffect(() => {\n setTimeout(() => {\n if (\n scrollerRef.current &&\n scrollerRef.current.scrollHeight > 0 &&\n scrollerRef.current.clientHeight > 0 &&\n scrollerRef.current.scrollHeight <= scrollerRef.current.clientHeight\n ) {\n triggerQuery();\n }\n }, 160);\n }, [items]);\n\n // making this separate variable to satisfy react hooks deps linter\n const isEmpty = items.length === 0 && page === 0;\n\n const onStateChange = useCallback(\n (changes: Parameters<NonNullable<ComboboxProps[\"onStateChange\"]>>[0]) => {\n // if the user interacts and we're empty, fire the first query\n if (isEmpty && \"isOpen\" in changes && changes.isOpen === true) {\n triggerQuery();\n }\n\n // otherwise, if inputValue or selectedItems changes, assume we need an\n // entirely new list back from the query\n const inputValueShouldUpdate =\n updateOnInputValueChange && \"inputValue\" in changes;\n const selectedItemsShouldUpdate =\n updateOnSelectedItemsChange &&\n \"selectedItems\" in changes &&\n changes.selectedItems;\n const selectedItemShouldUpdate =\n updateOnSelectedItemChange &&\n \"selectedItem\" in changes &&\n changes.selectedItem !== undefined;\n\n if (inputValueShouldUpdate) {\n setInputValue(changes.inputValue ?? \"\");\n }\n\n if (selectedItemsShouldUpdate) {\n setSelectedItems(changes.selectedItems ?? []);\n }\n\n if (selectedItemShouldUpdate) {\n setSelectedItem(changes.selectedItem);\n }\n\n if (\n inputValueShouldUpdate ||\n selectedItemsShouldUpdate ||\n selectedItemShouldUpdate\n ) {\n setPage(0);\n setDone(false);\n triggerQuery();\n }\n },\n [\n isEmpty,\n updateOnInputValueChange,\n updateOnSelectedItemsChange,\n updateOnSelectedItemChange,\n ],\n );\n\n const onScroll = useCallback(\n (e: UIEvent<HTMLElement>) => {\n // React events don't persist, so we need to store the element through the timeout\n const element = e.currentTarget;\n\n // using timeoutRef to throttle scroll calls\n // using loadingRef to not make additional queries if a query is already in flight\n if (!scrollTimeoutRef.current && !loadingRef.current) {\n setTimeout(() => {\n const shouldTriggerQuery =\n shouldTriggerQueryProp ?? defaultShouldTriggerQuery;\n\n if (shouldTriggerQuery(element) && !done) {\n triggerQuery();\n }\n\n scrollTimeoutRef.current = false;\n }, 40);\n\n scrollTimeoutRef.current = true;\n }\n },\n [done, shouldTriggerQueryProp],\n );\n\n return {\n comboboxProps: {\n items: deferredItems,\n loading: deferredLoading,\n initialInputValue,\n defaultInputValue,\n initialSelectedItem,\n defaultSelectedItem,\n initialSelectedItems,\n defaultSelectedItems,\n disableFilter: true, // assume the query will return a filtered/sorted list\n onStateChange,\n },\n contentProps: {\n onScroll,\n scrollerRef,\n },\n };\n}\n"],"names":[],"mappings":";;AAWA,MAAM,gBAAmB,GAAA,IAAA;AAmDlB,SAAS,mBAAgC,CAAA;AAAA,EAC9C,KAAA;AAAA,EACA,iBAAA;AAAA,EACA,eAAe,EAAC;AAAA,EAChB,WAAc,GAAA,CAAA;AAAA,EACd,cAAiB,GAAA,KAAA;AAAA,EACjB,iBAAA;AAAA,EACA,iBAAA;AAAA,EACA,mBAAA;AAAA,EACA,mBAAA;AAAA,EACA,oBAAA;AAAA,EACA,oBAAA;AAAA,EACA,wBAA2B,GAAA,IAAA;AAAA,EAC3B,0BAA6B,GAAA,IAAA;AAAA,EAC7B,2BAA8B,GAAA,IAAA;AAAA,EAC9B,kBAAqB,GAAA,KAAA;AAAA,EACrB,kBAAoB,EAAA;AACtB,CAAmC,EAAA;AACjC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,YAAY,CAAA;AAC/C,EAAM,MAAA,aAAA,GAAgB,iBAAiB,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,WAAW,CAAA;AAC5C,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,cAAc,CAAA;AACrD,EAAM,MAAA,eAAA,GAAkB,iBAAiB,OAAO,CAAA;AAChD,EAAM,MAAA,CAAC,UAAY,EAAA,aAAa,CAAI,GAAA,QAAA;AAAA,IAClC,qBAAqB,iBAAqB,IAAA;AAAA,GAC5C;AACA,EAAM,MAAA,CAAC,YAAc,EAAA,eAAe,CAAI,GAAA,QAAA;AAAA,IACtC,uBAAuB,mBAAuB,IAAA;AAAA,GAChD;AACA,EAAM,MAAA,CAAC,aAAe,EAAA,gBAAgB,CAAI,GAAA,QAAA;AAAA,IACxC,oBAAA,IAAwB,wBAAwB;AAAC,GACnD;AAEA,EAAM,MAAA,WAAA,GAAc,OAAuB,IAAI,CAAA;AAE/C,EAAM,MAAA,yBAAA,GAA4B,CAAC,OAAuC,KAAA;AACxE,IAAM,MAAA,iBAAA,GAAoB,OAAQ,CAAA,YAAA,GAAe,OAAQ,CAAA,YAAA;AACzD,IAAO,OAAA,OAAA,CAAQ,YAAY,iBAAoB,GAAA,gBAAA;AAAA,GACjD;AAIA,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAI,SAAS,CAAC,CAAA;AAC5D,EAAM,MAAA,oBAAA,GAAuB,OAAO,CAAC,CAAA;AACrC,EAAA,MAAM,eAAe,MAAM;AACzB,IAAqB,oBAAA,CAAA,CAAC,CAAM,KAAA,CAAA,GAAI,CAAC,CAAA;AACjC,IAAA,oBAAA,CAAqB,OAAW,IAAA,CAAA;AAAA,GAClC;AAEA,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,KAAK,CAAA;AAEtC,EAAM,MAAA,cAAA,GAAiB,OAAO,IAAI,CAAA;AAClC,EAAM,MAAA,gBAAA,GAAmB,OAAO,KAAK,CAAA;AAKrC,EAAM,MAAA,UAAA,GAAa,OAAO,KAAK,CAAA;AAC/B,EAAM,MAAA,qBAAA,GAAwB,CAAC,SAAuB,KAAA;AACpD,IAAA,UAAA,CAAW,SAAS,CAAA;AACpB,IAAA,UAAA,CAAW,OAAU,GAAA,SAAA;AAAA,GACvB;AAEA,EAAM,MAAA,kBAAA,GAAqB,OAAO,CAAC,CAAA;AAEnC,EAAA,SAAA,CAAU,MAAM;AACd,IAAI,IAAA,cAAA,CAAe,WAAW,iBAAmB,EAAA;AAC/C,MAAA,QAAA,CAAS,mBAAmB,CAAA;AAAA;AAI9B,IAAI,IAAA,cAAA,CAAe,OAAW,IAAA,CAAC,kBAAoB,EAAA;AACjD,MAAA,cAAA,CAAe,OAAU,GAAA,KAAA;AACzB,MAAA;AAAA;AAGF,IAAA,qBAAA,CAAsB,IAAI,CAAA;AAG1B,IAAM,MAAA,SAAA,GAAY,WAAW,MAAM;AAEjC,MAAI,IAAA,iBAAA,GAAoB,qBAAqB,OAAS,EAAA;AAEtD,MAAM,KAAA,CAAA;AAAA,QACJ,IAAA;AAAA,QACA,UAAA;AAAA,QACA,YAAA;AAAA,QACA;AAAA,OACD,CAAA,CACE,IAAK,CAAA,CAAC,GAAQ,KAAA;AACb,QAAA,kBAAA,CAAmB,OAAU,GAAA,CAAA;AAG7B,QAAI,IAAA,iBAAA,GAAoB,qBAAqB,OAAS,EAAA;AAEtD,QAAA,qBAAA,CAAsB,KAAK,CAAA;AAC3B,QAAQ,OAAA,CAAA,CAAC,CAAM,KAAA,CAAA,GAAI,CAAC,CAAA;AAIpB,QAAA,IAAI,OAAO,IAAS,IAAA,IAAA,GAAO,CAAK,IAAA,GAAA,CAAI,WAAW,CAAI,EAAA;AACjD,UAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,SACd,MAAA,IAAW,SAAS,CAAG,EAAA;AACrB,UAAA,QAAA,CAAS,MAAM,CAAC,GAAG,GAAG,CAAC,CAAA;AAAA,SAClB,MAAA;AACL,UAAA,QAAA,CAAS,CAAC,SAAc,KAAA,CAAC,GAAG,SAAW,EAAA,GAAG,GAAG,CAAC,CAAA;AAAA;AAChD,OACD,CACA,CAAA,KAAA,CAAM,MAAM;AAEX,QAAA,kBAAA,CAAmB,OAAW,IAAA,CAAA;AAE9B,QAAI,IAAA,kBAAA,CAAmB,UAAU,CAAG,EAAA;AAClC,UAAA,kBAAA,CAAmB,OAAU,GAAA,CAAA;AAC7B,UAAA;AAAA,SACK,MAAA;AACL,UAAA,UAAA;AAAA,YACE,MAAM;AACJ,cAAa,YAAA,EAAA;AAAA,aACf;AAAA,YACA,CAAA,IAAK,mBAAmB,OAAU,GAAA;AAAA,WACpC;AAAA;AACF,OACD,CAAA;AAAA,OACF,GAAG,CAAA;AAEN,IAAA,OAAO,MAAM;AACX,MAAA,YAAA,CAAa,SAAS,CAAA;AAAA,KACxB;AAAA,GAIF,EAAG,CAAC,iBAAiB,CAAC,CAAA;AAGtB,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,UAAA,CAAW,MAAM;AACf,MAAA,IACE,WAAY,CAAA,OAAA,IACZ,WAAY,CAAA,OAAA,CAAQ,eAAe,CACnC,IAAA,WAAA,CAAY,OAAQ,CAAA,YAAA,GAAe,KACnC,WAAY,CAAA,OAAA,CAAQ,YAAgB,IAAA,WAAA,CAAY,QAAQ,YACxD,EAAA;AACA,QAAa,YAAA,EAAA;AAAA;AACf,OACC,GAAG,CAAA;AAAA,GACR,EAAG,CAAC,KAAK,CAAC,CAAA;AAGV,EAAA,MAAM,OAAU,GAAA,KAAA,CAAM,MAAW,KAAA,CAAA,IAAK,IAAS,KAAA,CAAA;AAE/C,EAAA,MAAM,aAAgB,GAAA,WAAA;AAAA,IACpB,CAAC,OAAwE,KAAA;AAEvE,MAAA,IAAI,OAAW,IAAA,QAAA,IAAY,OAAW,IAAA,OAAA,CAAQ,WAAW,IAAM,EAAA;AAC7D,QAAa,YAAA,EAAA;AAAA;AAKf,MAAM,MAAA,sBAAA,GACJ,4BAA4B,YAAgB,IAAA,OAAA;AAC9C,MAAA,MAAM,yBACJ,GAAA,2BAAA,IACA,eAAmB,IAAA,OAAA,IACnB,OAAQ,CAAA,aAAA;AACV,MAAA,MAAM,wBACJ,GAAA,0BAAA,IACA,cAAkB,IAAA,OAAA,IAClB,QAAQ,YAAiB,KAAA,MAAA;AAE3B,MAAA,IAAI,sBAAwB,EAAA;AAC1B,QAAc,aAAA,CAAA,OAAA,CAAQ,cAAc,EAAE,CAAA;AAAA;AAGxC,MAAA,IAAI,yBAA2B,EAAA;AAC7B,QAAiB,gBAAA,CAAA,OAAA,CAAQ,aAAiB,IAAA,EAAE,CAAA;AAAA;AAG9C,MAAA,IAAI,wBAA0B,EAAA;AAC5B,QAAA,eAAA,CAAgB,QAAQ,YAAY,CAAA;AAAA;AAGtC,MACE,IAAA,sBAAA,IACA,6BACA,wBACA,EAAA;AACA,QAAA,OAAA,CAAQ,CAAC,CAAA;AACT,QAAA,OAAA,CAAQ,KAAK,CAAA;AACb,QAAa,YAAA,EAAA;AAAA;AACf,KACF;AAAA,IACA;AAAA,MACE,OAAA;AAAA,MACA,wBAAA;AAAA,MACA,2BAAA;AAAA,MACA;AAAA;AACF,GACF;AAEA,EAAA,MAAM,QAAW,GAAA,WAAA;AAAA,IACf,CAAC,CAA4B,KAAA;AAE3B,MAAA,MAAM,UAAU,CAAE,CAAA,aAAA;AAIlB,MAAA,IAAI,CAAC,gBAAA,CAAiB,OAAW,IAAA,CAAC,WAAW,OAAS,EAAA;AACpD,QAAA,UAAA,CAAW,MAAM;AACf,UAAA,MAAM,qBACJ,sBAA0B,IAAA,yBAAA;AAE5B,UAAA,IAAI,kBAAmB,CAAA,OAAO,CAAK,IAAA,CAAC,IAAM,EAAA;AACxC,YAAa,YAAA,EAAA;AAAA;AAGf,UAAA,gBAAA,CAAiB,OAAU,GAAA,KAAA;AAAA,WAC1B,EAAE,CAAA;AAEL,QAAA,gBAAA,CAAiB,OAAU,GAAA,IAAA;AAAA;AAC7B,KACF;AAAA,IACA,CAAC,MAAM,sBAAsB;AAAA,GAC/B;AAEA,EAAO,OAAA;AAAA,IACL,aAAe,EAAA;AAAA,MACb,KAAO,EAAA,aAAA;AAAA,MACP,OAAS,EAAA,eAAA;AAAA,MACT,iBAAA;AAAA,MACA,iBAAA;AAAA,MACA,mBAAA;AAAA,MACA,mBAAA;AAAA,MACA,oBAAA;AAAA,MACA,oBAAA;AAAA,MACA,aAAe,EAAA,IAAA;AAAA;AAAA,MACf;AAAA,KACF;AAAA,IACA,YAAc,EAAA;AAAA,MACZ,QAAA;AAAA,MACA;AAAA;AACF,GACF;AACF;;;;"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useState, useRef, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
function useIntersectionObserver({
|
|
4
|
+
threshold = 0,
|
|
5
|
+
root = null,
|
|
6
|
+
rootMargin = "0%",
|
|
7
|
+
freezeOnceVisible = false,
|
|
8
|
+
initialIsIntersecting = false,
|
|
9
|
+
onChange
|
|
10
|
+
} = {}) {
|
|
11
|
+
const [ref, setRef] = useState(null);
|
|
12
|
+
const [state, setState] = useState(() => ({
|
|
13
|
+
isIntersecting: initialIsIntersecting,
|
|
14
|
+
entry: void 0
|
|
15
|
+
}));
|
|
16
|
+
const callbackRef = useRef();
|
|
17
|
+
callbackRef.current = onChange;
|
|
18
|
+
const frozen = state.entry?.isIntersecting && freezeOnceVisible;
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!ref) return;
|
|
21
|
+
if (!("IntersectionObserver" in window)) return;
|
|
22
|
+
if (frozen) return;
|
|
23
|
+
const observer = new IntersectionObserver(
|
|
24
|
+
(entries) => {
|
|
25
|
+
const thresholds = Array.isArray(observer.thresholds) ? observer.thresholds : [observer.thresholds];
|
|
26
|
+
entries.forEach((entry) => {
|
|
27
|
+
const isIntersecting = entry.isIntersecting && thresholds.some(
|
|
28
|
+
(threshold2) => entry.intersectionRatio >= threshold2
|
|
29
|
+
);
|
|
30
|
+
setState({ isIntersecting, entry });
|
|
31
|
+
if (callbackRef.current) {
|
|
32
|
+
callbackRef.current(isIntersecting, entry);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
{ threshold, root, rootMargin }
|
|
37
|
+
);
|
|
38
|
+
observer.observe(ref);
|
|
39
|
+
return () => {
|
|
40
|
+
observer.disconnect();
|
|
41
|
+
};
|
|
42
|
+
}, [
|
|
43
|
+
ref,
|
|
44
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
45
|
+
JSON.stringify(threshold),
|
|
46
|
+
root,
|
|
47
|
+
rootMargin,
|
|
48
|
+
frozen,
|
|
49
|
+
freezeOnceVisible
|
|
50
|
+
]);
|
|
51
|
+
const prevRef = useRef(null);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!ref && state.entry?.target && !freezeOnceVisible && !frozen && prevRef.current !== state.entry.target) {
|
|
54
|
+
prevRef.current = state.entry.target;
|
|
55
|
+
setState({ isIntersecting: initialIsIntersecting, entry: void 0 });
|
|
56
|
+
}
|
|
57
|
+
}, [ref, state.entry, freezeOnceVisible, frozen, initialIsIntersecting]);
|
|
58
|
+
const result = [
|
|
59
|
+
setRef,
|
|
60
|
+
!!state.isIntersecting,
|
|
61
|
+
state.entry
|
|
62
|
+
];
|
|
63
|
+
result.ref = result[0];
|
|
64
|
+
result.isIntersecting = result[1];
|
|
65
|
+
result.entry = result[2];
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { useIntersectionObserver as u };
|
|
70
|
+
//# sourceMappingURL=useIntersectionObserver-BEmMDO3P.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIntersectionObserver-BEmMDO3P.js","sources":["../src/internal/hooks/useIntersectionObserver/useIntersectionObserver.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\n\n/**\n * Internal state type for intersection observer\n * @property {boolean} isIntersecting - Whether the element is intersecting\n * @property {IntersectionObserverEntry} [entry] - The intersection observer entry\n */\ntype State = {\n /**\n * Whether the element is intersecting\n */\n isIntersecting: boolean;\n /**\n * The intersection observer entry\n */\n entry?: IntersectionObserverEntry;\n};\n\n/**\n * Options for the useIntersectionObserver hook\n * @property {Element | Document | null} [root] - The root element for intersection calculations\n * @property {string} [rootMargin] - Margin around the root element\n * @property {number | number[]} [threshold] - Threshold values for intersection detection\n * @property {boolean} [freezeOnceVisible] - Whether to freeze once the element becomes visible\n * @property {(isIntersecting: boolean, entry: IntersectionObserverEntry) => void} [onChange] - Callback when intersection changes\n * @property {boolean} [initialIsIntersecting] - Initial intersection state\n */\nexport type UseIntersectionObserverOptions = {\n /**\n * The root element for intersection calculations\n */\n root?: Element | Document | null;\n /**\n * Margin around the root element\n */\n rootMargin?: string;\n /**\n * Threshold values for intersection detection\n */\n threshold?: number | number[];\n /**\n * Whether to freeze once the element becomes visible\n */\n freezeOnceVisible?: boolean;\n /**\n * Callback when intersection changes\n */\n onChange?: (\n isIntersecting: boolean,\n entry: IntersectionObserverEntry,\n ) => void;\n /**\n * Initial intersection state\n */\n initialIsIntersecting?: boolean;\n};\n\n/**\n * Return type for the useIntersectionObserver hook\n * @property {(node?: Element | null) => void} ref - Function to set the element to observe\n * @property {boolean} isIntersecting - Whether the element is intersecting\n * @property {IntersectionObserverEntry} [entry] - The intersection observer entry\n */\nexport type IntersectionReturn = [\n (node?: Element | null) => void,\n boolean,\n IntersectionObserverEntry | undefined,\n] & {\n ref: (node?: Element | null) => void;\n isIntersecting: boolean;\n entry?: IntersectionObserverEntry;\n};\n\n/**\n * Custom hook for observing element intersection with viewport or root element.\n *\n * Features:\n * - Observes element intersection with viewport or custom root element\n * - Supports configurable threshold values and root margins\n * - Provides intersection state and detailed entry information\n * - Supports freezing observation once element becomes visible\n * - Handles browser compatibility gracefully\n * - Provides both array and object destructuring support\n * - Automatically cleans up observer on unmount\n * - Supports onChange callback for custom intersection handling\n *\n * @param options - Configuration options for the intersection observer\n * @returns Array containing ref function, intersection state, and entry, with additional object properties\n */\nexport function useIntersectionObserver({\n threshold = 0,\n root = null,\n rootMargin = \"0%\",\n freezeOnceVisible = false,\n initialIsIntersecting = false,\n onChange,\n}: UseIntersectionObserverOptions = {}): IntersectionReturn {\n const [ref, setRef] = useState<Element | null>(null);\n\n const [state, setState] = useState<State>(() => ({\n isIntersecting: initialIsIntersecting,\n entry: undefined,\n }));\n\n const callbackRef = useRef<UseIntersectionObserverOptions[\"onChange\"]>();\n\n callbackRef.current = onChange;\n\n const frozen = state.entry?.isIntersecting && freezeOnceVisible;\n\n useEffect(() => {\n // Ensure we have a ref to observe\n if (!ref) return;\n\n // Ensure the browser supports the Intersection Observer API\n if (!(\"IntersectionObserver\" in window)) return;\n\n // Skip if frozen\n if (frozen) return;\n\n let unobserve: (() => void) | undefined;\n\n const observer = new IntersectionObserver(\n (entries: IntersectionObserverEntry[]): void => {\n const thresholds = Array.isArray(observer.thresholds)\n ? observer.thresholds\n : [observer.thresholds];\n\n entries.forEach((entry) => {\n const isIntersecting =\n entry.isIntersecting &&\n thresholds.some(\n (threshold) => entry.intersectionRatio >= threshold,\n );\n\n setState({ isIntersecting, entry });\n\n if (callbackRef.current) {\n callbackRef.current(isIntersecting, entry);\n }\n\n if (isIntersecting && freezeOnceVisible && unobserve) {\n unobserve();\n unobserve = undefined;\n }\n });\n },\n { threshold, root, rootMargin },\n );\n\n observer.observe(ref);\n\n return () => {\n observer.disconnect();\n };\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n ref,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n JSON.stringify(threshold),\n root,\n rootMargin,\n frozen,\n freezeOnceVisible,\n ]);\n\n // ensures that if the observed element changes, the intersection observer is reinitialized\n const prevRef = useRef<Element | null>(null);\n\n useEffect(() => {\n if (\n !ref &&\n state.entry?.target &&\n !freezeOnceVisible &&\n !frozen &&\n prevRef.current !== state.entry.target\n ) {\n prevRef.current = state.entry.target;\n setState({ isIntersecting: initialIsIntersecting, entry: undefined });\n }\n }, [ref, state.entry, freezeOnceVisible, frozen, initialIsIntersecting]);\n\n const result = [\n setRef,\n !!state.isIntersecting,\n state.entry,\n ] as IntersectionReturn;\n\n // Support object destructuring, by adding the specific values.\n result.ref = result[0];\n result.isIntersecting = result[1];\n result.entry = result[2];\n\n return result;\n}\n"],"names":["threshold"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/anvil2",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.46.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
],
|
|
16
16
|
"exports": {
|
|
17
17
|
".": "./dist/index.js",
|
|
18
|
+
"./beta": "./dist/beta.js",
|
|
18
19
|
"./assets/*": "./dist/assets/*",
|
|
19
20
|
"./token": "./dist/token.js",
|
|
20
21
|
"./token/*": "./dist/token/*",
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
"@react-hook/merged-ref": "^1.3.2",
|
|
33
34
|
"@react-hook/resize-observer": "^2.0.1",
|
|
34
35
|
"@servicetitan/anvil-fonts": ">=14",
|
|
36
|
+
"@tanstack/react-virtual": "^3.13.12",
|
|
35
37
|
"@types/big.js": "^6.2.2",
|
|
36
38
|
"big.js": "^7.0.1",
|
|
37
39
|
"classnames": "^2.5.1",
|