@wordpress/dataviews 13.1.1-next.v.202603102151.0 → 14.0.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 +18 -1
- package/README.md +19 -3
- package/build/components/dataform-controls/datetime.cjs +8 -4
- package/build/components/dataform-controls/datetime.cjs.map +2 -2
- package/build/components/dataform-layouts/card/index.cjs +132 -128
- package/build/components/dataform-layouts/card/index.cjs.map +3 -3
- package/build/components/dataviews-bulk-actions/index.cjs +28 -5
- package/build/components/dataviews-bulk-actions/index.cjs.map +2 -2
- package/build/components/dataviews-context/index.cjs +2 -2
- package/build/components/dataviews-context/index.cjs.map +2 -2
- package/build/components/dataviews-footer/index.cjs +2 -3
- package/build/components/dataviews-footer/index.cjs.map +2 -2
- package/build/components/dataviews-layout/index.cjs +12 -3
- package/build/components/dataviews-layout/index.cjs.map +2 -2
- package/build/components/dataviews-layouts/grid/composite-grid.cjs +378 -245
- package/build/components/dataviews-layouts/grid/composite-grid.cjs.map +2 -2
- package/build/components/dataviews-layouts/index.cjs +3 -3
- package/build/components/dataviews-layouts/index.cjs.map +3 -3
- package/build/components/dataviews-layouts/picker-grid/index.cjs +76 -32
- package/build/components/dataviews-layouts/picker-grid/index.cjs.map +2 -2
- package/build/components/dataviews-layouts/picker-table/index.cjs +34 -22
- package/build/components/dataviews-layouts/picker-table/index.cjs.map +2 -2
- package/build/components/dataviews-layouts/table/index.cjs +97 -88
- package/build/components/dataviews-layouts/table/index.cjs.map +2 -2
- package/build/components/dataviews-layouts/table/{use-is-horizontal-scroll-end.cjs → use-scroll-state.cjs} +29 -33
- package/build/components/dataviews-layouts/table/use-scroll-state.cjs.map +7 -0
- package/build/components/dataviews-layouts/utils/density-picker.cjs.map +2 -2
- package/build/components/dataviews-layouts/utils/grid-config-options.cjs +45 -0
- package/build/components/dataviews-layouts/utils/grid-config-options.cjs.map +7 -0
- package/build/components/dataviews-layouts/utils/use-infinite-scroll.cjs +62 -0
- package/build/components/dataviews-layouts/utils/use-infinite-scroll.cjs.map +7 -0
- package/build/components/dataviews-picker-footer/index.cjs +23 -4
- package/build/components/dataviews-picker-footer/index.cjs.map +2 -2
- package/build/components/dataviews-search/index.cjs +2 -1
- package/build/components/dataviews-search/index.cjs.map +2 -2
- package/build/components/dataviews-selection-checkbox/index.cjs +3 -2
- package/build/components/dataviews-selection-checkbox/index.cjs.map +2 -2
- package/build/components/dataviews-view-config/index.cjs +0 -2
- package/build/components/dataviews-view-config/index.cjs.map +3 -3
- package/build/components/dataviews-view-config/infinite-scroll-toggle.cjs +0 -3
- package/build/components/dataviews-view-config/infinite-scroll-toggle.cjs.map +2 -2
- package/build/dataviews/index.cjs +38 -34
- package/build/dataviews/index.cjs.map +3 -3
- package/build/dataviews-picker/index.cjs +26 -25
- package/build/dataviews-picker/index.cjs.map +3 -3
- package/build/hooks/index.cjs +11 -2
- package/build/hooks/index.cjs.map +2 -2
- package/build/hooks/use-data.cjs +146 -9
- package/build/hooks/use-data.cjs.map +2 -2
- package/build/hooks/use-infinite-scroll.cjs +208 -0
- package/build/hooks/use-infinite-scroll.cjs.map +7 -0
- package/build/hooks/use-selected-items.cjs +57 -0
- package/build/hooks/use-selected-items.cjs.map +7 -0
- package/build/types/dataviews.cjs.map +1 -1
- package/build/types/field-api.cjs.map +1 -1
- package/build/utils/filter-sort-and-paginate.cjs +5 -1
- package/build/utils/filter-sort-and-paginate.cjs.map +2 -2
- package/build/utils/get-footer-message.cjs +8 -8
- package/build/utils/get-footer-message.cjs.map +2 -2
- package/build-module/components/dataform-controls/datetime.mjs +8 -4
- package/build-module/components/dataform-controls/datetime.mjs.map +2 -2
- package/build-module/components/dataform-layouts/card/index.mjs +132 -133
- package/build-module/components/dataform-layouts/card/index.mjs.map +2 -2
- package/build-module/components/dataviews-bulk-actions/index.mjs +28 -5
- package/build-module/components/dataviews-bulk-actions/index.mjs.map +2 -2
- package/build-module/components/dataviews-context/index.mjs +2 -2
- package/build-module/components/dataviews-context/index.mjs.map +2 -2
- package/build-module/components/dataviews-footer/index.mjs +2 -3
- package/build-module/components/dataviews-footer/index.mjs.map +2 -2
- package/build-module/components/dataviews-layout/index.mjs +12 -3
- package/build-module/components/dataviews-layout/index.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/grid/composite-grid.mjs +387 -246
- package/build-module/components/dataviews-layouts/grid/composite-grid.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/index.mjs +3 -3
- package/build-module/components/dataviews-layouts/index.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/picker-grid/index.mjs +80 -33
- package/build-module/components/dataviews-layouts/picker-grid/index.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/picker-table/index.mjs +34 -22
- package/build-module/components/dataviews-layouts/picker-table/index.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/table/index.mjs +97 -88
- package/build-module/components/dataviews-layouts/table/index.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/table/use-scroll-state.mjs +46 -0
- package/build-module/components/dataviews-layouts/table/use-scroll-state.mjs.map +7 -0
- package/build-module/components/dataviews-layouts/utils/density-picker.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/utils/grid-config-options.mjs +14 -0
- package/build-module/components/dataviews-layouts/utils/grid-config-options.mjs.map +7 -0
- package/build-module/components/dataviews-layouts/utils/use-infinite-scroll.mjs +26 -0
- package/build-module/components/dataviews-layouts/utils/use-infinite-scroll.mjs.map +7 -0
- package/build-module/components/dataviews-picker-footer/index.mjs +23 -4
- package/build-module/components/dataviews-picker-footer/index.mjs.map +2 -2
- package/build-module/components/dataviews-search/index.mjs +2 -1
- package/build-module/components/dataviews-search/index.mjs.map +2 -2
- package/build-module/components/dataviews-selection-checkbox/index.mjs +3 -2
- package/build-module/components/dataviews-selection-checkbox/index.mjs.map +2 -2
- package/build-module/components/dataviews-view-config/index.mjs +0 -2
- package/build-module/components/dataviews-view-config/index.mjs.map +2 -2
- package/build-module/components/dataviews-view-config/infinite-scroll-toggle.mjs +0 -3
- package/build-module/components/dataviews-view-config/infinite-scroll-toggle.mjs.map +2 -2
- package/build-module/dataviews/index.mjs +46 -36
- package/build-module/dataviews/index.mjs.map +2 -2
- package/build-module/dataviews-picker/index.mjs +34 -27
- package/build-module/dataviews-picker/index.mjs.map +2 -2
- package/build-module/hooks/index.mjs +7 -1
- package/build-module/hooks/index.mjs.map +2 -2
- package/build-module/hooks/use-data.mjs +147 -10
- package/build-module/hooks/use-data.mjs.map +2 -2
- package/build-module/hooks/use-infinite-scroll.mjs +188 -0
- package/build-module/hooks/use-infinite-scroll.mjs.map +7 -0
- package/build-module/hooks/use-selected-items.mjs +36 -0
- package/build-module/hooks/use-selected-items.mjs.map +7 -0
- package/build-module/utils/filter-sort-and-paginate.mjs +5 -1
- package/build-module/utils/filter-sort-and-paginate.mjs.map +2 -2
- package/build-module/utils/get-footer-message.mjs +8 -8
- package/build-module/utils/get-footer-message.mjs.map +2 -2
- package/build-style/style-rtl.css +107 -41
- package/build-style/style.css +107 -41
- package/build-types/components/dataform-controls/datetime.d.ts +1 -1
- package/build-types/components/dataform-controls/datetime.d.ts.map +1 -1
- package/build-types/components/dataform-layouts/card/index.d.ts.map +1 -1
- package/build-types/components/dataviews-bulk-actions/index.d.ts +2 -1
- package/build-types/components/dataviews-bulk-actions/index.d.ts.map +1 -1
- package/build-types/components/dataviews-context/index.d.ts +1 -1
- package/build-types/components/dataviews-context/index.d.ts.map +1 -1
- package/build-types/components/dataviews-footer/index.d.ts.map +1 -1
- package/build-types/components/dataviews-layout/index.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/grid/composite-grid.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/index.d.ts +3 -3
- package/build-types/components/dataviews-layouts/index.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/picker-grid/index.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/picker-table/index.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/table/index.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/table/use-scroll-state.d.ts +25 -0
- package/build-types/components/dataviews-layouts/table/use-scroll-state.d.ts.map +1 -0
- package/build-types/components/dataviews-layouts/utils/density-picker.d.ts.map +1 -1
- package/build-types/components/dataviews-layouts/utils/grid-config-options.d.ts +2 -0
- package/build-types/components/dataviews-layouts/utils/grid-config-options.d.ts.map +1 -0
- package/build-types/components/dataviews-layouts/utils/use-infinite-scroll.d.ts +22 -0
- package/build-types/components/dataviews-layouts/utils/use-infinite-scroll.d.ts.map +1 -0
- package/build-types/components/dataviews-picker-footer/index.d.ts.map +1 -1
- package/build-types/components/dataviews-search/index.d.ts.map +1 -1
- package/build-types/components/dataviews-selection-checkbox/index.d.ts.map +1 -1
- package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
- package/build-types/components/dataviews-view-config/infinite-scroll-toggle.d.ts +1 -1
- package/build-types/components/dataviews-view-config/infinite-scroll-toggle.d.ts.map +1 -1
- package/build-types/dataviews/index.d.ts +0 -1
- package/build-types/dataviews/index.d.ts.map +1 -1
- package/build-types/dataviews/stories/empty.d.ts +1 -2
- package/build-types/dataviews/stories/empty.d.ts.map +1 -1
- package/build-types/dataviews/stories/fixtures.d.ts.map +1 -1
- package/build-types/dataviews/stories/free-composition.d.ts.map +1 -1
- package/build-types/dataviews/stories/index.story.d.ts +18 -10
- package/build-types/dataviews/stories/index.story.d.ts.map +1 -1
- package/build-types/dataviews/stories/infinite-scroll.d.ts.map +1 -1
- package/build-types/dataviews/stories/layout-activity.d.ts.map +1 -1
- package/build-types/dataviews/stories/layout-custom.d.ts +3 -1
- package/build-types/dataviews/stories/layout-custom.d.ts.map +1 -1
- package/build-types/dataviews/stories/layout-grid.d.ts.map +1 -1
- package/build-types/dataviews/stories/layout-list.d.ts.map +1 -1
- package/build-types/dataviews/stories/layout-table.d.ts.map +1 -1
- package/build-types/dataviews/stories/with-card.d.ts +3 -1
- package/build-types/dataviews/stories/with-card.d.ts.map +1 -1
- package/build-types/dataviews-picker/index.d.ts +0 -1
- package/build-types/dataviews-picker/index.d.ts.map +1 -1
- package/build-types/dataviews-picker/stories/fixtures.d.ts.map +1 -1
- package/build-types/dataviews-picker/stories/index.story.d.ts.map +1 -1
- package/build-types/field-types/stories/index.story.d.ts.map +1 -1
- package/build-types/hooks/index.d.ts +3 -0
- package/build-types/hooks/index.d.ts.map +1 -1
- package/build-types/hooks/test/use-data.d.ts +2 -0
- package/build-types/hooks/test/use-data.d.ts.map +1 -0
- package/build-types/hooks/use-data.d.ts +41 -3
- package/build-types/hooks/use-data.d.ts.map +1 -1
- package/build-types/hooks/use-infinite-scroll.d.ts +21 -0
- package/build-types/hooks/use-infinite-scroll.d.ts.map +1 -0
- package/build-types/hooks/use-selected-items.d.ts +19 -0
- package/build-types/hooks/use-selected-items.d.ts.map +1 -0
- package/build-types/types/dataviews.d.ts +15 -1
- package/build-types/types/dataviews.d.ts.map +1 -1
- package/build-types/types/field-api.d.ts +15 -4
- package/build-types/types/field-api.d.ts.map +1 -1
- package/build-types/utils/filter-sort-and-paginate.d.ts.map +1 -1
- package/build-types/utils/get-footer-message.d.ts +3 -2
- package/build-types/utils/get-footer-message.d.ts.map +1 -1
- package/build-wp/index.js +3202 -2761
- package/package.json +19 -19
- package/src/components/dataform-controls/datetime.tsx +19 -11
- package/src/components/dataform-layouts/card/index.tsx +171 -146
- package/src/components/dataform-layouts/card/style.scss +8 -5
- package/src/components/dataviews-bulk-actions/index.tsx +28 -1
- package/src/components/dataviews-context/index.ts +2 -2
- package/src/components/dataviews-footer/index.tsx +1 -6
- package/src/components/dataviews-layout/index.tsx +41 -19
- package/src/components/dataviews-layout/style.scss +8 -0
- package/src/components/dataviews-layouts/grid/composite-grid.tsx +433 -278
- package/src/components/dataviews-layouts/grid/style.scss +22 -2
- package/src/components/dataviews-layouts/index.ts +3 -3
- package/src/components/dataviews-layouts/picker-grid/index.tsx +70 -17
- package/src/components/dataviews-layouts/picker-grid/style.scss +10 -0
- package/src/components/dataviews-layouts/picker-table/index.tsx +42 -22
- package/src/components/dataviews-layouts/table/index.tsx +10 -4
- package/src/components/dataviews-layouts/table/style.scss +13 -0
- package/src/components/dataviews-layouts/table/use-scroll-state.ts +79 -0
- package/src/components/dataviews-layouts/utils/density-picker.tsx +12 -2
- package/src/components/dataviews-layouts/utils/grid-config-options.tsx +14 -0
- package/src/components/dataviews-layouts/utils/grid-items.scss +9 -1
- package/src/components/dataviews-layouts/utils/use-infinite-scroll.ts +64 -0
- package/src/components/dataviews-picker-footer/index.tsx +21 -1
- package/src/components/dataviews-search/index.tsx +2 -1
- package/src/components/dataviews-selection-checkbox/index.tsx +4 -2
- package/src/components/dataviews-view-config/index.tsx +0 -2
- package/src/components/dataviews-view-config/infinite-scroll-toggle.tsx +0 -5
- package/src/dataviews/index.tsx +58 -45
- package/src/dataviews/stories/empty.tsx +1 -3
- package/src/dataviews/stories/fixtures.tsx +288 -0
- package/src/dataviews/stories/free-composition.tsx +44 -45
- package/src/dataviews/stories/index.story.tsx +31 -8
- package/src/dataviews/stories/infinite-scroll.tsx +7 -93
- package/src/dataviews/stories/layout-activity.tsx +1 -0
- package/src/dataviews/stories/layout-custom.tsx +7 -3
- package/src/dataviews/stories/layout-grid.tsx +1 -0
- package/src/dataviews/stories/layout-list.tsx +1 -0
- package/src/dataviews/stories/layout-table.tsx +1 -0
- package/src/dataviews/stories/style.css +0 -5
- package/src/dataviews/stories/with-card.tsx +35 -24
- package/src/dataviews/style.scss +5 -8
- package/src/dataviews/test/dataviews.tsx +54 -1
- package/src/dataviews-picker/index.tsx +41 -35
- package/src/dataviews-picker/stories/fixtures.tsx +270 -0
- package/src/dataviews-picker/stories/index.story.tsx +62 -129
- package/src/field-types/stories/index.story.tsx +12 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/test/use-data.ts +791 -0
- package/src/hooks/use-data.ts +288 -21
- package/src/hooks/use-infinite-scroll.ts +304 -0
- package/src/hooks/use-selected-items.ts +72 -0
- package/src/style.scss +1 -0
- package/src/types/dataviews.ts +18 -1
- package/src/types/field-api.ts +16 -3
- package/src/utils/filter-sort-and-paginate.ts +13 -1
- package/src/utils/get-footer-message.ts +12 -9
- package/src/utils/test/filter-sort-and-paginate.js +78 -54
- package/build/components/dataviews-layouts/table/use-is-horizontal-scroll-end.cjs.map +0 -7
- package/build-module/components/dataviews-layouts/table/use-is-horizontal-scroll-end.mjs +0 -50
- package/build-module/components/dataviews-layouts/table/use-is-horizontal-scroll-end.mjs.map +0 -7
- package/build-types/components/dataviews-layouts/table/use-is-horizontal-scroll-end.d.ts +0 -19
- package/build-types/components/dataviews-layouts/table/use-is-horizontal-scroll-end.d.ts.map +0 -1
- package/src/components/dataviews-layouts/table/use-is-horizontal-scroll-end.ts +0 -82
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// packages/dataviews/src/dataviews-picker/index.tsx
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import {
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
useState
|
|
9
|
+
} from "@wordpress/element";
|
|
10
|
+
import { useResizeObserver } from "@wordpress/compose";
|
|
4
11
|
import { Stack } from "@wordpress/ui";
|
|
5
12
|
import DataViewsContext from "../components/dataviews-context/index.mjs";
|
|
6
13
|
import { VIEW_LAYOUTS } from "../components/dataviews-layouts/index.mjs";
|
|
@@ -19,6 +26,8 @@ import DataViewsViewConfig, {
|
|
|
19
26
|
ViewTypeMenu
|
|
20
27
|
} from "../components/dataviews-view-config/index.mjs";
|
|
21
28
|
import normalizeFields from "../field-types/index.mjs";
|
|
29
|
+
import useData from "../hooks/use-data.mjs";
|
|
30
|
+
import { useInfiniteScroll } from "../hooks/use-infinite-scroll.mjs";
|
|
22
31
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
23
32
|
var isItemClickable = () => false;
|
|
24
33
|
var dataViewsPickerLayouts = VIEW_LAYOUTS.filter(
|
|
@@ -30,6 +39,8 @@ function DefaultUI({
|
|
|
30
39
|
search = true,
|
|
31
40
|
searchLabel = void 0
|
|
32
41
|
}) {
|
|
42
|
+
const { view } = useContext(DataViewsContext);
|
|
43
|
+
const isInfiniteScroll = view.infiniteScrollEnabled;
|
|
33
44
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
34
45
|
/* @__PURE__ */ jsxs(
|
|
35
46
|
Stack,
|
|
@@ -37,7 +48,9 @@ function DefaultUI({
|
|
|
37
48
|
direction: "row",
|
|
38
49
|
align: "top",
|
|
39
50
|
justify: "space-between",
|
|
40
|
-
className: "dataviews__view-actions",
|
|
51
|
+
className: clsx("dataviews__view-actions", {
|
|
52
|
+
"dataviews__view-actions--infinite-scroll": isInfiniteScroll
|
|
53
|
+
}),
|
|
41
54
|
gap: "xs",
|
|
42
55
|
children: [
|
|
43
56
|
/* @__PURE__ */ jsxs(
|
|
@@ -81,7 +94,13 @@ function DataViewsPicker({
|
|
|
81
94
|
itemListLabel,
|
|
82
95
|
empty
|
|
83
96
|
}) {
|
|
84
|
-
const {
|
|
97
|
+
const { data: displayData, setVisibleEntries } = useData({
|
|
98
|
+
view,
|
|
99
|
+
data,
|
|
100
|
+
getItemId,
|
|
101
|
+
selection,
|
|
102
|
+
paginationInfo
|
|
103
|
+
});
|
|
85
104
|
const containerRef = useRef(null);
|
|
86
105
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
87
106
|
const resizeObserverRef = useResizeObserver(
|
|
@@ -110,31 +129,19 @@ function DataViewsPicker({
|
|
|
110
129
|
const [isShowingFilter, setIsShowingFilter] = useState(
|
|
111
130
|
hasPrimaryOrLockedFilters
|
|
112
131
|
);
|
|
132
|
+
const { intersectionObserver } = useInfiniteScroll({
|
|
133
|
+
view,
|
|
134
|
+
onChangeView,
|
|
135
|
+
isLoading,
|
|
136
|
+
paginationInfo,
|
|
137
|
+
containerRef,
|
|
138
|
+
setVisibleEntries
|
|
139
|
+
});
|
|
113
140
|
useEffect(() => {
|
|
114
141
|
if (hasPrimaryOrLockedFilters && !isShowingFilter) {
|
|
115
142
|
setIsShowingFilter(true);
|
|
116
143
|
}
|
|
117
144
|
}, [hasPrimaryOrLockedFilters, isShowingFilter]);
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
if (!view.infiniteScrollEnabled || !containerRef.current) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const handleScroll = throttle((event) => {
|
|
123
|
-
const target = event.target;
|
|
124
|
-
const scrollTop = target.scrollTop;
|
|
125
|
-
const scrollHeight = target.scrollHeight;
|
|
126
|
-
const clientHeight = target.clientHeight;
|
|
127
|
-
if (scrollTop + clientHeight >= scrollHeight - 100) {
|
|
128
|
-
infiniteScrollHandler?.();
|
|
129
|
-
}
|
|
130
|
-
}, 100);
|
|
131
|
-
const container = containerRef.current;
|
|
132
|
-
container.addEventListener("scroll", handleScroll);
|
|
133
|
-
return () => {
|
|
134
|
-
container.removeEventListener("scroll", handleScroll);
|
|
135
|
-
handleScroll.cancel();
|
|
136
|
-
};
|
|
137
|
-
}, [infiniteScrollHandler, view.infiniteScrollEnabled]);
|
|
138
145
|
const defaultLayouts = useMemo(
|
|
139
146
|
() => Object.fromEntries(
|
|
140
147
|
Object.entries(defaultLayoutsProperty).filter(
|
|
@@ -158,7 +165,7 @@ function DataViewsPicker({
|
|
|
158
165
|
onChangeView,
|
|
159
166
|
fields: _fields,
|
|
160
167
|
actions,
|
|
161
|
-
data,
|
|
168
|
+
data: displayData,
|
|
162
169
|
isLoading,
|
|
163
170
|
paginationInfo,
|
|
164
171
|
isItemClickable,
|
|
@@ -178,9 +185,9 @@ function DataViewsPicker({
|
|
|
178
185
|
itemListLabel,
|
|
179
186
|
empty,
|
|
180
187
|
hasInitiallyLoaded: true,
|
|
181
|
-
|
|
188
|
+
intersectionObserver
|
|
182
189
|
},
|
|
183
|
-
children: /* @__PURE__ */ jsx("div", { className: "dataviews-picker-wrapper",
|
|
190
|
+
children: /* @__PURE__ */ jsx("div", { className: "dataviews-picker-wrapper", children: children ?? /* @__PURE__ */ jsx(DefaultUI, { search, searchLabel }) })
|
|
184
191
|
}
|
|
185
192
|
);
|
|
186
193
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/dataviews-picker/index.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * External dependencies\n */\nimport type { ReactNode } from 'react';\n\n/**\n * WordPress dependencies\n */\nimport {
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport type { ReactNode } from 'react';\nimport clsx from 'clsx';\n\n/**\n * WordPress dependencies\n */\nimport {\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from '@wordpress/element';\nimport { useResizeObserver } from '@wordpress/compose';\nimport { Stack } from '@wordpress/ui';\n\n/**\n * Internal dependencies\n */\nimport DataViewsContext from '../components/dataviews-context';\nimport { VIEW_LAYOUTS } from '../components/dataviews-layouts';\nimport {\n\tFilters,\n\tFiltersToggled,\n\tuseFilters,\n\tFiltersToggle,\n} from '../components/dataviews-filters';\nimport DataViewsLayout from '../components/dataviews-layout';\nimport { DataViewsPickerFooter } from '../components/dataviews-picker-footer';\nimport DataViewsSearch from '../components/dataviews-search';\nimport { DataViewsPagination } from '../components/dataviews-pagination';\nimport DataViewsViewConfig, {\n\tDataviewsViewConfigDropdown,\n\tViewTypeMenu,\n} from '../components/dataviews-view-config';\nimport normalizeFields from '../field-types';\nimport useData from '../hooks/use-data';\nimport { useInfiniteScroll } from '../hooks/use-infinite-scroll';\nimport type { ActionButton, Field, View, SupportedLayouts } from '../types';\nimport type { SelectionOrUpdater } from '../types/private';\ntype ItemWithId = { id: string };\n\nconst isItemClickable = () => false;\n\nconst dataViewsPickerLayouts = VIEW_LAYOUTS.filter(\n\t( viewLayout ) => viewLayout.isPicker\n);\n\ntype DataViewsPickerProps< Item > = {\n\tview: View;\n\tonChangeView: ( view: View ) => void;\n\tfields: Field< Item >[];\n\tactions?: ActionButton< Item >[];\n\tsearch?: boolean;\n\tsearchLabel?: string;\n\tdata: Item[];\n\tisLoading?: boolean;\n\tpaginationInfo: {\n\t\ttotalItems: number;\n\t\ttotalPages: number;\n\t};\n\tdefaultLayouts: SupportedLayouts;\n\tselection: string[];\n\tonChangeSelection: ( items: string[] ) => void;\n\tchildren?: ReactNode;\n\tconfig?: {\n\t\tperPageSizes: number[];\n\t};\n\titemListLabel?: string;\n\tempty?: ReactNode;\n} & ( Item extends ItemWithId\n\t? { getItemId?: ( item: Item ) => string }\n\t: { getItemId: ( item: Item ) => string } );\n\nconst defaultGetItemId = ( item: ItemWithId ) => item.id;\nconst EMPTY_ARRAY: any[] = [];\n\ntype DefaultUIProps = Pick<\n\tDataViewsPickerProps< any >,\n\t'search' | 'searchLabel'\n>;\n\nfunction DefaultUI( {\n\tsearch = true,\n\tsearchLabel = undefined,\n}: DefaultUIProps ) {\n\tconst { view } = useContext( DataViewsContext );\n\tconst isInfiniteScroll = view.infiniteScrollEnabled;\n\treturn (\n\t\t<>\n\t\t\t<Stack\n\t\t\t\tdirection=\"row\"\n\t\t\t\talign=\"top\"\n\t\t\t\tjustify=\"space-between\"\n\t\t\t\tclassName={ clsx( 'dataviews__view-actions', {\n\t\t\t\t\t'dataviews__view-actions--infinite-scroll':\n\t\t\t\t\t\tisInfiniteScroll,\n\t\t\t\t} ) }\n\t\t\t\tgap=\"xs\"\n\t\t\t>\n\t\t\t\t<Stack\n\t\t\t\t\tdirection=\"row\"\n\t\t\t\t\tgap=\"sm\"\n\t\t\t\t\tjustify=\"start\"\n\t\t\t\t\tclassName=\"dataviews__search\"\n\t\t\t\t>\n\t\t\t\t\t{ search && <DataViewsSearch label={ searchLabel } /> }\n\t\t\t\t\t<FiltersToggle />\n\t\t\t\t</Stack>\n\t\t\t\t<Stack direction=\"row\" gap=\"xs\" style={ { flexShrink: 0 } }>\n\t\t\t\t\t<DataViewsViewConfig />\n\t\t\t\t</Stack>\n\t\t\t</Stack>\n\t\t\t<FiltersToggled className=\"dataviews-filters__container\" />\n\t\t\t<DataViewsLayout />\n\t\t\t<DataViewsPickerFooter />\n\t\t</>\n\t);\n}\n\nfunction DataViewsPicker< Item >( {\n\tview,\n\tonChangeView,\n\tfields,\n\tsearch = true,\n\tsearchLabel = undefined,\n\tactions = EMPTY_ARRAY,\n\tdata,\n\tgetItemId = defaultGetItemId,\n\tisLoading = false,\n\tpaginationInfo,\n\tdefaultLayouts: defaultLayoutsProperty,\n\tselection,\n\tonChangeSelection,\n\tchildren,\n\tconfig = { perPageSizes: [ 10, 20, 50, 100 ] },\n\titemListLabel,\n\tempty,\n}: DataViewsPickerProps< Item > ) {\n\t// useData ensures data loading is correct whether infinite scroll is enabled or pagination is used.\n\tconst { data: displayData, setVisibleEntries } = useData( {\n\t\tview,\n\t\tdata: data as any,\n\t\tgetItemId: getItemId as any,\n\t\tselection,\n\t\tpaginationInfo,\n\t} ) as {\n\t\tdata: ( Item & { position?: number } )[];\n\t\tsetVisibleEntries?: React.Dispatch< React.SetStateAction< number[] > >;\n\t};\n\tconst containerRef = useRef< HTMLDivElement >( null );\n\tconst [ containerWidth, setContainerWidth ] = useState( 0 );\n\tconst resizeObserverRef = useResizeObserver(\n\t\t( resizeObserverEntries: any ) => {\n\t\t\tsetContainerWidth(\n\t\t\t\tresizeObserverEntries[ 0 ].borderBoxSize[ 0 ].inlineSize\n\t\t\t);\n\t\t},\n\t\t{ box: 'border-box' }\n\t);\n\tconst [ openedFilter, setOpenedFilter ] = useState< string | null >( null );\n\tfunction setSelectionWithChange( value: SelectionOrUpdater ) {\n\t\tconst newValue =\n\t\t\ttypeof value === 'function' ? value( selection ) : value;\n\t\tif ( onChangeSelection ) {\n\t\t\tonChangeSelection( newValue );\n\t\t}\n\t}\n\tconst _fields = useMemo( () => normalizeFields( fields ), [ fields ] );\n\tconst filters = useFilters( _fields, view );\n\n\tconst hasPrimaryOrLockedFilters = useMemo(\n\t\t() =>\n\t\t\t( filters || [] ).some(\n\t\t\t\t( filter ) => filter.isPrimary || filter.isLocked\n\t\t\t),\n\t\t[ filters ]\n\t);\n\tconst [ isShowingFilter, setIsShowingFilter ] = useState< boolean >(\n\t\thasPrimaryOrLockedFilters\n\t);\n\n\tconst { intersectionObserver } = useInfiniteScroll( {\n\t\tview,\n\t\tonChangeView,\n\t\tisLoading,\n\t\tpaginationInfo,\n\t\tcontainerRef,\n\t\tsetVisibleEntries,\n\t} );\n\n\tuseEffect( () => {\n\t\tif ( hasPrimaryOrLockedFilters && ! isShowingFilter ) {\n\t\t\tsetIsShowingFilter( true );\n\t\t}\n\t}, [ hasPrimaryOrLockedFilters, isShowingFilter ] );\n\n\t// Filter out DataViewsPicker layouts.\n\tconst defaultLayouts = useMemo(\n\t\t() =>\n\t\t\tObject.fromEntries(\n\t\t\t\tObject.entries( defaultLayoutsProperty ).filter(\n\t\t\t\t\t( [ layoutType ] ) => {\n\t\t\t\t\t\treturn dataViewsPickerLayouts.some(\n\t\t\t\t\t\t\t( viewLayout ) => viewLayout.type === layoutType\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t),\n\t\t[ defaultLayoutsProperty ]\n\t);\n\n\tif ( ! defaultLayouts[ view.type ] ) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<DataViewsContext.Provider\n\t\t\tvalue={ {\n\t\t\t\tview,\n\t\t\t\tonChangeView,\n\t\t\t\tfields: _fields,\n\t\t\t\tactions,\n\t\t\t\tdata: displayData,\n\t\t\t\tisLoading,\n\t\t\t\tpaginationInfo,\n\t\t\t\tisItemClickable,\n\t\t\t\tselection,\n\t\t\t\tonChangeSelection: setSelectionWithChange,\n\t\t\t\topenedFilter,\n\t\t\t\tsetOpenedFilter,\n\t\t\t\tgetItemId,\n\t\t\t\tcontainerWidth,\n\t\t\t\tcontainerRef,\n\t\t\t\tresizeObserverRef,\n\t\t\t\tdefaultLayouts,\n\t\t\t\tfilters,\n\t\t\t\tisShowingFilter,\n\t\t\t\tsetIsShowingFilter,\n\t\t\t\tconfig,\n\t\t\t\titemListLabel,\n\t\t\t\tempty,\n\t\t\t\thasInitiallyLoaded: true,\n\t\t\t\tintersectionObserver,\n\t\t\t} }\n\t\t>\n\t\t\t<div className=\"dataviews-picker-wrapper\">\n\t\t\t\t{ children ?? (\n\t\t\t\t\t<DefaultUI search={ search } searchLabel={ searchLabel } />\n\t\t\t\t) }\n\t\t\t</div>\n\t\t</DataViewsContext.Provider>\n\t);\n}\n\n// Populate the DataViews sub components\nconst DataViewsPickerSubComponents =\n\tDataViewsPicker as typeof DataViewsPicker & {\n\t\tBulkActionToolbar: typeof DataViewsPickerFooter;\n\t\tFilters: typeof Filters;\n\t\tFiltersToggled: typeof FiltersToggled;\n\t\tFiltersToggle: typeof FiltersToggle;\n\t\tLayout: typeof DataViewsLayout;\n\t\tLayoutSwitcher: typeof ViewTypeMenu;\n\t\tPagination: typeof DataViewsPagination;\n\t\tSearch: typeof DataViewsSearch;\n\t\tViewConfig: typeof DataviewsViewConfigDropdown;\n\t};\n\nDataViewsPickerSubComponents.BulkActionToolbar = DataViewsPickerFooter;\nDataViewsPickerSubComponents.Filters = Filters;\nDataViewsPickerSubComponents.FiltersToggled = FiltersToggled;\nDataViewsPickerSubComponents.FiltersToggle = FiltersToggle;\nDataViewsPickerSubComponents.Layout = DataViewsLayout;\nDataViewsPickerSubComponents.LayoutSwitcher = ViewTypeMenu;\nDataViewsPickerSubComponents.Pagination = DataViewsPagination;\nDataViewsPickerSubComponents.Search = DataViewsSearch;\nDataViewsPickerSubComponents.ViewConfig = DataviewsViewConfigDropdown;\n\nexport default DataViewsPickerSubComponents;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
// packages/dataviews/src/hooks/index.ts
|
|
2
2
|
import { default as default2 } from "./use-form-validity.mjs";
|
|
3
|
+
import { default as default3 } from "./use-data.mjs";
|
|
4
|
+
import { useInfiniteScroll } from "./use-infinite-scroll.mjs";
|
|
5
|
+
import { default as default4 } from "./use-selected-items.mjs";
|
|
3
6
|
export {
|
|
4
|
-
|
|
7
|
+
default3 as useData,
|
|
8
|
+
default2 as useFormValidity,
|
|
9
|
+
useInfiniteScroll,
|
|
10
|
+
default4 as useSelectedItems
|
|
5
11
|
};
|
|
6
12
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/hooks/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Internal dependencies\n */\nexport { default as useFormValidity } from './use-form-validity';\n"],
|
|
5
|
-
"mappings": ";AAGA,SAAoB,WAAXA,gBAAkC;",
|
|
4
|
+
"sourcesContent": ["/**\n * Internal dependencies\n */\nexport { default as useFormValidity } from './use-form-validity';\nexport { default as useData } from './use-data';\nexport { useInfiniteScroll } from './use-infinite-scroll';\nexport { default as useSelectedItems } from './use-selected-items';\n"],
|
|
5
|
+
"mappings": ";AAGA,SAAoB,WAAXA,gBAAkC;AAC3C,SAAoB,WAAXA,gBAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAoB,WAAXA,gBAAmC;",
|
|
6
6
|
"names": ["default"]
|
|
7
7
|
}
|
|
@@ -1,22 +1,159 @@
|
|
|
1
1
|
// packages/dataviews/src/hooks/use-data.ts
|
|
2
|
-
import { useEffect,
|
|
3
|
-
function useData(
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { useState, useEffect, useMemo, useRef } from "@wordpress/element";
|
|
3
|
+
function useData({
|
|
4
|
+
view,
|
|
5
|
+
data: shownData,
|
|
6
|
+
getItemId,
|
|
7
|
+
isLoading,
|
|
8
|
+
paginationInfo,
|
|
9
|
+
selection
|
|
10
|
+
}) {
|
|
11
|
+
const isInfiniteScrollEnabled = view.infiniteScrollEnabled;
|
|
6
12
|
const [hasInitiallyLoaded, setHasInitiallyLoaded] = useState(
|
|
7
13
|
!isLoading
|
|
8
14
|
);
|
|
9
15
|
useEffect(() => {
|
|
10
16
|
if (!isLoading) {
|
|
11
|
-
previousDataRef.current = data;
|
|
12
|
-
previousPaginationInfoRef.current = paginationInfo;
|
|
13
17
|
setHasInitiallyLoaded(true);
|
|
14
18
|
}
|
|
15
|
-
}, [
|
|
19
|
+
}, [isLoading]);
|
|
20
|
+
const previousDataRef = useRef(shownData);
|
|
21
|
+
const previousPaginationInfoRef = useRef(paginationInfo);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!isLoading) {
|
|
24
|
+
previousDataRef.current = shownData;
|
|
25
|
+
previousPaginationInfoRef.current = paginationInfo;
|
|
26
|
+
}
|
|
27
|
+
}, [shownData, isLoading, paginationInfo]);
|
|
28
|
+
const [visibleEntries, setVisibleEntries] = useState([]);
|
|
29
|
+
const positionMapRef = useRef(/* @__PURE__ */ new Map());
|
|
30
|
+
const allLoadedRecordsRef = useRef([]);
|
|
31
|
+
const prevViewParamsRef = useRef({
|
|
32
|
+
search: void 0,
|
|
33
|
+
filters: void 0,
|
|
34
|
+
perPage: void 0
|
|
35
|
+
});
|
|
36
|
+
const scrollDirectionRef = useRef(void 0);
|
|
37
|
+
const prevStartPositionRef = useRef(void 0);
|
|
38
|
+
const hasInitializedRef = useRef(false);
|
|
39
|
+
const allLoadedRecords = useMemo(() => {
|
|
40
|
+
if (view.startPosition !== void 0 && prevStartPositionRef.current !== void 0) {
|
|
41
|
+
if (view.startPosition < prevStartPositionRef.current) {
|
|
42
|
+
scrollDirectionRef.current = "up";
|
|
43
|
+
} else if (view.startPosition > prevStartPositionRef.current) {
|
|
44
|
+
scrollDirectionRef.current = "down";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
prevStartPositionRef.current = view.startPosition;
|
|
48
|
+
const currentFiltersKey = JSON.stringify(view.filters ?? []);
|
|
49
|
+
const prevFiltersKey = prevViewParamsRef.current.filters;
|
|
50
|
+
const shouldReset = !hasInitializedRef.current || !view.infiniteScrollEnabled || view.search !== prevViewParamsRef.current.search || currentFiltersKey !== prevFiltersKey || view.perPage !== prevViewParamsRef.current.perPage;
|
|
51
|
+
hasInitializedRef.current = true;
|
|
52
|
+
prevViewParamsRef.current = {
|
|
53
|
+
search: view.search,
|
|
54
|
+
filters: currentFiltersKey,
|
|
55
|
+
perPage: view.perPage
|
|
56
|
+
};
|
|
57
|
+
if (shouldReset) {
|
|
58
|
+
positionMapRef.current.clear();
|
|
59
|
+
scrollDirectionRef.current = void 0;
|
|
60
|
+
const startPosition = view.search ? 1 : view.startPosition ?? 1;
|
|
61
|
+
const records = shownData.map((record, index) => {
|
|
62
|
+
const position = startPosition + index;
|
|
63
|
+
positionMapRef.current.set(getItemId(record), position);
|
|
64
|
+
return {
|
|
65
|
+
...record,
|
|
66
|
+
position
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
allLoadedRecordsRef.current = records;
|
|
70
|
+
return records;
|
|
71
|
+
}
|
|
72
|
+
const prev = allLoadedRecordsRef.current;
|
|
73
|
+
const shownDataIds = new Set(shownData.map(getItemId));
|
|
74
|
+
const scrollDirection = scrollDirectionRef.current;
|
|
75
|
+
const basePosition = view.search ? 1 : view.startPosition ?? 1;
|
|
76
|
+
const newRecords = shownData.map((record, index) => {
|
|
77
|
+
const itemId = getItemId(record);
|
|
78
|
+
const position = view.infiniteScrollEnabled ? basePosition + index : void 0;
|
|
79
|
+
if (position !== void 0) {
|
|
80
|
+
positionMapRef.current.set(itemId, position);
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
...record,
|
|
84
|
+
position
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
if (newRecords.length === 0) {
|
|
88
|
+
return prev;
|
|
89
|
+
}
|
|
90
|
+
const prevWithoutDuplicates = prev.filter(
|
|
91
|
+
(record) => !shownDataIds.has(getItemId(record))
|
|
92
|
+
);
|
|
93
|
+
const allRecords = scrollDirection === "up" ? [...newRecords, ...prevWithoutDuplicates] : [...prevWithoutDuplicates, ...newRecords];
|
|
94
|
+
allRecords.sort((a, b) => {
|
|
95
|
+
const posA = a.position;
|
|
96
|
+
const posB = b.position;
|
|
97
|
+
return posA - posB;
|
|
98
|
+
});
|
|
99
|
+
let result = allRecords;
|
|
100
|
+
if (visibleEntries.length > 0) {
|
|
101
|
+
const visibleMin = Math.min(...visibleEntries);
|
|
102
|
+
const visibleMax = Math.max(...visibleEntries);
|
|
103
|
+
const buffer = 20;
|
|
104
|
+
const recordPositions = allRecords.map(
|
|
105
|
+
(r) => r.position
|
|
106
|
+
);
|
|
107
|
+
const minRecordPos = Math.min(...recordPositions);
|
|
108
|
+
const maxRecordPos = Math.max(...recordPositions);
|
|
109
|
+
const hasOverlap = !(maxRecordPos < visibleMin - buffer || minRecordPos > visibleMax + buffer);
|
|
110
|
+
if (hasOverlap) {
|
|
111
|
+
result = allRecords.filter((record) => {
|
|
112
|
+
const itemId = getItemId(record);
|
|
113
|
+
const isSelected = selection?.includes(itemId);
|
|
114
|
+
if (isSelected) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
const itemPosition = record.position;
|
|
118
|
+
if (scrollDirection === "up") {
|
|
119
|
+
return itemPosition <= visibleMax + buffer;
|
|
120
|
+
} else if (scrollDirection === "down") {
|
|
121
|
+
return itemPosition >= visibleMin - buffer;
|
|
122
|
+
}
|
|
123
|
+
return itemPosition >= visibleMin - buffer && itemPosition <= visibleMax + buffer;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
allLoadedRecordsRef.current = result;
|
|
128
|
+
return result;
|
|
129
|
+
}, [
|
|
130
|
+
shownData,
|
|
131
|
+
view.search,
|
|
132
|
+
view.filters,
|
|
133
|
+
view.perPage,
|
|
134
|
+
view.startPosition,
|
|
135
|
+
view.infiniteScrollEnabled,
|
|
136
|
+
visibleEntries,
|
|
137
|
+
selection,
|
|
138
|
+
getItemId
|
|
139
|
+
]);
|
|
140
|
+
if (!isInfiniteScrollEnabled) {
|
|
141
|
+
const dataToReturn = isLoading && previousDataRef.current?.length ? previousDataRef.current : shownData;
|
|
142
|
+
return {
|
|
143
|
+
data: dataToReturn.map((item) => ({
|
|
144
|
+
...item,
|
|
145
|
+
position: void 0
|
|
146
|
+
})),
|
|
147
|
+
paginationInfo: isLoading && previousDataRef.current?.length ? previousPaginationInfoRef.current : paginationInfo,
|
|
148
|
+
hasInitiallyLoaded,
|
|
149
|
+
setVisibleEntries: void 0
|
|
150
|
+
};
|
|
151
|
+
}
|
|
16
152
|
return {
|
|
17
|
-
data:
|
|
18
|
-
paginationInfo
|
|
19
|
-
hasInitiallyLoaded
|
|
153
|
+
data: allLoadedRecords,
|
|
154
|
+
paginationInfo,
|
|
155
|
+
hasInitiallyLoaded,
|
|
156
|
+
setVisibleEntries
|
|
20
157
|
};
|
|
21
158
|
}
|
|
22
159
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/hooks/use-data.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { useEffect, useRef, useState } from '@wordpress/element';\n\ntype PaginationInfo = {\n\ttotalItems: number;\n\ttotalPages: number;\n\tinfiniteScrollHandler?: () => void;\n};\n\nexport default function useData< Item >(\n\tdata: Item[],\n\tisLoading: boolean | undefined,\n\tpaginationInfo: PaginationInfo\n): {\n\tdata: Item[];\n\tpaginationInfo: PaginationInfo;\n\thasInitiallyLoaded: boolean;\n} {\n\tconst previousDataRef = useRef< Item[] >( data );\n\tconst previousPaginationInfoRef =\n\t\tuseRef< PaginationInfo >( paginationInfo );\n\tconst [ hasInitiallyLoaded, setHasInitiallyLoaded ] = useState(\n\t\t! isLoading\n\t);\n\tuseEffect( () => {\n\t\tif ( ! isLoading ) {\n\t\t\tpreviousDataRef.current = data;\n\t\t\tpreviousPaginationInfoRef.current = paginationInfo;\n\t\t\tsetHasInitiallyLoaded( true );\n\t\t}\n\t}, [ data, isLoading, paginationInfo ] );\n\treturn {\n\t\tdata:\n\t\t\tisLoading && previousDataRef.current?.length\n\t\t\t\t? previousDataRef.current\n\t\t\t\t: data,\n\t\tpaginationInfo:\n\t\t\tisLoading && previousDataRef.current?.length\n\t\t\t\t? previousPaginationInfoRef.current\n\t\t\t\t: paginationInfo,\n\t\thasInitiallyLoaded,\n\t};\n}\n"],
|
|
5
|
-
"mappings": ";AAGA,SAAS,WAAW,
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { useState, useEffect, useMemo, useRef } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport type { View } from '../types';\n\ntype PaginationInfo = {\n\ttotalItems: number;\n\ttotalPages: number;\n};\n\ninterface UseDataParams< Item > {\n\tview: View;\n\tdata: Item[];\n\tgetItemId: ( item: Item ) => string;\n\tisLoading?: boolean;\n\tpaginationInfo: PaginationInfo;\n\tselection?: string[];\n}\n\ninterface UseDataResult< Item > {\n\tdata: ( Item & { position?: number } )[];\n\tpaginationInfo: PaginationInfo;\n\thasInitiallyLoaded: boolean;\n\tsetVisibleEntries?: React.Dispatch< React.SetStateAction< number[] > >;\n}\n\n/**\n * Hook to manage data for DataViews.\n *\n * When infinite scroll is enabled, this hook handles:\n * - Loading more data when scrolling up or down\n * - Maintaining stable positions for items\n * - Unloading items that are no longer visible (with a buffer)\n *\n * When infinite scroll is disabled, it preserves the previous data and\n * pagination info while loading, so the UI doesn't flash empty.\n *\n * In both cases, it tracks whether data has initially loaded.\n *\n * @param params - Configuration parameters\n * @param params.view - Current view configuration\n * @param params.data - Current page of data\n * @param params.getItemId - Function to extract item ID\n * @param params.isLoading - Whether data is currently loading\n * @param params.paginationInfo - Pagination info (totalItems, totalPages)\n * @param params.selection - Currently selected item IDs\n * @return Object containing data, paginationInfo, hasInitiallyLoaded,\n * and optional setVisibleEntries callback\n */\nexport default function useData< Item >( {\n\tview,\n\tdata: shownData,\n\tgetItemId,\n\tisLoading,\n\tpaginationInfo,\n\tselection,\n}: UseDataParams< Item > ): UseDataResult< Item > {\n\tconst isInfiniteScrollEnabled = view.infiniteScrollEnabled;\n\n\tconst [ hasInitiallyLoaded, setHasInitiallyLoaded ] = useState(\n\t\t! isLoading\n\t);\n\tuseEffect( () => {\n\t\tif ( ! isLoading ) {\n\t\t\tsetHasInitiallyLoaded( true );\n\t\t}\n\t}, [ isLoading ] );\n\n\tconst previousDataRef = useRef< Item[] >( shownData );\n\tconst previousPaginationInfoRef =\n\t\tuseRef< PaginationInfo >( paginationInfo );\n\tuseEffect( () => {\n\t\tif ( ! isLoading ) {\n\t\t\tpreviousDataRef.current = shownData;\n\t\t\tpreviousPaginationInfoRef.current = paginationInfo;\n\t\t}\n\t}, [ shownData, isLoading, paginationInfo ] );\n\n\t// Infinite scroll state.\n\tconst [ visibleEntries, setVisibleEntries ] = useState< number[] >( [] );\n\n\t// Track the mapping of item IDs to their positions in the full dataset\n\tconst positionMapRef = useRef< Map< string, number > >( new Map() );\n\n\t// Store accumulated records in a ref for persistence across renders\n\tconst allLoadedRecordsRef = useRef< Item[] >( [] );\n\n\t// Track previous view parameters to detect when we need to reset\n\tconst prevViewParamsRef = useRef< {\n\t\tsearch: string | undefined;\n\t\tfilters: string | undefined;\n\t\tperPage: number | undefined;\n\t} >( {\n\t\tsearch: undefined,\n\t\tfilters: undefined,\n\t\tperPage: undefined,\n\t} );\n\n\t// Determine scroll direction based on position changes\n\tconst scrollDirectionRef = useRef< 'up' | 'down' | undefined >( undefined );\n\tconst prevStartPositionRef = useRef< number | undefined >( undefined );\n\n\t// Track whether we've done initial load\n\tconst hasInitializedRef = useRef( false );\n\n\t// Compute data synchronously during render using useMemo\n\t// This ensures the returned data is always in sync with shownData\n\tconst allLoadedRecords = useMemo( () => {\n\t\t// Update scroll direction based on position changes\n\t\tif (\n\t\t\tview.startPosition !== undefined &&\n\t\t\tprevStartPositionRef.current !== undefined\n\t\t) {\n\t\t\tif ( view.startPosition < prevStartPositionRef.current ) {\n\t\t\t\tscrollDirectionRef.current = 'up';\n\t\t\t} else if ( view.startPosition > prevStartPositionRef.current ) {\n\t\t\t\tscrollDirectionRef.current = 'down';\n\t\t\t}\n\t\t}\n\t\tprevStartPositionRef.current = view.startPosition;\n\n\t\t// Serialize filters for comparison\n\t\tconst currentFiltersKey = JSON.stringify( view.filters ?? [] );\n\t\tconst prevFiltersKey = prevViewParamsRef.current.filters;\n\n\t\t// Check if view parameters that require a reset have changed\n\t\tconst shouldReset =\n\t\t\t! hasInitializedRef.current ||\n\t\t\t! view.infiniteScrollEnabled ||\n\t\t\tview.search !== prevViewParamsRef.current.search ||\n\t\t\tcurrentFiltersKey !== prevFiltersKey ||\n\t\t\tview.perPage !== prevViewParamsRef.current.perPage;\n\t\thasInitializedRef.current = true;\n\t\t// Update tracked view parameters\n\t\tprevViewParamsRef.current = {\n\t\t\tsearch: view.search,\n\t\t\tfilters: currentFiltersKey,\n\t\t\tperPage: view.perPage,\n\t\t};\n\n\t\tif ( shouldReset ) {\n\t\t\t// Reset - clear position map and replace all data\n\t\t\tpositionMapRef.current.clear();\n\t\t\t// Reset scroll direction to prevent stale directional filtering\n\t\t\tscrollDirectionRef.current = undefined;\n\t\t\t// Use the view's startPosition if defined, otherwise default to 1\n\t\t\tconst startPosition = view.search ? 1 : view.startPosition ?? 1;\n\t\t\tconst records = shownData.map( ( record, index ) => {\n\t\t\t\tconst position = startPosition + index;\n\t\t\t\tpositionMapRef.current.set( getItemId( record ), position );\n\t\t\t\treturn {\n\t\t\t\t\t...record,\n\t\t\t\t\tposition,\n\t\t\t\t};\n\t\t\t} );\n\t\t\tallLoadedRecordsRef.current = records;\n\t\t\treturn records;\n\t\t}\n\n\t\t// Subsequent pages - merge with existing data\n\t\tconst prev = allLoadedRecordsRef.current;\n\t\tconst shownDataIds = new Set( shownData.map( getItemId ) );\n\t\tconst scrollDirection = scrollDirectionRef.current;\n\n\t\t// The position for each item in shownData should be based on the\n\t\t// current startPosition from the view, which reflects the actual\n\t\t// offset in the dataset. This ensures aria-posinset values are\n\t\t// semantically correct - if startPosition is 40, there are exactly\n\t\t// 39 items before the first item in shownData.\n\t\t// When there's an active search, always start from position 1 since\n\t\t// search results are a filtered subset, not a paginated view.\n\t\tconst basePosition = view.search ? 1 : view.startPosition ?? 1;\n\t\tconst newRecords = shownData.map( ( record, index ) => {\n\t\t\tconst itemId = getItemId( record );\n\t\t\tconst position = view.infiniteScrollEnabled\n\t\t\t\t? basePosition + index\n\t\t\t\t: undefined;\n\n\t\t\t// Always update the position map with the correct position\n\t\t\t// based on the current query's startPosition\n\t\t\tif ( position !== undefined ) {\n\t\t\t\tpositionMapRef.current.set( itemId, position );\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t...record,\n\t\t\t\tposition,\n\t\t\t};\n\t\t} );\n\n\t\tif ( newRecords.length === 0 ) {\n\t\t\treturn prev;\n\t\t}\n\n\t\t// Remove duplicates from prev, keeping only records not in shownData\n\t\tconst prevWithoutDuplicates = prev.filter(\n\t\t\t( record ) => ! shownDataIds.has( getItemId( record ) )\n\t\t);\n\n\t\t// Update the loaded range\n\t\tconst allRecords =\n\t\t\tscrollDirection === 'up'\n\t\t\t\t? [ ...newRecords, ...prevWithoutDuplicates ]\n\t\t\t\t: [ ...prevWithoutDuplicates, ...newRecords ];\n\n\t\t// Sort all records by position to ensure correct order\n\t\t// This is crucial when items are reloaded after scrolling in different directions\n\t\tallRecords.sort( ( a, b ) => {\n\t\t\tconst posA = ( a as Item & { position: number } ).position;\n\t\t\tconst posB = ( b as Item & { position: number } ).position;\n\t\t\treturn posA - posB;\n\t\t} );\n\n\t\tlet result = allRecords;\n\n\t\tif ( visibleEntries.length > 0 ) {\n\t\t\tconst visibleMin = Math.min( ...visibleEntries );\n\t\t\tconst visibleMax = Math.max( ...visibleEntries );\n\t\t\t// Buffer size balances allowing new items to render (when prepended\n\t\t\t// during scroll up) while unloading items no longer on screen.\n\t\t\t// Use a larger buffer to prevent scrollbar from jumping when items\n\t\t\t// are unloaded, which could trigger unwanted scroll events.\n\t\t\tconst buffer = 20;\n\n\t\t\tconst recordPositions = allRecords.map(\n\t\t\t\t( r ) => ( r as Item & { position: number } ).position\n\t\t\t);\n\t\t\tconst minRecordPos = Math.min( ...recordPositions );\n\t\t\tconst maxRecordPos = Math.max( ...recordPositions );\n\n\t\t\t// Check if there's any overlap between visible range and actual record positions\n\t\t\t// to avoid filtering when visibleEntries are stale (e.g., after search/filter reset)\n\t\t\tconst hasOverlap = ! (\n\t\t\t\tmaxRecordPos < visibleMin - buffer ||\n\t\t\t\tminRecordPos > visibleMax + buffer\n\t\t\t);\n\n\t\t\tif ( hasOverlap ) {\n\t\t\t\tresult = allRecords.filter( ( record ) => {\n\t\t\t\t\tconst itemId = getItemId( record );\n\t\t\t\t\tconst isSelected = selection?.includes( itemId );\n\t\t\t\t\t// Never unload selected items, even if outside visible range\n\t\t\t\t\tif ( isSelected ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst itemPosition = (\n\t\t\t\t\t\trecord as Item & { position: number }\n\t\t\t\t\t ).position;\n\t\t\t\t\t// When scrolling, only trim items from the end we're scrolling away from\n\t\t\t\t\tif ( scrollDirection === 'up' ) {\n\t\t\t\t\t\t// When scrolling up, only trim items below the visible range\n\t\t\t\t\t\treturn itemPosition <= visibleMax + buffer;\n\t\t\t\t\t} else if ( scrollDirection === 'down' ) {\n\t\t\t\t\t\t// When scrolling down, only trim items above the visible range\n\t\t\t\t\t\treturn itemPosition >= visibleMin - buffer;\n\t\t\t\t\t}\n\t\t\t\t\t// When not scrolling or first load, keep items within buffer range\n\t\t\t\t\treturn (\n\t\t\t\t\t\titemPosition >= visibleMin - buffer &&\n\t\t\t\t\t\titemPosition <= visibleMax + buffer\n\t\t\t\t\t);\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\n\t\tallLoadedRecordsRef.current = result;\n\t\treturn result;\n\t}, [\n\t\tshownData,\n\t\tview.search,\n\t\tview.filters,\n\t\tview.perPage,\n\t\tview.startPosition,\n\t\tview.infiniteScrollEnabled,\n\t\tvisibleEntries,\n\t\tselection,\n\t\tgetItemId,\n\t] );\n\n\t// When infinite scroll is disabled, preserve previous data while loading\n\tif ( ! isInfiniteScrollEnabled ) {\n\t\tconst dataToReturn =\n\t\t\tisLoading && previousDataRef.current?.length\n\t\t\t\t? previousDataRef.current\n\t\t\t\t: shownData;\n\t\treturn {\n\t\t\tdata: dataToReturn.map( ( item ) => ( {\n\t\t\t\t...item,\n\t\t\t\tposition: undefined,\n\t\t\t} ) ) as ( Item & { position?: number } )[],\n\t\t\tpaginationInfo:\n\t\t\t\tisLoading && previousDataRef.current?.length\n\t\t\t\t\t? previousPaginationInfoRef.current\n\t\t\t\t\t: paginationInfo,\n\t\t\thasInitiallyLoaded,\n\t\t\tsetVisibleEntries: undefined,\n\t\t};\n\t}\n\n\treturn {\n\t\tdata: allLoadedRecords as ( Item & { position?: number } )[],\n\t\tpaginationInfo,\n\t\thasInitiallyLoaded,\n\t\tsetVisibleEntries,\n\t};\n}\n"],
|
|
5
|
+
"mappings": ";AAGA,SAAS,UAAU,WAAW,SAAS,cAAc;AAmDtC,SAAR,QAAkC;AAAA,EACxC;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAkD;AACjD,QAAM,0BAA0B,KAAK;AAErC,QAAM,CAAE,oBAAoB,qBAAsB,IAAI;AAAA,IACrD,CAAE;AAAA,EACH;AACA,YAAW,MAAM;AAChB,QAAK,CAAE,WAAY;AAClB,4BAAuB,IAAK;AAAA,IAC7B;AAAA,EACD,GAAG,CAAE,SAAU,CAAE;AAEjB,QAAM,kBAAkB,OAAkB,SAAU;AACpD,QAAM,4BACL,OAA0B,cAAe;AAC1C,YAAW,MAAM;AAChB,QAAK,CAAE,WAAY;AAClB,sBAAgB,UAAU;AAC1B,gCAA0B,UAAU;AAAA,IACrC;AAAA,EACD,GAAG,CAAE,WAAW,WAAW,cAAe,CAAE;AAG5C,QAAM,CAAE,gBAAgB,iBAAkB,IAAI,SAAsB,CAAC,CAAE;AAGvE,QAAM,iBAAiB,OAAiC,oBAAI,IAAI,CAAE;AAGlE,QAAM,sBAAsB,OAAkB,CAAC,CAAE;AAGjD,QAAM,oBAAoB,OAIrB;AAAA,IACJ,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACV,CAAE;AAGF,QAAM,qBAAqB,OAAqC,MAAU;AAC1E,QAAM,uBAAuB,OAA8B,MAAU;AAGrE,QAAM,oBAAoB,OAAQ,KAAM;AAIxC,QAAM,mBAAmB,QAAS,MAAM;AAEvC,QACC,KAAK,kBAAkB,UACvB,qBAAqB,YAAY,QAChC;AACD,UAAK,KAAK,gBAAgB,qBAAqB,SAAU;AACxD,2BAAmB,UAAU;AAAA,MAC9B,WAAY,KAAK,gBAAgB,qBAAqB,SAAU;AAC/D,2BAAmB,UAAU;AAAA,MAC9B;AAAA,IACD;AACA,yBAAqB,UAAU,KAAK;AAGpC,UAAM,oBAAoB,KAAK,UAAW,KAAK,WAAW,CAAC,CAAE;AAC7D,UAAM,iBAAiB,kBAAkB,QAAQ;AAGjD,UAAM,cACL,CAAE,kBAAkB,WACpB,CAAE,KAAK,yBACP,KAAK,WAAW,kBAAkB,QAAQ,UAC1C,sBAAsB,kBACtB,KAAK,YAAY,kBAAkB,QAAQ;AAC5C,sBAAkB,UAAU;AAE5B,sBAAkB,UAAU;AAAA,MAC3B,QAAQ,KAAK;AAAA,MACb,SAAS;AAAA,MACT,SAAS,KAAK;AAAA,IACf;AAEA,QAAK,aAAc;AAElB,qBAAe,QAAQ,MAAM;AAE7B,yBAAmB,UAAU;AAE7B,YAAM,gBAAgB,KAAK,SAAS,IAAI,KAAK,iBAAiB;AAC9D,YAAM,UAAU,UAAU,IAAK,CAAE,QAAQ,UAAW;AACnD,cAAM,WAAW,gBAAgB;AACjC,uBAAe,QAAQ,IAAK,UAAW,MAAO,GAAG,QAAS;AAC1D,eAAO;AAAA,UACN,GAAG;AAAA,UACH;AAAA,QACD;AAAA,MACD,CAAE;AACF,0BAAoB,UAAU;AAC9B,aAAO;AAAA,IACR;AAGA,UAAM,OAAO,oBAAoB;AACjC,UAAM,eAAe,IAAI,IAAK,UAAU,IAAK,SAAU,CAAE;AACzD,UAAM,kBAAkB,mBAAmB;AAS3C,UAAM,eAAe,KAAK,SAAS,IAAI,KAAK,iBAAiB;AAC7D,UAAM,aAAa,UAAU,IAAK,CAAE,QAAQ,UAAW;AACtD,YAAM,SAAS,UAAW,MAAO;AACjC,YAAM,WAAW,KAAK,wBACnB,eAAe,QACf;AAIH,UAAK,aAAa,QAAY;AAC7B,uBAAe,QAAQ,IAAK,QAAQ,QAAS;AAAA,MAC9C;AAEA,aAAO;AAAA,QACN,GAAG;AAAA,QACH;AAAA,MACD;AAAA,IACD,CAAE;AAEF,QAAK,WAAW,WAAW,GAAI;AAC9B,aAAO;AAAA,IACR;AAGA,UAAM,wBAAwB,KAAK;AAAA,MAClC,CAAE,WAAY,CAAE,aAAa,IAAK,UAAW,MAAO,CAAE;AAAA,IACvD;AAGA,UAAM,aACL,oBAAoB,OACjB,CAAE,GAAG,YAAY,GAAG,qBAAsB,IAC1C,CAAE,GAAG,uBAAuB,GAAG,UAAW;AAI9C,eAAW,KAAM,CAAE,GAAG,MAAO;AAC5B,YAAM,OAAS,EAAmC;AAClD,YAAM,OAAS,EAAmC;AAClD,aAAO,OAAO;AAAA,IACf,CAAE;AAEF,QAAI,SAAS;AAEb,QAAK,eAAe,SAAS,GAAI;AAChC,YAAM,aAAa,KAAK,IAAK,GAAG,cAAe;AAC/C,YAAM,aAAa,KAAK,IAAK,GAAG,cAAe;AAK/C,YAAM,SAAS;AAEf,YAAM,kBAAkB,WAAW;AAAA,QAClC,CAAE,MAAS,EAAmC;AAAA,MAC/C;AACA,YAAM,eAAe,KAAK,IAAK,GAAG,eAAgB;AAClD,YAAM,eAAe,KAAK,IAAK,GAAG,eAAgB;AAIlD,YAAM,aAAa,EAClB,eAAe,aAAa,UAC5B,eAAe,aAAa;AAG7B,UAAK,YAAa;AACjB,iBAAS,WAAW,OAAQ,CAAE,WAAY;AACzC,gBAAM,SAAS,UAAW,MAAO;AACjC,gBAAM,aAAa,WAAW,SAAU,MAAO;AAE/C,cAAK,YAAa;AACjB,mBAAO;AAAA,UACR;AAEA,gBAAM,eACL,OACE;AAEH,cAAK,oBAAoB,MAAO;AAE/B,mBAAO,gBAAgB,aAAa;AAAA,UACrC,WAAY,oBAAoB,QAAS;AAExC,mBAAO,gBAAgB,aAAa;AAAA,UACrC;AAEA,iBACC,gBAAgB,aAAa,UAC7B,gBAAgB,aAAa;AAAA,QAE/B,CAAE;AAAA,MACH;AAAA,IACD;AAEA,wBAAoB,UAAU;AAC9B,WAAO;AAAA,EACR,GAAG;AAAA,IACF;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACD,CAAE;AAGF,MAAK,CAAE,yBAA0B;AAChC,UAAM,eACL,aAAa,gBAAgB,SAAS,SACnC,gBAAgB,UAChB;AACJ,WAAO;AAAA,MACN,MAAM,aAAa,IAAK,CAAE,UAAY;AAAA,QACrC,GAAG;AAAA,QACH,UAAU;AAAA,MACX,EAAI;AAAA,MACJ,gBACC,aAAa,gBAAgB,SAAS,SACnC,0BAA0B,UAC1B;AAAA,MACJ;AAAA,MACA,mBAAmB;AAAA,IACpB;AAAA,EACD;AAEA,SAAO;AAAA,IACN,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// packages/dataviews/src/hooks/use-infinite-scroll.ts
|
|
2
|
+
import {
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useLayoutEffect,
|
|
6
|
+
useRef
|
|
7
|
+
} from "@wordpress/element";
|
|
8
|
+
import { throttle } from "@wordpress/compose";
|
|
9
|
+
function captureAnchorElement(container, anchorElementRef, direction) {
|
|
10
|
+
const containerRect = container.getBoundingClientRect();
|
|
11
|
+
const centerY = containerRect.top + containerRect.height / 2;
|
|
12
|
+
const items = Array.from(container.querySelectorAll("[aria-posinset]"));
|
|
13
|
+
if (items.length === 0) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const bestAnchor = items.reduce((best, item) => {
|
|
17
|
+
const itemRect = item.getBoundingClientRect();
|
|
18
|
+
const itemCenterY = itemRect.top + itemRect.height / 2;
|
|
19
|
+
const distance = Math.abs(itemCenterY - centerY);
|
|
20
|
+
const bestRect = best.getBoundingClientRect();
|
|
21
|
+
const bestCenterY = bestRect.top + bestRect.height / 2;
|
|
22
|
+
const bestDistance = Math.abs(bestCenterY - centerY);
|
|
23
|
+
return distance < bestDistance ? item : best;
|
|
24
|
+
});
|
|
25
|
+
const posinset = Number(bestAnchor.getAttribute("aria-posinset"));
|
|
26
|
+
const anchorRect = bestAnchor.getBoundingClientRect();
|
|
27
|
+
anchorElementRef.current = {
|
|
28
|
+
posinset,
|
|
29
|
+
viewportOffset: anchorRect.top - containerRect.top,
|
|
30
|
+
direction
|
|
31
|
+
};
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
function useInfiniteScroll({
|
|
35
|
+
view,
|
|
36
|
+
onChangeView,
|
|
37
|
+
isLoading,
|
|
38
|
+
paginationInfo,
|
|
39
|
+
containerRef,
|
|
40
|
+
setVisibleEntries
|
|
41
|
+
}) {
|
|
42
|
+
const anchorElementRef = useRef(null);
|
|
43
|
+
const viewRef = useRef(view);
|
|
44
|
+
const isLoadingRef = useRef(isLoading);
|
|
45
|
+
const onChangeViewRef = useRef(onChangeView);
|
|
46
|
+
const totalItemsRef = useRef(paginationInfo.totalItems);
|
|
47
|
+
useLayoutEffect(() => {
|
|
48
|
+
viewRef.current = view;
|
|
49
|
+
isLoadingRef.current = isLoading;
|
|
50
|
+
onChangeViewRef.current = onChangeView;
|
|
51
|
+
totalItemsRef.current = paginationInfo.totalItems;
|
|
52
|
+
}, [view, isLoading, onChangeView, paginationInfo.totalItems]);
|
|
53
|
+
const intersectionObserverCallback = useCallback(
|
|
54
|
+
(entries) => {
|
|
55
|
+
if (!setVisibleEntries) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
setVisibleEntries((prev) => {
|
|
59
|
+
const newVisibleEntries = new Set(prev);
|
|
60
|
+
let hasChanged = false;
|
|
61
|
+
entries.forEach((entry) => {
|
|
62
|
+
const posInSet = Number(
|
|
63
|
+
entry.target?.attributes?.getNamedItem(
|
|
64
|
+
"aria-posinset"
|
|
65
|
+
)?.value
|
|
66
|
+
);
|
|
67
|
+
if (isNaN(posInSet)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (entry.isIntersecting) {
|
|
71
|
+
if (!newVisibleEntries.has(posInSet)) {
|
|
72
|
+
newVisibleEntries.add(posInSet);
|
|
73
|
+
hasChanged = true;
|
|
74
|
+
}
|
|
75
|
+
} else if (newVisibleEntries.has(posInSet)) {
|
|
76
|
+
newVisibleEntries.delete(posInSet);
|
|
77
|
+
hasChanged = true;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
return hasChanged ? Array.from(newVisibleEntries).sort() : prev;
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
[setVisibleEntries]
|
|
84
|
+
);
|
|
85
|
+
useLayoutEffect(() => {
|
|
86
|
+
const container = containerRef.current;
|
|
87
|
+
const anchor = anchorElementRef.current;
|
|
88
|
+
if (!container || !view.infiniteScrollEnabled || !anchor || isLoading) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const anchorElement = container.querySelector(
|
|
92
|
+
`[aria-posinset="${anchor.posinset}"]`
|
|
93
|
+
);
|
|
94
|
+
if (anchorElement) {
|
|
95
|
+
const containerRect = container.getBoundingClientRect();
|
|
96
|
+
const anchorRect = anchorElement.getBoundingClientRect();
|
|
97
|
+
const currentOffset = anchorRect.top - containerRect.top;
|
|
98
|
+
const scrollAdjustment = currentOffset - anchor.viewportOffset;
|
|
99
|
+
if (Math.abs(scrollAdjustment) > 1) {
|
|
100
|
+
container.scrollTop += scrollAdjustment;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
anchorElementRef.current = null;
|
|
104
|
+
}, [containerRef, isLoading, view.infiniteScrollEnabled]);
|
|
105
|
+
const intersectionObserverRef = useRef(
|
|
106
|
+
null
|
|
107
|
+
);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
if (!view.infiniteScrollEnabled || !intersectionObserverCallback) {
|
|
110
|
+
if (intersectionObserverRef.current) {
|
|
111
|
+
intersectionObserverRef.current.disconnect();
|
|
112
|
+
intersectionObserverRef.current = null;
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
intersectionObserverRef.current = new IntersectionObserver(
|
|
117
|
+
intersectionObserverCallback,
|
|
118
|
+
{ root: null, rootMargin: "0px", threshold: 0.1 }
|
|
119
|
+
);
|
|
120
|
+
return () => {
|
|
121
|
+
if (intersectionObserverRef.current) {
|
|
122
|
+
intersectionObserverRef.current.disconnect();
|
|
123
|
+
intersectionObserverRef.current = null;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}, [view.infiniteScrollEnabled, intersectionObserverCallback]);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (!view.infiniteScrollEnabled || !containerRef.current) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
let lastScrollTop = 0;
|
|
132
|
+
const BOTTOM_THRESHOLD = 600;
|
|
133
|
+
const TOP_THRESHOLD = 800;
|
|
134
|
+
const handleScroll = throttle((event) => {
|
|
135
|
+
const currentView = viewRef.current;
|
|
136
|
+
const totalItems = totalItemsRef.current;
|
|
137
|
+
const target = event.target;
|
|
138
|
+
const scrollTop = target.scrollTop;
|
|
139
|
+
const scrollHeight = target.scrollHeight;
|
|
140
|
+
const clientHeight = target.clientHeight;
|
|
141
|
+
const scrollDirection = scrollTop > lastScrollTop ? "down" : "up";
|
|
142
|
+
lastScrollTop = scrollTop;
|
|
143
|
+
if (isLoadingRef.current) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const currentStartPosition = currentView.startPosition || 1;
|
|
147
|
+
const batchSize = currentView.perPage || 10;
|
|
148
|
+
const currentEndPosition = Math.min(
|
|
149
|
+
currentStartPosition + batchSize,
|
|
150
|
+
totalItems
|
|
151
|
+
);
|
|
152
|
+
if (scrollDirection === "down" && scrollTop + clientHeight >= scrollHeight - BOTTOM_THRESHOLD) {
|
|
153
|
+
if (currentEndPosition < totalItems) {
|
|
154
|
+
const newStartPosition = currentEndPosition;
|
|
155
|
+
captureAnchorElement(target, anchorElementRef, "down");
|
|
156
|
+
onChangeViewRef.current({
|
|
157
|
+
...currentView,
|
|
158
|
+
startPosition: newStartPosition
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (scrollDirection === "up" && scrollTop <= TOP_THRESHOLD) {
|
|
163
|
+
if (currentStartPosition > 1) {
|
|
164
|
+
const calculatedStartPosition = currentStartPosition - batchSize;
|
|
165
|
+
const newStartPosition = calculatedStartPosition < 6 ? 1 : calculatedStartPosition;
|
|
166
|
+
captureAnchorElement(target, anchorElementRef, "up");
|
|
167
|
+
onChangeViewRef.current({
|
|
168
|
+
...currentView,
|
|
169
|
+
startPosition: newStartPosition
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}, 50);
|
|
174
|
+
const container = containerRef.current;
|
|
175
|
+
container.addEventListener("scroll", handleScroll);
|
|
176
|
+
return () => {
|
|
177
|
+
container.removeEventListener("scroll", handleScroll);
|
|
178
|
+
handleScroll.cancel();
|
|
179
|
+
};
|
|
180
|
+
}, [containerRef, view.infiniteScrollEnabled]);
|
|
181
|
+
return {
|
|
182
|
+
intersectionObserver: intersectionObserverRef.current
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
export {
|
|
186
|
+
useInfiniteScroll
|
|
187
|
+
};
|
|
188
|
+
//# sourceMappingURL=use-infinite-scroll.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/hooks/use-infinite-scroll.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport {\n\tuseCallback,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseRef,\n} from '@wordpress/element';\nimport { throttle } from '@wordpress/compose';\n\n/**\n * Internal dependencies\n */\nimport type { View } from '../types';\n\n/**\n * Captures an anchor element for scroll position preservation.\n * Finds the element closest to the center of the viewport and stores its position.\n *\n * @param container The scrollable container element.\n * @param anchorElementRef Ref to store the anchor element data.\n * @param direction The scroll direction ('up' or 'down').\n * @return Whether an anchor element was successfully captured.\n */\nfunction captureAnchorElement(\n\tcontainer: HTMLElement,\n\tanchorElementRef: React.MutableRefObject< {\n\t\tposinset: number;\n\t\tviewportOffset: number;\n\t\tdirection: 'up' | 'down' | null;\n\t} | null >,\n\tdirection: 'up' | 'down'\n): boolean {\n\t// Find a visible element to use as anchor - prefer one in the middle of the viewport\n\tconst containerRect = container.getBoundingClientRect();\n\tconst centerY = containerRect.top + containerRect.height / 2;\n\n\t// Query all items with aria-posinset and find the one closest to center\n\tconst items = Array.from( container.querySelectorAll( '[aria-posinset]' ) );\n\n\tif ( items.length === 0 ) {\n\t\treturn false;\n\t}\n\n\t// Find the item closest to the center of the viewport\n\tconst bestAnchor = items.reduce( ( best, item ) => {\n\t\tconst itemRect = item.getBoundingClientRect();\n\t\tconst itemCenterY = itemRect.top + itemRect.height / 2;\n\t\tconst distance = Math.abs( itemCenterY - centerY );\n\n\t\tconst bestRect = best.getBoundingClientRect();\n\t\tconst bestCenterY = bestRect.top + bestRect.height / 2;\n\t\tconst bestDistance = Math.abs( bestCenterY - centerY );\n\n\t\treturn distance < bestDistance ? item : best;\n\t} );\n\n\tconst posinset = Number( bestAnchor.getAttribute( 'aria-posinset' ) );\n\tconst anchorRect = bestAnchor.getBoundingClientRect();\n\tanchorElementRef.current = {\n\t\tposinset,\n\t\tviewportOffset: anchorRect.top - containerRect.top,\n\t\tdirection,\n\t};\n\treturn true;\n}\n\ntype UseInfiniteScrollProps = {\n\tview: View;\n\tonChangeView: ( view: View ) => void;\n\tisLoading: boolean;\n\tpaginationInfo: {\n\t\ttotalItems: number;\n\t\ttotalPages: number;\n\t};\n\tcontainerRef: React.MutableRefObject< HTMLDivElement | null >;\n\tsetVisibleEntries?: React.Dispatch< React.SetStateAction< number[] > >;\n};\n\ntype UseInfiniteScrollResult = {\n\tintersectionObserver?: IntersectionObserver | null;\n};\n\nexport function useInfiniteScroll( {\n\tview,\n\tonChangeView,\n\tisLoading,\n\tpaginationInfo,\n\tcontainerRef,\n\tsetVisibleEntries,\n}: UseInfiniteScrollProps ): UseInfiniteScrollResult {\n\t// Track an anchor element for scroll position preservation\n\t// This approach is robust even when items are added/removed from both ends simultaneously\n\tconst anchorElementRef = useRef< {\n\t\tposinset: number;\n\t\tviewportOffset: number;\n\t\tdirection: 'up' | 'down' | null;\n\t} | null >( null );\n\tconst viewRef = useRef( view );\n\tconst isLoadingRef = useRef( isLoading );\n\tconst onChangeViewRef = useRef( onChangeView );\n\tconst totalItemsRef = useRef( paginationInfo.totalItems );\n\n\tuseLayoutEffect( () => {\n\t\tviewRef.current = view;\n\t\tisLoadingRef.current = isLoading;\n\t\tonChangeViewRef.current = onChangeView;\n\t\ttotalItemsRef.current = paginationInfo.totalItems;\n\t}, [ view, isLoading, onChangeView, paginationInfo.totalItems ] );\n\n\tconst intersectionObserverCallback: IntersectionObserverCallback =\n\t\tuseCallback(\n\t\t\t( entries: IntersectionObserverEntry[] ) => {\n\t\t\t\t// Calculate new visible entries outside of setState\n\t\t\t\tif ( ! setVisibleEntries ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsetVisibleEntries( ( prev: number[] ) => {\n\t\t\t\t\tconst newVisibleEntries = new Set( prev );\n\t\t\t\t\tlet hasChanged = false;\n\n\t\t\t\t\tentries.forEach( ( entry ) => {\n\t\t\t\t\t\tconst posInSet = Number(\n\t\t\t\t\t\t\tentry.target?.attributes?.getNamedItem(\n\t\t\t\t\t\t\t\t'aria-posinset'\n\t\t\t\t\t\t\t)?.value\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif ( isNaN( posInSet ) ) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( entry.isIntersecting ) {\n\t\t\t\t\t\t\tif ( ! newVisibleEntries.has( posInSet ) ) {\n\t\t\t\t\t\t\t\tnewVisibleEntries.add( posInSet );\n\t\t\t\t\t\t\t\thasChanged = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if ( newVisibleEntries.has( posInSet ) ) {\n\t\t\t\t\t\t\tnewVisibleEntries.delete( posInSet );\n\t\t\t\t\t\t\thasChanged = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\n\t\t\t\t\t// Only return new array if something actually changed\n\t\t\t\t\treturn hasChanged\n\t\t\t\t\t\t? Array.from( newVisibleEntries ).sort()\n\t\t\t\t\t\t: prev;\n\t\t\t\t} );\n\t\t\t},\n\t\t\t[ setVisibleEntries ]\n\t\t);\n\n\t// Preserve scroll position when items are added or removed during infinite scroll\n\t// Uses anchor element approach: find the same element after render and restore its viewport position\n\tuseLayoutEffect( () => {\n\t\tconst container = containerRef.current;\n\t\tconst anchor = anchorElementRef.current;\n\n\t\tif (\n\t\t\t! container ||\n\t\t\t! view.infiniteScrollEnabled ||\n\t\t\t! anchor ||\n\t\t\tisLoading\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Find the anchor element by its posinset\n\t\tconst anchorElement = container.querySelector(\n\t\t\t`[aria-posinset=\"${ anchor.posinset }\"]`\n\t\t);\n\n\t\tif ( anchorElement ) {\n\t\t\tconst containerRect = container.getBoundingClientRect();\n\t\t\tconst anchorRect = anchorElement.getBoundingClientRect();\n\t\t\tconst currentOffset = anchorRect.top - containerRect.top;\n\n\t\t\t// Calculate how much the anchor has moved and adjust scroll to compensate\n\t\t\tconst scrollAdjustment = currentOffset - anchor.viewportOffset;\n\n\t\t\tif ( Math.abs( scrollAdjustment ) > 1 ) {\n\t\t\t\tcontainer.scrollTop += scrollAdjustment;\n\t\t\t}\n\t\t}\n\n\t\t// Reset the anchor state now that we've adjusted\n\t\tanchorElementRef.current = null;\n\t}, [ containerRef, isLoading, view.infiniteScrollEnabled ] );\n\n\t// Create and expose a shared IntersectionObserver for provider-level reuse.\n\tconst intersectionObserverRef = useRef< IntersectionObserver | null >(\n\t\tnull\n\t);\n\tuseEffect( () => {\n\t\tif ( ! view.infiniteScrollEnabled || ! intersectionObserverCallback ) {\n\t\t\tif ( intersectionObserverRef.current ) {\n\t\t\t\tintersectionObserverRef.current.disconnect();\n\t\t\t\tintersectionObserverRef.current = null;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tintersectionObserverRef.current = new IntersectionObserver(\n\t\t\tintersectionObserverCallback,\n\t\t\t{ root: null, rootMargin: '0px', threshold: 0.1 }\n\t\t);\n\n\t\treturn () => {\n\t\t\tif ( intersectionObserverRef.current ) {\n\t\t\t\tintersectionObserverRef.current.disconnect();\n\t\t\t\tintersectionObserverRef.current = null;\n\t\t\t}\n\t\t};\n\t}, [ view.infiniteScrollEnabled, intersectionObserverCallback ] );\n\n\t// Attach scroll event listener for infinite scroll\n\tuseEffect( () => {\n\t\tif ( ! view.infiniteScrollEnabled || ! containerRef.current ) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet lastScrollTop = 0;\n\t\t// Use larger thresholds to trigger loading earlier during fast scrolling\n\t\tconst BOTTOM_THRESHOLD = 600; // px from bottom to trigger load\n\t\tconst TOP_THRESHOLD = 800; // px from top to trigger load\n\n\t\tconst handleScroll = throttle( ( event: unknown ) => {\n\t\t\tconst currentView = viewRef.current;\n\t\t\tconst totalItems = totalItemsRef.current;\n\t\t\tconst target = ( event as Event ).target as HTMLElement;\n\t\t\tconst scrollTop = target.scrollTop;\n\t\t\tconst scrollHeight = target.scrollHeight;\n\t\t\tconst clientHeight = target.clientHeight;\n\n\t\t\t// Determine scroll direction\n\t\t\tconst scrollDirection = scrollTop > lastScrollTop ? 'down' : 'up';\n\t\t\tlastScrollTop = scrollTop;\n\n\t\t\t// Don't trigger if already loading\n\t\t\tif ( isLoadingRef.current ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst currentStartPosition = currentView.startPosition || 1;\n\t\t\tconst batchSize = currentView.perPage || 10;\n\t\t\tconst currentEndPosition = Math.min(\n\t\t\t\tcurrentStartPosition + batchSize,\n\t\t\t\ttotalItems\n\t\t\t);\n\n\t\t\t// Check if user has scrolled near the bottom\n\t\t\tif (\n\t\t\t\tscrollDirection === 'down' &&\n\t\t\t\tscrollTop + clientHeight >= scrollHeight - BOTTOM_THRESHOLD\n\t\t\t) {\n\t\t\t\t// Check if there's more data to load\n\t\t\t\tif ( currentEndPosition < totalItems ) {\n\t\t\t\t\tconst newStartPosition = currentEndPosition;\n\n\t\t\t\t\t// Capture anchor element for scroll position preservation\n\t\t\t\t\tcaptureAnchorElement( target, anchorElementRef, 'down' );\n\n\t\t\t\t\tonChangeViewRef.current( {\n\t\t\t\t\t\t...currentView,\n\t\t\t\t\t\tstartPosition: newStartPosition,\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check if user has scrolled near the top\n\t\t\tif ( scrollDirection === 'up' && scrollTop <= TOP_THRESHOLD ) {\n\t\t\t\t// Check if there's more data to load\n\t\t\t\tif ( currentStartPosition > 1 ) {\n\t\t\t\t\t// Round to 1 if we're close to the beginning to avoid tiny batches\n\t\t\t\t\tconst calculatedStartPosition =\n\t\t\t\t\t\tcurrentStartPosition - batchSize;\n\t\t\t\t\tconst newStartPosition =\n\t\t\t\t\t\tcalculatedStartPosition < 6\n\t\t\t\t\t\t\t? 1\n\t\t\t\t\t\t\t: calculatedStartPosition;\n\n\t\t\t\t\t// Capture anchor element for scroll position preservation\n\t\t\t\t\tcaptureAnchorElement( target, anchorElementRef, 'up' );\n\n\t\t\t\t\tonChangeViewRef.current( {\n\t\t\t\t\t\t...currentView,\n\t\t\t\t\t\tstartPosition: newStartPosition,\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\t}, 50 ); // Faster throttle (50ms) for better response to fast scrolling\n\n\t\tconst container = containerRef.current;\n\t\tcontainer.addEventListener( 'scroll', handleScroll );\n\n\t\treturn () => {\n\t\t\tcontainer.removeEventListener( 'scroll', handleScroll );\n\t\t\thandleScroll.cancel(); // Cancel any pending throttled calls\n\t\t};\n\t}, [ containerRef, view.infiniteScrollEnabled ] );\n\n\treturn {\n\t\tintersectionObserver: intersectionObserverRef.current,\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|