@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.
- package/CHANGELOG.md +15 -6
- package/README.md +17 -2
- 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-layouts/grid/composite-grid.cjs +378 -249
- package/build/components/dataviews-layouts/grid/composite-grid.cjs.map +2 -2
- package/build/components/dataviews-layouts/picker-grid/index.cjs +63 -30
- 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/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 +37 -37
- package/build/dataviews/index.cjs.map +3 -3
- package/build/dataviews-picker/index.cjs +25 -24
- 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-layouts/grid/composite-grid.mjs +387 -250
- package/build-module/components/dataviews-layouts/grid/composite-grid.mjs.map +2 -2
- package/build-module/components/dataviews-layouts/picker-grid/index.mjs +67 -31
- 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/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 +45 -39
- package/build-module/dataviews/index.mjs.map +2 -2
- package/build-module/dataviews-picker/index.mjs +33 -26
- 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 +61 -37
- package/build-style/style.css +61 -37
- 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-layouts/grid/composite-grid.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/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/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 +11 -0
- 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/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 +7 -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 +3013 -2613
- 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-layouts/grid/composite-grid.tsx +433 -284
- package/src/components/dataviews-layouts/grid/style.scss +4 -0
- package/src/components/dataviews-layouts/picker-grid/index.tsx +53 -15
- package/src/components/dataviews-layouts/picker-table/index.tsx +42 -22
- 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 +57 -52
- package/src/dataviews/stories/fixtures.tsx +288 -0
- package/src/dataviews/stories/free-composition.tsx +12 -11
- package/src/dataviews/stories/index.story.tsx +19 -2
- package/src/dataviews/stories/infinite-scroll.tsx +12 -92
- package/src/dataviews/stories/with-card.tsx +30 -23
- package/src/dataviews/style.scss +5 -7
- package/src/dataviews/test/dataviews.tsx +21 -9
- package/src/dataviews-picker/index.tsx +40 -34
- 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/types/dataviews.ts +8 -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
|
@@ -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.
|
|
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": ";AAGA,OAAO,mBAAmB;AAK1B,OAAO,gBAAgB;AAKvB,SAAS,2BAA2B;AACpC,OAAO,qBAAqB;AAG5B,SAAS,qBAAsB,QAAQ,IAAK;AAC3C,SAAO,cAAe,MAAM,KAAK,EAAE,YAAY,CAAE;AAClD;AAEA,IAAM,cAAkB,CAAC;AAWV,SAAR,sBACN,MACA,MACA,QAIC;AACD,MAAK,CAAE,MAAO;AACb,WAAO;AAAA,MACN,MAAM;AAAA,MACN,gBAAgB,EAAE,YAAY,GAAG,YAAY,EAAE;AAAA,IAChD;AAAA,EACD;AACA,QAAM,UAAU,gBAAiB,MAAO;AACxC,MAAI,eAAe,CAAE,GAAG,IAAK;AAE7B,MAAK,KAAK,QAAS;AAClB,UAAM,mBAAmB,qBAAsB,KAAK,MAAO;AAC3D,mBAAe,aAAa,OAAQ,CAAE,SAAU;AAC/C,aAAO,QACL,OAAQ,CAAE,UAAW,MAAM,kBAAmB,EAC9C,KAAM,CAAE,UAAW;AACnB,cAAM,aAAa,MAAM,SAAU,EAAE,KAAK,CAAE;AAC5C,cAAM,SAAS,MAAM,QAAS,UAAW,IACtC,aACA,CAAE,UAAW;AAChB,eAAO,OAAO;AAAA,UAAM,CAAE,UACrB,qBAAsB,OAAQ,KAAM,CAAE,EAAE;AAAA,YACvC;AAAA,UACD;AAAA,QACD;AAAA,MACD,CAAE;AAAA,IACJ,CAAE;AAAA,EACH;AAEA,MAAK,KAAK,WAAW,KAAK,SAAS,SAAS,GAAI;AAC/C,SAAK,QAAQ,QAAS,CAAE,WAAY;AACnC,YAAM,QAAQ,QAAQ;AAAA,QACrB,CAAE,WAAY,OAAO,OAAO,OAAO;AAAA,MACpC;AACA,UAAK,OAAQ;AAGZ,YAAK,OAAO,aAAa,qBAAsB;AAC9C,qBAAY,kCAAkC;AAAA,YAC7C,OAAO;AAAA,YACP,aAAa;AAAA,UACd,CAAE;AAAA,QACH;AAEA,cAAM,UAAU,MAAM,OAAQ,OAAO,QAAqB;AAC1D,YAAK,SAAU;AACd,yBAAe,aAAa;AAAA,YAAQ,CAAE,SACrC,QAAS,MAAM,OAAO,OAAO,KAAM;AAAA,UACpC;AAAA,QACD;AAAA,MACD;AAAA,IACD,CAAE;AAAA,EACH;AAGA,QAAM,cAAc,KAAK,MAAM,QAC5B,QAAQ,KAAM,CAAE,UAAW;AAC3B,WACC,MAAM,kBAAkB,SACxB,MAAM,OAAO,KAAK,MAAM;AAAA,EAEzB,CAAE,IACF;AACH,QAAM,eAAe,KAAK,SAAS,QAChC,QAAQ,KAAM,CAAE,UAAW;AAC3B,WACC,MAAM,kBAAkB,SACxB,MAAM,OAAO,KAAK,SAAS;AAAA,EAE5B,CAAE,IACF;AACH,MAAK,eAAe,cAAe;AAClC,iBAAa,KAAM,CAAE,GAAG,MAAO;AAC9B,UAAK,cAAe;AACnB,cAAM,eAAe,aAAa;AAAA,UACjC;AAAA,UACA;AAAA,UACA,KAAK,SAAS,aAAa;AAAA,QAC5B;AAIA,YAAK,iBAAiB,GAAI;AACzB,iBAAO;AAAA,QACR;AAAA,MACD;AAEA,UAAK,aAAc;AAClB,eAAO,YAAY,KAAM,GAAG,GAAG,KAAK,MAAM,aAAa,MAAO;AAAA,MAC/D;AAEA,aAAO;AAAA,IACR,CAAE;AAAA,EACH;AAGA,MAAI,aAAa,aAAa;AAC9B,MAAI,aAAa;
|
|
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
|
|
11
|
+
if (onlyTotalCount || totalItems <= itemsCount) {
|
|
12
12
|
return sprintf(
|
|
13
|
-
/* translators: %
|
|
14
|
-
_n("%
|
|
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",
|
|
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
|
|
5
|
-
"mappings": ";AAGA,SAAS,IAAI,eAAe;
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
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;
|