@wordpress/dataviews 13.1.1-next.v.202603161435.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.
Files changed (183) hide show
  1. package/CHANGELOG.md +15 -6
  2. package/README.md +17 -2
  3. package/build/components/dataform-controls/datetime.cjs +8 -4
  4. package/build/components/dataform-controls/datetime.cjs.map +2 -2
  5. package/build/components/dataform-layouts/card/index.cjs +132 -128
  6. package/build/components/dataform-layouts/card/index.cjs.map +3 -3
  7. package/build/components/dataviews-bulk-actions/index.cjs +28 -5
  8. package/build/components/dataviews-bulk-actions/index.cjs.map +2 -2
  9. package/build/components/dataviews-context/index.cjs +2 -2
  10. package/build/components/dataviews-context/index.cjs.map +2 -2
  11. package/build/components/dataviews-footer/index.cjs +2 -3
  12. package/build/components/dataviews-footer/index.cjs.map +2 -2
  13. package/build/components/dataviews-layouts/grid/composite-grid.cjs +378 -249
  14. package/build/components/dataviews-layouts/grid/composite-grid.cjs.map +2 -2
  15. package/build/components/dataviews-layouts/picker-grid/index.cjs +63 -30
  16. package/build/components/dataviews-layouts/picker-grid/index.cjs.map +2 -2
  17. package/build/components/dataviews-layouts/picker-table/index.cjs +34 -22
  18. package/build/components/dataviews-layouts/picker-table/index.cjs.map +2 -2
  19. package/build/components/dataviews-layouts/utils/use-infinite-scroll.cjs +62 -0
  20. package/build/components/dataviews-layouts/utils/use-infinite-scroll.cjs.map +7 -0
  21. package/build/components/dataviews-picker-footer/index.cjs +23 -4
  22. package/build/components/dataviews-picker-footer/index.cjs.map +2 -2
  23. package/build/components/dataviews-search/index.cjs +2 -1
  24. package/build/components/dataviews-search/index.cjs.map +2 -2
  25. package/build/components/dataviews-selection-checkbox/index.cjs +3 -2
  26. package/build/components/dataviews-selection-checkbox/index.cjs.map +2 -2
  27. package/build/components/dataviews-view-config/index.cjs +0 -2
  28. package/build/components/dataviews-view-config/index.cjs.map +3 -3
  29. package/build/components/dataviews-view-config/infinite-scroll-toggle.cjs +0 -3
  30. package/build/components/dataviews-view-config/infinite-scroll-toggle.cjs.map +2 -2
  31. package/build/dataviews/index.cjs +37 -37
  32. package/build/dataviews/index.cjs.map +3 -3
  33. package/build/dataviews-picker/index.cjs +25 -24
  34. package/build/dataviews-picker/index.cjs.map +3 -3
  35. package/build/hooks/index.cjs +11 -2
  36. package/build/hooks/index.cjs.map +2 -2
  37. package/build/hooks/use-data.cjs +146 -9
  38. package/build/hooks/use-data.cjs.map +2 -2
  39. package/build/hooks/use-infinite-scroll.cjs +208 -0
  40. package/build/hooks/use-infinite-scroll.cjs.map +7 -0
  41. package/build/hooks/use-selected-items.cjs +57 -0
  42. package/build/hooks/use-selected-items.cjs.map +7 -0
  43. package/build/types/dataviews.cjs.map +1 -1
  44. package/build/types/field-api.cjs.map +1 -1
  45. package/build/utils/filter-sort-and-paginate.cjs +5 -1
  46. package/build/utils/filter-sort-and-paginate.cjs.map +2 -2
  47. package/build/utils/get-footer-message.cjs +8 -8
  48. package/build/utils/get-footer-message.cjs.map +2 -2
  49. package/build-module/components/dataform-controls/datetime.mjs +8 -4
  50. package/build-module/components/dataform-controls/datetime.mjs.map +2 -2
  51. package/build-module/components/dataform-layouts/card/index.mjs +132 -133
  52. package/build-module/components/dataform-layouts/card/index.mjs.map +2 -2
  53. package/build-module/components/dataviews-bulk-actions/index.mjs +28 -5
  54. package/build-module/components/dataviews-bulk-actions/index.mjs.map +2 -2
  55. package/build-module/components/dataviews-context/index.mjs +2 -2
  56. package/build-module/components/dataviews-context/index.mjs.map +2 -2
  57. package/build-module/components/dataviews-footer/index.mjs +2 -3
  58. package/build-module/components/dataviews-footer/index.mjs.map +2 -2
  59. package/build-module/components/dataviews-layouts/grid/composite-grid.mjs +387 -250
  60. package/build-module/components/dataviews-layouts/grid/composite-grid.mjs.map +2 -2
  61. package/build-module/components/dataviews-layouts/picker-grid/index.mjs +67 -31
  62. package/build-module/components/dataviews-layouts/picker-grid/index.mjs.map +2 -2
  63. package/build-module/components/dataviews-layouts/picker-table/index.mjs +34 -22
  64. package/build-module/components/dataviews-layouts/picker-table/index.mjs.map +2 -2
  65. package/build-module/components/dataviews-layouts/utils/use-infinite-scroll.mjs +26 -0
  66. package/build-module/components/dataviews-layouts/utils/use-infinite-scroll.mjs.map +7 -0
  67. package/build-module/components/dataviews-picker-footer/index.mjs +23 -4
  68. package/build-module/components/dataviews-picker-footer/index.mjs.map +2 -2
  69. package/build-module/components/dataviews-search/index.mjs +2 -1
  70. package/build-module/components/dataviews-search/index.mjs.map +2 -2
  71. package/build-module/components/dataviews-selection-checkbox/index.mjs +3 -2
  72. package/build-module/components/dataviews-selection-checkbox/index.mjs.map +2 -2
  73. package/build-module/components/dataviews-view-config/index.mjs +0 -2
  74. package/build-module/components/dataviews-view-config/index.mjs.map +2 -2
  75. package/build-module/components/dataviews-view-config/infinite-scroll-toggle.mjs +0 -3
  76. package/build-module/components/dataviews-view-config/infinite-scroll-toggle.mjs.map +2 -2
  77. package/build-module/dataviews/index.mjs +45 -39
  78. package/build-module/dataviews/index.mjs.map +2 -2
  79. package/build-module/dataviews-picker/index.mjs +33 -26
  80. package/build-module/dataviews-picker/index.mjs.map +2 -2
  81. package/build-module/hooks/index.mjs +7 -1
  82. package/build-module/hooks/index.mjs.map +2 -2
  83. package/build-module/hooks/use-data.mjs +147 -10
  84. package/build-module/hooks/use-data.mjs.map +2 -2
  85. package/build-module/hooks/use-infinite-scroll.mjs +188 -0
  86. package/build-module/hooks/use-infinite-scroll.mjs.map +7 -0
  87. package/build-module/hooks/use-selected-items.mjs +36 -0
  88. package/build-module/hooks/use-selected-items.mjs.map +7 -0
  89. package/build-module/utils/filter-sort-and-paginate.mjs +5 -1
  90. package/build-module/utils/filter-sort-and-paginate.mjs.map +2 -2
  91. package/build-module/utils/get-footer-message.mjs +8 -8
  92. package/build-module/utils/get-footer-message.mjs.map +2 -2
  93. package/build-style/style-rtl.css +61 -37
  94. package/build-style/style.css +61 -37
  95. package/build-types/components/dataform-controls/datetime.d.ts +1 -1
  96. package/build-types/components/dataform-controls/datetime.d.ts.map +1 -1
  97. package/build-types/components/dataform-layouts/card/index.d.ts.map +1 -1
  98. package/build-types/components/dataviews-bulk-actions/index.d.ts +2 -1
  99. package/build-types/components/dataviews-bulk-actions/index.d.ts.map +1 -1
  100. package/build-types/components/dataviews-context/index.d.ts +1 -1
  101. package/build-types/components/dataviews-context/index.d.ts.map +1 -1
  102. package/build-types/components/dataviews-footer/index.d.ts.map +1 -1
  103. package/build-types/components/dataviews-layouts/grid/composite-grid.d.ts.map +1 -1
  104. package/build-types/components/dataviews-layouts/picker-grid/index.d.ts.map +1 -1
  105. package/build-types/components/dataviews-layouts/picker-table/index.d.ts.map +1 -1
  106. package/build-types/components/dataviews-layouts/utils/use-infinite-scroll.d.ts +22 -0
  107. package/build-types/components/dataviews-layouts/utils/use-infinite-scroll.d.ts.map +1 -0
  108. package/build-types/components/dataviews-picker-footer/index.d.ts.map +1 -1
  109. package/build-types/components/dataviews-search/index.d.ts.map +1 -1
  110. package/build-types/components/dataviews-selection-checkbox/index.d.ts.map +1 -1
  111. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  112. package/build-types/components/dataviews-view-config/infinite-scroll-toggle.d.ts +1 -1
  113. package/build-types/components/dataviews-view-config/infinite-scroll-toggle.d.ts.map +1 -1
  114. package/build-types/dataviews/index.d.ts +0 -1
  115. package/build-types/dataviews/index.d.ts.map +1 -1
  116. package/build-types/dataviews/stories/fixtures.d.ts.map +1 -1
  117. package/build-types/dataviews/stories/free-composition.d.ts.map +1 -1
  118. package/build-types/dataviews/stories/index.story.d.ts +11 -0
  119. package/build-types/dataviews/stories/index.story.d.ts.map +1 -1
  120. package/build-types/dataviews/stories/infinite-scroll.d.ts.map +1 -1
  121. package/build-types/dataviews/stories/with-card.d.ts.map +1 -1
  122. package/build-types/dataviews-picker/index.d.ts +0 -1
  123. package/build-types/dataviews-picker/index.d.ts.map +1 -1
  124. package/build-types/dataviews-picker/stories/fixtures.d.ts.map +1 -1
  125. package/build-types/dataviews-picker/stories/index.story.d.ts.map +1 -1
  126. package/build-types/field-types/stories/index.story.d.ts.map +1 -1
  127. package/build-types/hooks/index.d.ts +3 -0
  128. package/build-types/hooks/index.d.ts.map +1 -1
  129. package/build-types/hooks/test/use-data.d.ts +2 -0
  130. package/build-types/hooks/test/use-data.d.ts.map +1 -0
  131. package/build-types/hooks/use-data.d.ts +41 -3
  132. package/build-types/hooks/use-data.d.ts.map +1 -1
  133. package/build-types/hooks/use-infinite-scroll.d.ts +21 -0
  134. package/build-types/hooks/use-infinite-scroll.d.ts.map +1 -0
  135. package/build-types/hooks/use-selected-items.d.ts +19 -0
  136. package/build-types/hooks/use-selected-items.d.ts.map +1 -0
  137. package/build-types/types/dataviews.d.ts +7 -1
  138. package/build-types/types/dataviews.d.ts.map +1 -1
  139. package/build-types/types/field-api.d.ts +15 -4
  140. package/build-types/types/field-api.d.ts.map +1 -1
  141. package/build-types/utils/filter-sort-and-paginate.d.ts.map +1 -1
  142. package/build-types/utils/get-footer-message.d.ts +3 -2
  143. package/build-types/utils/get-footer-message.d.ts.map +1 -1
  144. package/build-wp/index.js +3013 -2613
  145. package/package.json +19 -19
  146. package/src/components/dataform-controls/datetime.tsx +19 -11
  147. package/src/components/dataform-layouts/card/index.tsx +171 -146
  148. package/src/components/dataform-layouts/card/style.scss +8 -5
  149. package/src/components/dataviews-bulk-actions/index.tsx +28 -1
  150. package/src/components/dataviews-context/index.ts +2 -2
  151. package/src/components/dataviews-footer/index.tsx +1 -6
  152. package/src/components/dataviews-layouts/grid/composite-grid.tsx +433 -284
  153. package/src/components/dataviews-layouts/grid/style.scss +4 -0
  154. package/src/components/dataviews-layouts/picker-grid/index.tsx +53 -15
  155. package/src/components/dataviews-layouts/picker-table/index.tsx +42 -22
  156. package/src/components/dataviews-layouts/utils/use-infinite-scroll.ts +64 -0
  157. package/src/components/dataviews-picker-footer/index.tsx +21 -1
  158. package/src/components/dataviews-search/index.tsx +2 -1
  159. package/src/components/dataviews-selection-checkbox/index.tsx +4 -2
  160. package/src/components/dataviews-view-config/index.tsx +0 -2
  161. package/src/components/dataviews-view-config/infinite-scroll-toggle.tsx +0 -5
  162. package/src/dataviews/index.tsx +57 -52
  163. package/src/dataviews/stories/fixtures.tsx +288 -0
  164. package/src/dataviews/stories/free-composition.tsx +12 -11
  165. package/src/dataviews/stories/index.story.tsx +19 -2
  166. package/src/dataviews/stories/infinite-scroll.tsx +12 -92
  167. package/src/dataviews/stories/with-card.tsx +30 -23
  168. package/src/dataviews/style.scss +5 -7
  169. package/src/dataviews/test/dataviews.tsx +21 -9
  170. package/src/dataviews-picker/index.tsx +40 -34
  171. package/src/dataviews-picker/stories/fixtures.tsx +270 -0
  172. package/src/dataviews-picker/stories/index.story.tsx +62 -129
  173. package/src/field-types/stories/index.story.tsx +12 -0
  174. package/src/hooks/index.ts +3 -0
  175. package/src/hooks/test/use-data.ts +791 -0
  176. package/src/hooks/use-data.ts +288 -21
  177. package/src/hooks/use-infinite-scroll.ts +304 -0
  178. package/src/hooks/use-selected-items.ts +72 -0
  179. package/src/types/dataviews.ts +8 -1
  180. package/src/types/field-api.ts +16 -3
  181. package/src/utils/filter-sort-and-paginate.ts +13 -1
  182. package/src/utils/get-footer-message.ts +12 -9
  183. package/src/utils/test/filter-sort-and-paginate.js +78 -54
@@ -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
+ }
@@ -0,0 +1,36 @@
1
+ // packages/dataviews/src/hooks/use-selected-items.ts
2
+ import { useMemo, useRef } from "@wordpress/element";
3
+ function useSelectedItems(view, data, selection, getItemId, filterFn) {
4
+ const selectedItemsCacheRef = useRef(/* @__PURE__ */ new Map());
5
+ return useMemo(() => {
6
+ const selectionSet = new Set(selection);
7
+ if (view.infiniteScrollEnabled) {
8
+ data.forEach((item) => {
9
+ const id = getItemId(item);
10
+ if (selectionSet.has(id)) {
11
+ const passesFilter = filterFn ? filterFn(item) : true;
12
+ if (passesFilter) {
13
+ selectedItemsCacheRef.current.set(id, item);
14
+ }
15
+ }
16
+ });
17
+ selectedItemsCacheRef.current.forEach((_, id) => {
18
+ if (!selectionSet.has(id)) {
19
+ selectedItemsCacheRef.current.delete(id);
20
+ }
21
+ });
22
+ return Array.from(selectedItemsCacheRef.current.values());
23
+ }
24
+ return data.filter((item) => {
25
+ const id = getItemId(item);
26
+ if (!selectionSet.has(id)) {
27
+ return false;
28
+ }
29
+ return filterFn ? filterFn(item) : true;
30
+ });
31
+ }, [view.infiniteScrollEnabled, selection, getItemId, data, filterFn]);
32
+ }
33
+ export {
34
+ useSelectedItems as default
35
+ };
36
+ //# sourceMappingURL=use-selected-items.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/hooks/use-selected-items.ts"],
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { useMemo, useRef } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport type { View } from '../types';\n\n/**\n * Hook to get selected items, with support for infinite scroll.\n *\n * When infinite scroll is enabled, items that scroll out of view are cached\n * so they remain available for bulk actions even when not in the current data set.\n *\n * @param view The current view configuration.\n * @param data The current page of data items.\n * @param selection Array of selected item IDs.\n * @param getItemId Function to get the ID of an item.\n * @param filterFn Optional filter function to apply to selected items (e.g., for selectability).\n * @return Array of selected items.\n */\nexport default function useSelectedItems< Item >(\n\tview: View,\n\tdata: Item[],\n\tselection: string[],\n\tgetItemId: ( item: Item ) => string,\n\tfilterFn?: ( item: Item ) => boolean\n): Item[] {\n\t// With infinite scroll enabled items scroll out of view, but we want to keep the selection unaltered,\n\t// unlike page-based navigation where we might clear selection upon navigating to a different page.\n\tconst selectedItemsCacheRef = useRef< Map< string, Item > >( new Map() );\n\n\treturn useMemo( () => {\n\t\tconst selectionSet = new Set( selection );\n\n\t\tif ( view.infiniteScrollEnabled ) {\n\t\t\t// Selection array contains selected item IDs\n\t\t\t// Cache selected items so they remain available when scrolled out of view\n\t\t\tdata.forEach( ( item ) => {\n\t\t\t\tconst id = getItemId( item );\n\t\t\t\tif ( selectionSet.has( id ) ) {\n\t\t\t\t\tconst passesFilter = filterFn ? filterFn( item ) : true;\n\t\t\t\t\tif ( passesFilter ) {\n\t\t\t\t\t\tselectedItemsCacheRef.current.set( id, item );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Remove items from cache that are no longer selected\n\t\t\tselectedItemsCacheRef.current.forEach( ( _, id ) => {\n\t\t\t\tif ( ! selectionSet.has( id ) ) {\n\t\t\t\t\tselectedItemsCacheRef.current.delete( id );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Return all cached selected items\n\t\t\treturn Array.from( selectedItemsCacheRef.current.values() );\n\t\t}\n\n\t\t// Non-infinite scroll mode\n\t\treturn data.filter( ( item ) => {\n\t\t\tconst id = getItemId( item );\n\t\t\tif ( ! selectionSet.has( id ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t// Apply optional filter (e.g., selectability check for bulk actions)\n\t\t\treturn filterFn ? filterFn( item ) : true;\n\t\t} );\n\t}, [ view.infiniteScrollEnabled, selection, getItemId, data, filterFn ] );\n}\n"],
5
+ "mappings": ";AAGA,SAAS,SAAS,cAAc;AAoBjB,SAAR,iBACN,MACA,MACA,WACA,WACA,UACS;AAGT,QAAM,wBAAwB,OAA+B,oBAAI,IAAI,CAAE;AAEvE,SAAO,QAAS,MAAM;AACrB,UAAM,eAAe,IAAI,IAAK,SAAU;AAExC,QAAK,KAAK,uBAAwB;AAGjC,WAAK,QAAS,CAAE,SAAU;AACzB,cAAM,KAAK,UAAW,IAAK;AAC3B,YAAK,aAAa,IAAK,EAAG,GAAI;AAC7B,gBAAM,eAAe,WAAW,SAAU,IAAK,IAAI;AACnD,cAAK,cAAe;AACnB,kCAAsB,QAAQ,IAAK,IAAI,IAAK;AAAA,UAC7C;AAAA,QACD;AAAA,MACD,CAAE;AAGF,4BAAsB,QAAQ,QAAS,CAAE,GAAG,OAAQ;AACnD,YAAK,CAAE,aAAa,IAAK,EAAG,GAAI;AAC/B,gCAAsB,QAAQ,OAAQ,EAAG;AAAA,QAC1C;AAAA,MACD,CAAE;AAGF,aAAO,MAAM,KAAM,sBAAsB,QAAQ,OAAO,CAAE;AAAA,IAC3D;AAGA,WAAO,KAAK,OAAQ,CAAE,SAAU;AAC/B,YAAM,KAAK,UAAW,IAAK;AAC3B,UAAK,CAAE,aAAa,IAAK,EAAG,GAAI;AAC/B,eAAO;AAAA,MACR;AAEA,aAAO,WAAW,SAAU,IAAK,IAAI;AAAA,IACtC,CAAE;AAAA,EACH,GAAG,CAAE,KAAK,uBAAuB,WAAW,WAAW,MAAM,QAAS,CAAE;AACzE;",
6
+ "names": []
7
+ }
@@ -77,7 +77,11 @@ function filterSortAndPaginate(data, view, fields) {
77
77
  }
78
78
  let totalItems = filteredData.length;
79
79
  let totalPages = 1;
80
- if (view.page !== void 0 && view.perPage !== void 0) {
80
+ if (view.infiniteScrollEnabled && view.startPosition !== void 0 && view.perPage !== void 0) {
81
+ const start = view.startPosition - 1;
82
+ const end = Math.min(start + view.perPage, totalItems);
83
+ filteredData = filteredData?.slice(start, end);
84
+ } else if (view.page !== void 0 && view.perPage !== void 0) {
81
85
  const start = (view.page - 1) * view.perPage;
82
86
  totalItems = filteredData?.length || 0;
83
87
  totalPages = Math.ceil(totalItems / view.perPage);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/filter-sort-and-paginate.ts"],
4
- "sourcesContent": ["/**\n * External dependencies\n */\nimport removeAccents from 'remove-accents';\n\n/**\n * WordPress dependencies\n */\nimport deprecated from '@wordpress/deprecated';\n\n/**\n * Internal dependencies\n */\nimport { OPERATOR_IS_NOT_ALL } from '../constants';\nimport normalizeFields from '../field-types';\nimport type { Field, Operator, View } from '../types';\n\nfunction normalizeSearchInput( input = '' ) {\n\treturn removeAccents( input.trim().toLowerCase() );\n}\n\nconst EMPTY_ARRAY: [] = [];\n\n/**\n * Applies the filtering, sorting and pagination to the raw data based on the view configuration.\n *\n * @param data Raw data.\n * @param view View config.\n * @param fields Fields config.\n *\n * @return Filtered, sorted and paginated data.\n */\nexport default function filterSortAndPaginate< Item >(\n\tdata: Item[],\n\tview: View,\n\tfields: Field< Item >[]\n): {\n\tdata: Item[];\n\tpaginationInfo: { totalItems: number; totalPages: number };\n} {\n\tif ( ! data ) {\n\t\treturn {\n\t\t\tdata: EMPTY_ARRAY,\n\t\t\tpaginationInfo: { totalItems: 0, totalPages: 0 },\n\t\t};\n\t}\n\tconst _fields = normalizeFields( fields );\n\tlet filteredData = [ ...data ];\n\t// Handle global search.\n\tif ( view.search ) {\n\t\tconst normalizedSearch = normalizeSearchInput( view.search );\n\t\tfilteredData = filteredData.filter( ( item ) => {\n\t\t\treturn _fields\n\t\t\t\t.filter( ( field ) => field.enableGlobalSearch )\n\t\t\t\t.some( ( field ) => {\n\t\t\t\t\tconst fieldValue = field.getValue( { item } );\n\t\t\t\t\tconst values = Array.isArray( fieldValue )\n\t\t\t\t\t\t? fieldValue\n\t\t\t\t\t\t: [ fieldValue ];\n\t\t\t\t\treturn values.some( ( value ) =>\n\t\t\t\t\t\tnormalizeSearchInput( String( value ) ).includes(\n\t\t\t\t\t\t\tnormalizedSearch\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t} );\n\t\t} );\n\t}\n\n\tif ( view.filters && view.filters?.length > 0 ) {\n\t\tview.filters.forEach( ( filter ) => {\n\t\t\tconst field = _fields.find(\n\t\t\t\t( _field ) => _field.id === filter.field\n\t\t\t);\n\t\t\tif ( field ) {\n\t\t\t\t// Show deprecation warning for `isNotAll` operator.\n\t\t\t\t// We still handle this by mapping it to `isNone` in `getFilter`.\n\t\t\t\tif ( filter.operator === OPERATOR_IS_NOT_ALL ) {\n\t\t\t\t\tdeprecated( \"The 'isNotAll' filter operator\", {\n\t\t\t\t\t\tsince: '7.0',\n\t\t\t\t\t\talternative: \"'isNone'\",\n\t\t\t\t\t} );\n\t\t\t\t}\n\n\t\t\t\tconst handler = field.filter[ filter.operator as Operator ];\n\t\t\t\tif ( handler ) {\n\t\t\t\t\tfilteredData = filteredData.filter( ( item ) =>\n\t\t\t\t\t\thandler( item, field, filter.value )\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t}\n\n\t// Handle sorting.\n\tconst sortByField = view.sort?.field\n\t\t? _fields.find( ( field ) => {\n\t\t\t\treturn (\n\t\t\t\t\tfield.enableSorting !== false &&\n\t\t\t\t\tfield.id === view.sort?.field\n\t\t\t\t);\n\t\t } )\n\t\t: null;\n\tconst groupByField = view.groupBy?.field\n\t\t? _fields.find( ( field ) => {\n\t\t\t\treturn (\n\t\t\t\t\tfield.enableSorting !== false &&\n\t\t\t\t\tfield.id === view.groupBy?.field\n\t\t\t\t);\n\t\t } )\n\t\t: null;\n\tif ( sortByField || groupByField ) {\n\t\tfilteredData.sort( ( a, b ) => {\n\t\t\tif ( groupByField ) {\n\t\t\t\tconst groupCompare = groupByField.sort(\n\t\t\t\t\ta,\n\t\t\t\t\tb,\n\t\t\t\t\tview.groupBy?.direction ?? 'asc'\n\t\t\t\t);\n\n\t\t\t\t// If items are in different groups, return the group comparison result.\n\t\t\t\t// Otherwise, fall back to sorting by the sort field.\n\t\t\t\tif ( groupCompare !== 0 ) {\n\t\t\t\t\treturn groupCompare;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( sortByField ) {\n\t\t\t\treturn sortByField.sort( a, b, view.sort?.direction ?? 'desc' );\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t} );\n\t}\n\n\t// Handle pagination.\n\tlet totalItems = filteredData.length;\n\tlet totalPages = 1;\n\tif ( view.page !== undefined && view.perPage !== undefined ) {\n\t\tconst start = ( view.page - 1 ) * view.perPage;\n\t\ttotalItems = filteredData?.length || 0;\n\t\ttotalPages = Math.ceil( totalItems / view.perPage );\n\t\tfilteredData = filteredData?.slice( start, start + view.perPage );\n\t}\n\n\treturn {\n\t\tdata: filteredData,\n\t\tpaginationInfo: {\n\t\t\ttotalItems,\n\t\t\ttotalPages,\n\t\t},\n\t};\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * External dependencies\n */\nimport removeAccents from 'remove-accents';\n\n/**\n * WordPress dependencies\n */\nimport deprecated from '@wordpress/deprecated';\n\n/**\n * Internal dependencies\n */\nimport { OPERATOR_IS_NOT_ALL } from '../constants';\nimport normalizeFields from '../field-types';\nimport type { Field, Operator, View } from '../types';\n\nfunction normalizeSearchInput( input = '' ) {\n\treturn removeAccents( input.trim().toLowerCase() );\n}\n\nconst EMPTY_ARRAY: [] = [];\n\n/**\n * Applies the filtering, sorting and pagination to the raw data based on the view configuration.\n *\n * @param data Raw data.\n * @param view View config.\n * @param fields Fields config.\n *\n * @return Filtered, sorted and paginated data.\n */\nexport default function filterSortAndPaginate< Item >(\n\tdata: Item[],\n\tview: View,\n\tfields: Field< Item >[]\n): {\n\tdata: Item[];\n\tpaginationInfo: { totalItems: number; totalPages: number };\n} {\n\tif ( ! data ) {\n\t\treturn {\n\t\t\tdata: EMPTY_ARRAY,\n\t\t\tpaginationInfo: { totalItems: 0, totalPages: 0 },\n\t\t};\n\t}\n\tconst _fields = normalizeFields( fields );\n\tlet filteredData = [ ...data ];\n\t// Handle global search.\n\tif ( view.search ) {\n\t\tconst normalizedSearch = normalizeSearchInput( view.search );\n\t\tfilteredData = filteredData.filter( ( item ) => {\n\t\t\treturn _fields\n\t\t\t\t.filter( ( field ) => field.enableGlobalSearch )\n\t\t\t\t.some( ( field ) => {\n\t\t\t\t\tconst fieldValue = field.getValue( { item } );\n\t\t\t\t\tconst values = Array.isArray( fieldValue )\n\t\t\t\t\t\t? fieldValue\n\t\t\t\t\t\t: [ fieldValue ];\n\t\t\t\t\treturn values.some( ( value ) =>\n\t\t\t\t\t\tnormalizeSearchInput( String( value ) ).includes(\n\t\t\t\t\t\t\tnormalizedSearch\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t} );\n\t\t} );\n\t}\n\n\tif ( view.filters && view.filters?.length > 0 ) {\n\t\tview.filters.forEach( ( filter ) => {\n\t\t\tconst field = _fields.find(\n\t\t\t\t( _field ) => _field.id === filter.field\n\t\t\t);\n\t\t\tif ( field ) {\n\t\t\t\t// Show deprecation warning for `isNotAll` operator.\n\t\t\t\t// We still handle this by mapping it to `isNone` in `getFilter`.\n\t\t\t\tif ( filter.operator === OPERATOR_IS_NOT_ALL ) {\n\t\t\t\t\tdeprecated( \"The 'isNotAll' filter operator\", {\n\t\t\t\t\t\tsince: '7.0',\n\t\t\t\t\t\talternative: \"'isNone'\",\n\t\t\t\t\t} );\n\t\t\t\t}\n\n\t\t\t\tconst handler = field.filter[ filter.operator as Operator ];\n\t\t\t\tif ( handler ) {\n\t\t\t\t\tfilteredData = filteredData.filter( ( item ) =>\n\t\t\t\t\t\thandler( item, field, filter.value )\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t}\n\n\t// Handle sorting.\n\tconst sortByField = view.sort?.field\n\t\t? _fields.find( ( field ) => {\n\t\t\t\treturn (\n\t\t\t\t\tfield.enableSorting !== false &&\n\t\t\t\t\tfield.id === view.sort?.field\n\t\t\t\t);\n\t\t } )\n\t\t: null;\n\tconst groupByField = view.groupBy?.field\n\t\t? _fields.find( ( field ) => {\n\t\t\t\treturn (\n\t\t\t\t\tfield.enableSorting !== false &&\n\t\t\t\t\tfield.id === view.groupBy?.field\n\t\t\t\t);\n\t\t } )\n\t\t: null;\n\tif ( sortByField || groupByField ) {\n\t\tfilteredData.sort( ( a, b ) => {\n\t\t\tif ( groupByField ) {\n\t\t\t\tconst groupCompare = groupByField.sort(\n\t\t\t\t\ta,\n\t\t\t\t\tb,\n\t\t\t\t\tview.groupBy?.direction ?? 'asc'\n\t\t\t\t);\n\n\t\t\t\t// If items are in different groups, return the group comparison result.\n\t\t\t\t// Otherwise, fall back to sorting by the sort field.\n\t\t\t\tif ( groupCompare !== 0 ) {\n\t\t\t\t\treturn groupCompare;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( sortByField ) {\n\t\t\t\treturn sortByField.sort( a, b, view.sort?.direction ?? 'desc' );\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t} );\n\t}\n\n\t// Handle pagination.\n\tlet totalItems = filteredData.length;\n\tlet totalPages = 1;\n\n\t// Use position-based pagination for infinite scroll\n\tif (\n\t\tview.infiniteScrollEnabled &&\n\t\tview.startPosition !== undefined &&\n\t\tview.perPage !== undefined\n\t) {\n\t\t// Convert 1-indexed positions to 0-indexed array indices\n\t\tconst start = view.startPosition - 1;\n\t\tconst end = Math.min( start + view.perPage, totalItems );\n\t\tfilteredData = filteredData?.slice( start, end );\n\t} else if ( view.page !== undefined && view.perPage !== undefined ) {\n\t\t// Use traditional page-based pagination\n\t\tconst start = ( view.page - 1 ) * view.perPage;\n\t\ttotalItems = filteredData?.length || 0;\n\t\ttotalPages = Math.ceil( totalItems / view.perPage );\n\t\tfilteredData = filteredData?.slice( start, start + view.perPage );\n\t}\n\n\treturn {\n\t\tdata: filteredData,\n\t\tpaginationInfo: {\n\t\t\ttotalItems,\n\t\t\ttotalPages,\n\t\t},\n\t};\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  // packages/dataviews/src/utils/get-footer-message.ts
2
2
  import { _n, sprintf } from "@wordpress/i18n";
3
- function getFooterMessage(selectionCount, itemsCount, totalItems) {
3
+ function getFooterMessage(selectionCount, itemsCount, totalItems, onlyTotalCount = false) {
4
4
  if (selectionCount > 0) {
5
5
  return sprintf(
6
6
  /* translators: %d: number of items. */
@@ -8,18 +8,18 @@ function getFooterMessage(selectionCount, itemsCount, totalItems) {
8
8
  selectionCount
9
9
  );
10
10
  }
11
- if (totalItems > itemsCount) {
11
+ if (onlyTotalCount || totalItems <= itemsCount) {
12
12
  return sprintf(
13
- /* translators: %1$d: number of items. %2$d: total number of items. */
14
- _n("%1$d of %2$d Item", "%1$d of %2$d Items", totalItems),
15
- itemsCount,
13
+ /* translators: %d: number of items. */
14
+ _n("%d Item", "%d Items", totalItems),
16
15
  totalItems
17
16
  );
18
17
  }
19
18
  return sprintf(
20
- /* translators: %d: number of items. */
21
- _n("%d Item", "%d Items", itemsCount),
22
- itemsCount
19
+ /* translators: %1$d: number of items. %2$d: total number of items. */
20
+ _n("%1$d of %2$d Item", "%1$d of %2$d Items", totalItems),
21
+ itemsCount,
22
+ totalItems
23
23
  );
24
24
  }
25
25
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/get-footer-message.ts"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { _n, sprintf } from '@wordpress/i18n';\n\n/**\n * Get the footer message for the DataViews footer.\n *\n * @param selectionCount - The number of items selected.\n * @param itemsCount - The number of items in the current page.\n * @param totalItems - The total number of items.\n * @return - The footer message.\n */\nexport default function getFooterMessage(\n\tselectionCount: number,\n\titemsCount: number,\n\ttotalItems: number\n): string {\n\tif ( selectionCount > 0 ) {\n\t\treturn sprintf(\n\t\t\t/* translators: %d: number of items. */\n\t\t\t_n( '%d Item selected', '%d Items selected', selectionCount ),\n\t\t\tselectionCount\n\t\t);\n\t}\n\n\tif ( totalItems > itemsCount ) {\n\t\treturn sprintf(\n\t\t\t/* translators: %1$d: number of items. %2$d: total number of items. */\n\t\t\t_n( '%1$d of %2$d Item', '%1$d of %2$d Items', totalItems ),\n\t\t\titemsCount,\n\t\t\ttotalItems\n\t\t);\n\t}\n\n\treturn sprintf(\n\t\t/* translators: %d: number of items. */\n\t\t_n( '%d Item', '%d Items', itemsCount ),\n\t\titemsCount\n\t);\n}\n"],
5
- "mappings": ";AAGA,SAAS,IAAI,eAAe;AAUb,SAAR,iBACN,gBACA,YACA,YACS;AACT,MAAK,iBAAiB,GAAI;AACzB,WAAO;AAAA;AAAA,MAEN,GAAI,oBAAoB,qBAAqB,cAAe;AAAA,MAC5D;AAAA,IACD;AAAA,EACD;AAEA,MAAK,aAAa,YAAa;AAC9B,WAAO;AAAA;AAAA,MAEN,GAAI,qBAAqB,sBAAsB,UAAW;AAAA,MAC1D;AAAA,MACA;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AAAA;AAAA,IAEN,GAAI,WAAW,YAAY,UAAW;AAAA,IACtC;AAAA,EACD;AACD;",
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { _n, sprintf } from '@wordpress/i18n';\n\n/**\n * Get the footer message for the DataViews footer.\n *\n * @param selectionCount - The number of selected items.\n * @param itemsCount - The number of items in the current page.\n * @param totalItems - The total number of items.\n * @param onlyTotalCount - Whether to only show the total count (used with infinite scroll).\n * @return - The footer message.\n */\nexport default function getFooterMessage(\n\tselectionCount: number,\n\titemsCount: number,\n\ttotalItems: number,\n\tonlyTotalCount = false\n): string {\n\tif ( selectionCount > 0 ) {\n\t\treturn sprintf(\n\t\t\t/* translators: %d: number of items. */\n\t\t\t_n( '%d Item selected', '%d Items selected', selectionCount ),\n\t\t\tselectionCount\n\t\t);\n\t}\n\n\t// No selection - show item count\n\tif ( onlyTotalCount || totalItems <= itemsCount ) {\n\t\treturn sprintf(\n\t\t\t/* translators: %d: number of items. */\n\t\t\t_n( '%d Item', '%d Items', totalItems ),\n\t\t\ttotalItems\n\t\t);\n\t}\n\n\treturn sprintf(\n\t\t/* translators: %1$d: number of items. %2$d: total number of items. */\n\t\t_n( '%1$d of %2$d Item', '%1$d of %2$d Items', totalItems ),\n\t\titemsCount,\n\t\ttotalItems\n\t);\n}\n"],
5
+ "mappings": ";AAGA,SAAS,IAAI,eAAe;AAWb,SAAR,iBACN,gBACA,YACA,YACA,iBAAiB,OACR;AACT,MAAK,iBAAiB,GAAI;AACzB,WAAO;AAAA;AAAA,MAEN,GAAI,oBAAoB,qBAAqB,cAAe;AAAA,MAC5D;AAAA,IACD;AAAA,EACD;AAGA,MAAK,kBAAkB,cAAc,YAAa;AACjD,WAAO;AAAA;AAAA,MAEN,GAAI,WAAW,YAAY,UAAW;AAAA,MACtC;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AAAA;AAAA,IAEN,GAAI,qBAAqB,sBAAsB,UAAW;AAAA,IAC1D;AAAA,IACA;AAAA,EACD;AACD;",
6
6
  "names": []
7
7
  }
@@ -213,13 +213,11 @@
213
213
  border-radius: 2px;
214
214
  }
215
215
 
216
- /**
217
- * When DataViews are placed within cards, apply a consistent top padding.
218
- */
219
- .components-card__body:has(> .dataviews-wrapper),
220
- .components-card__body:has(> .dataviews-picker-wrapper) {
221
- padding: 8px 0 0;
222
- overflow: hidden;
216
+ .dataviews__view-actions--infinite-scroll {
217
+ position: sticky;
218
+ top: 0;
219
+ z-index: 2;
220
+ background-color: #fff;
223
221
  }
224
222
 
225
223
  .dataviews-bulk-actions-footer__item-count {
@@ -976,37 +974,48 @@
976
974
  pointer-events: none;
977
975
  outline: 2px solid transparent;
978
976
  }
979
- .dataviews-view-grid .dataviews-view-grid__card {
977
+
978
+ .dataviews-view-grid .dataviews-view-grid__card,
979
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__card {
980
980
  height: 100%;
981
981
  justify-content: flex-start;
982
982
  position: relative;
983
983
  }
984
- .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__title {
984
+ .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__title,
985
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__card .dataviews-view-grid__title {
985
986
  padding: 8px 0 4px;
986
987
  }
987
- .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__title-field {
988
+ .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__title-field,
989
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__card .dataviews-view-grid__title-field {
988
990
  min-height: 24px;
989
991
  overflow: hidden;
990
992
  align-content: center;
991
993
  text-align: start;
992
994
  }
993
- .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__title-field--clickable {
995
+ .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__title-field--clickable,
996
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__card .dataviews-view-grid__title-field--clickable {
994
997
  width: fit-content;
995
998
  }
996
- .dataviews-view-grid .dataviews-view-grid__card.is-selected .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-value {
999
+ .dataviews-view-grid .dataviews-view-grid__card.is-selected .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-value,
1000
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__card.is-selected .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-value {
997
1001
  color: #1e1e1e;
998
1002
  }
999
1003
  .dataviews-view-grid .dataviews-view-grid__card.is-selected .dataviews-view-grid__media::after,
1000
- .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__media:focus::after {
1004
+ .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__media:focus::after,
1005
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__card.is-selected .dataviews-view-grid__media::after,
1006
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__card .dataviews-view-grid__media:focus::after {
1001
1007
  background-color: rgba(var(--wp-admin-theme-color--rgb), 0.08);
1002
1008
  }
1003
- .dataviews-view-grid .dataviews-view-grid__card.is-selected .dataviews-view-grid__media::after {
1009
+ .dataviews-view-grid .dataviews-view-grid__card.is-selected .dataviews-view-grid__media::after,
1010
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__card.is-selected .dataviews-view-grid__media::after {
1004
1011
  box-shadow: inset 0 0 0 1px var(--wp-admin-theme-color);
1005
1012
  }
1006
- .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__media:focus::after {
1013
+ .dataviews-view-grid .dataviews-view-grid__card .dataviews-view-grid__media:focus::after,
1014
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__card .dataviews-view-grid__media:focus::after {
1007
1015
  box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
1008
1016
  }
1009
- .dataviews-view-grid .dataviews-view-grid__media {
1017
+ .dataviews-view-grid .dataviews-view-grid__media,
1018
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__media {
1010
1019
  width: 100%;
1011
1020
  aspect-ratio: 1/1;
1012
1021
  background-color: #fff;
@@ -1014,15 +1023,18 @@
1014
1023
  overflow: hidden;
1015
1024
  position: relative;
1016
1025
  }
1017
- .dataviews-view-grid .dataviews-view-grid__media.dataviews-view-grid__media--placeholder {
1026
+ .dataviews-view-grid .dataviews-view-grid__media.dataviews-view-grid__media--placeholder,
1027
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__media.dataviews-view-grid__media--placeholder {
1018
1028
  aspect-ratio: 3/1;
1019
1029
  }
1020
- .dataviews-view-grid .dataviews-view-grid__media img {
1030
+ .dataviews-view-grid .dataviews-view-grid__media img,
1031
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__media img {
1021
1032
  object-fit: cover;
1022
1033
  width: 100%;
1023
1034
  height: 100%;
1024
1035
  }
1025
- .dataviews-view-grid .dataviews-view-grid__media::after {
1036
+ .dataviews-view-grid .dataviews-view-grid__media::after,
1037
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__media::after {
1026
1038
  content: "";
1027
1039
  position: absolute;
1028
1040
  top: 0;
@@ -1033,7 +1045,8 @@
1033
1045
  border-radius: 4px;
1034
1046
  pointer-events: none;
1035
1047
  }
1036
- .dataviews-view-grid .dataviews-view-grid__media .dataviews-view-grid__media-placeholder {
1048
+ .dataviews-view-grid .dataviews-view-grid__media .dataviews-view-grid__media-placeholder,
1049
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__media .dataviews-view-grid__media-placeholder {
1037
1050
  width: 100%;
1038
1051
  height: 100%;
1039
1052
  display: block;
@@ -1041,48 +1054,58 @@
1041
1054
  box-shadow: none;
1042
1055
  background: #f0f0f0;
1043
1056
  }
1044
- .dataviews-view-grid .dataviews-view-grid__fields {
1057
+ .dataviews-view-grid .dataviews-view-grid__fields,
1058
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__fields {
1045
1059
  position: relative;
1046
1060
  font-size: 12px;
1047
1061
  line-height: 16px;
1048
1062
  }
1049
- .dataviews-view-grid .dataviews-view-grid__fields:not(:empty) {
1063
+ .dataviews-view-grid .dataviews-view-grid__fields:not(:empty),
1064
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__fields:not(:empty) {
1050
1065
  padding: 0 0 12px;
1051
1066
  }
1052
- .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field-value:not(:empty) {
1067
+ .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field-value:not(:empty),
1068
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__fields .dataviews-view-grid__field-value:not(:empty) {
1053
1069
  min-height: 24px;
1054
1070
  line-height: 20px;
1055
1071
  padding-top: 2px;
1056
1072
  }
1057
- .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field {
1073
+ .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field,
1074
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__fields .dataviews-view-grid__field {
1058
1075
  min-height: 24px;
1059
1076
  align-items: center;
1060
1077
  }
1061
- .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-name {
1078
+ .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-name,
1079
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-name {
1062
1080
  width: 35%;
1063
1081
  color: #757575;
1064
1082
  overflow: hidden;
1065
1083
  text-overflow: ellipsis;
1066
1084
  white-space: nowrap;
1067
1085
  }
1068
- .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-value {
1086
+ .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-value,
1087
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__fields .dataviews-view-grid__field .dataviews-view-grid__field-value {
1069
1088
  width: 65%;
1070
1089
  overflow: hidden;
1071
1090
  text-overflow: ellipsis;
1072
1091
  white-space: nowrap;
1073
1092
  }
1074
- .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field:not(:has(.dataviews-view-grid__field-value:not(:empty))) {
1093
+ .dataviews-view-grid .dataviews-view-grid__fields .dataviews-view-grid__field:not(:has(.dataviews-view-grid__field-value:not(:empty))),
1094
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__fields .dataviews-view-grid__field:not(:has(.dataviews-view-grid__field-value:not(:empty))) {
1075
1095
  display: none;
1076
1096
  }
1077
- .dataviews-view-grid .dataviews-view-grid__badge-fields:not(:empty) {
1097
+ .dataviews-view-grid .dataviews-view-grid__badge-fields:not(:empty),
1098
+ .dataviews-view-grid-infinite-scroll .dataviews-view-grid__badge-fields:not(:empty) {
1078
1099
  padding-bottom: 12px;
1079
1100
  }
1080
- .dataviews-view-grid.is-refreshing {
1101
+ .dataviews-view-grid.is-refreshing,
1102
+ .dataviews-view-grid-infinite-scroll.is-refreshing {
1081
1103
  opacity: 0.5;
1082
1104
  pointer-events: none;
1083
1105
  }
1084
1106
  @media not (prefers-reduced-motion) {
1085
- .dataviews-view-grid.is-refreshing {
1107
+ .dataviews-view-grid.is-refreshing,
1108
+ .dataviews-view-grid-infinite-scroll.is-refreshing {
1086
1109
  opacity: 1;
1087
1110
  animation: dataviews-pulse 1s ease-in-out infinite;
1088
1111
  }
@@ -2233,17 +2256,18 @@ div.dataviews-view-list {
2233
2256
  align-items: center;
2234
2257
  }
2235
2258
 
2236
- .dataforms-layouts-card__field-header-label {
2237
- font-family: -apple-system, "system-ui", "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
2238
- font-weight: 499;
2239
- font-size: 15px;
2240
- line-height: 20px;
2241
- }
2242
-
2243
2259
  .dataforms-layouts-card__field {
2244
2260
  width: 100%;
2245
2261
  }
2246
2262
 
2263
+ .dataforms-layouts-card__field-header-content {
2264
+ min-height: 24px;
2265
+ }
2266
+
2267
+ .dataforms-layouts-card__field-header-content-description {
2268
+ display: contents;
2269
+ }
2270
+
2247
2271
  .dataforms-layouts-card__field-description {
2248
2272
  color: #757575;
2249
2273
  display: block;