virtua 0.47.2 → 0.48.1
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/lib/core/index.cjs +1 -1
- package/lib/core/index.cjs.map +1 -1
- package/lib/core/index.d.ts +1 -1
- package/lib/core/index.js +1 -1
- package/lib/core/index.js.map +1 -1
- package/lib/index.cjs +1 -1
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/react/VList.d.ts +0 -4
- package/lib/solid/index.cjs +1 -1
- package/lib/solid/index.cjs.map +1 -1
- package/lib/solid/index.js +1 -1
- package/lib/solid/index.js.map +1 -1
- package/lib/solid/index.jsx +69 -44
- package/lib/solid/index.jsx.map +1 -1
- package/lib/svelte/ListItem.svelte +2 -3
- package/lib/svelte/Virtualizer.svelte +25 -3
- package/lib/svelte/WindowVirtualizer.svelte +14 -3
- package/lib/svelte/utils.d.ts +0 -2
- package/lib/svelte/utils.js +1 -25
- package/lib/svelte/utils.js.map +1 -1
- package/lib/vue/index.cjs +1 -1
- package/lib/vue/index.cjs.map +1 -1
- package/lib/vue/index.js +1 -1
- package/lib/vue/index.js.map +1 -1
- package/package.json +1 -1
package/lib/solid/index.jsx
CHANGED
|
@@ -212,24 +212,18 @@ const updateCacheLength = (cache, length, isShift) => {
|
|
|
212
212
|
* @internal
|
|
213
213
|
*/
|
|
214
214
|
const isBrowser = typeof window !== "undefined";
|
|
215
|
-
const getDocumentElement = () => document.documentElement;
|
|
216
215
|
/**
|
|
217
216
|
* @internal
|
|
218
217
|
*/
|
|
219
|
-
const
|
|
218
|
+
const getDocumentElement = (doc) => doc.documentElement;
|
|
220
219
|
/**
|
|
221
220
|
* @internal
|
|
222
221
|
*/
|
|
223
|
-
const
|
|
222
|
+
const getCurrentDocument = (node) => node.ownerDocument;
|
|
224
223
|
/**
|
|
225
224
|
* @internal
|
|
226
225
|
*/
|
|
227
|
-
const
|
|
228
|
-
// TODO support SSR in rtl
|
|
229
|
-
return isBrowser
|
|
230
|
-
? getComputedStyle(getDocumentElement()).direction === "rtl"
|
|
231
|
-
: false;
|
|
232
|
-
});
|
|
226
|
+
const getCurrentWindow = (doc) => doc.defaultView;
|
|
233
227
|
/**
|
|
234
228
|
* Currently, all browsers on iOS/iPadOS are WebKit, including WebView.
|
|
235
229
|
* @internal
|
|
@@ -248,7 +242,7 @@ const isIOSWebKit = /*#__PURE__*/ once(() => {
|
|
|
248
242
|
* @internal
|
|
249
243
|
*/
|
|
250
244
|
const isSmoothScrollSupported = /*#__PURE__*/ once(() => {
|
|
251
|
-
return "scrollBehavior" in getDocumentElement().style;
|
|
245
|
+
return "scrollBehavior" in getDocumentElement(document).style;
|
|
252
246
|
});
|
|
253
247
|
|
|
254
248
|
const MAX_INT_32 = 0x7fffffff;
|
|
@@ -316,8 +310,12 @@ const createVirtualStore = (elementsCount, itemSize = 40, ssrCount = 0, cacheSna
|
|
|
316
310
|
return computeRange(cache, startOffset, endOffset, _prevRange[0]);
|
|
317
311
|
};
|
|
318
312
|
const getTotalSize = () => getItemOffset(cache, cache._length);
|
|
319
|
-
const getItemOffset$1 = (index) => {
|
|
320
|
-
|
|
313
|
+
const getItemOffset$1 = (index, fromEnd) => {
|
|
314
|
+
const offset = getItemOffset(cache, index) - pendingJump;
|
|
315
|
+
if (fromEnd) {
|
|
316
|
+
return getTotalSize() - offset - getItemSize$1(index);
|
|
317
|
+
}
|
|
318
|
+
return offset;
|
|
321
319
|
};
|
|
322
320
|
const getItemSize$1 = (index) => {
|
|
323
321
|
return getItemSize(cache, index);
|
|
@@ -598,20 +596,18 @@ const debounce = (fn, ms) => {
|
|
|
598
596
|
return debouncedFn;
|
|
599
597
|
};
|
|
600
598
|
/**
|
|
601
|
-
* scrollLeft
|
|
599
|
+
* scrollTop/scrollLeft can be negative value under certain styles.
|
|
600
|
+
* - direction: rtl https://github.com/othree/jquery.rtl-scroll-type
|
|
601
|
+
* - writing-mode https://people.igalia.com/fwang/scrollable-elements-in-non-default-writing-modes/
|
|
602
|
+
* - flex-direction: column-reverse/row-reverse
|
|
602
603
|
*
|
|
603
|
-
* left
|
|
604
|
-
* 0
|
|
605
|
-
* -100
|
|
606
|
-
* https://
|
|
604
|
+
* top/left bottom/right
|
|
605
|
+
* 0 100 spec compliant bottom/right overflow, or possibly top/left overflow in Chrome earlier than v85
|
|
606
|
+
* -100 0 spec compliant top/left overflow
|
|
607
|
+
* https://drafts.csswg.org/cssom-view/#scroll-an-element
|
|
607
608
|
*/
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
return -offset;
|
|
611
|
-
}
|
|
612
|
-
else {
|
|
613
|
-
return offset;
|
|
614
|
-
}
|
|
609
|
+
const normalizeScrollOffset = (offset, isNegative) => {
|
|
610
|
+
return isNegative ? -offset : offset;
|
|
615
611
|
};
|
|
616
612
|
const createScrollObserver = (store, viewport, isHorizontal, getScrollOffset, updateScrollOffset, getStartOffset) => {
|
|
617
613
|
const now = Date.now;
|
|
@@ -795,10 +791,11 @@ const createScroller = (store, isHorizontal) => {
|
|
|
795
791
|
let viewportElement;
|
|
796
792
|
let scrollObserver;
|
|
797
793
|
let initialized = createPromise();
|
|
794
|
+
let isNegative = false;
|
|
798
795
|
const scrollOffsetKey = isHorizontal ? "scrollLeft" : "scrollTop";
|
|
799
796
|
const overflowKey = isHorizontal ? "overflowX" : "overflowY";
|
|
800
797
|
const [scheduleScroll, cancelScroll] = createScrollScheduler(store, () => initialized[0], (offset, smooth) => {
|
|
801
|
-
offset =
|
|
798
|
+
offset = normalizeScrollOffset(offset, isNegative);
|
|
802
799
|
if (smooth) {
|
|
803
800
|
viewportElement.scrollTo({
|
|
804
801
|
[isHorizontal ? "left" : "top"]: offset,
|
|
@@ -812,7 +809,25 @@ const createScroller = (store, isHorizontal) => {
|
|
|
812
809
|
return {
|
|
813
810
|
$observe(viewport) {
|
|
814
811
|
viewportElement = viewport;
|
|
815
|
-
|
|
812
|
+
const clean = store.$subscribe(UPDATE_SIZE_EVENT, () => {
|
|
813
|
+
const viewportSize = store.$getViewportSize();
|
|
814
|
+
if (viewportSize) {
|
|
815
|
+
const prev = viewport[scrollOffsetKey];
|
|
816
|
+
// Detect overflowed direction after the initial viewport measurement
|
|
817
|
+
const dummy = getCurrentDocument(viewport).createElement("div");
|
|
818
|
+
dummy.style.cssText = `visibility:hidden;min-${isHorizontal ? "width" : "height"}:${
|
|
819
|
+
// Set larger value than the viewportSize to force overflow. And the value must take subpixels into account.
|
|
820
|
+
viewportSize + 9}px`;
|
|
821
|
+
viewport.appendChild(dummy);
|
|
822
|
+
viewport[scrollOffsetKey] = 1;
|
|
823
|
+
// It can be positive under some specific situations even if negative mode, so we use `<` for now.
|
|
824
|
+
isNegative = viewport[scrollOffsetKey] < 1;
|
|
825
|
+
viewport.removeChild(dummy);
|
|
826
|
+
viewport[scrollOffsetKey] = prev;
|
|
827
|
+
clean();
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
scrollObserver = createScrollObserver(store, viewport, isHorizontal, () => normalizeScrollOffset(viewport[scrollOffsetKey], isNegative), (jump, shift, isMomentumScrolling) => {
|
|
816
831
|
// If we update scroll position while touching on iOS, the position will be reverted.
|
|
817
832
|
// However iOS WebKit fires touch events only once at the beginning of momentum scrolling.
|
|
818
833
|
// That means we have no reliable way to confirm still touched or not if user touches more than once during momentum scrolling...
|
|
@@ -827,7 +842,7 @@ const createScroller = (store, isHorizontal) => {
|
|
|
827
842
|
}
|
|
828
843
|
// Use absolute position not to exceed scrollable bounds
|
|
829
844
|
// https://github.com/inokawa/virtua/discussions/475
|
|
830
|
-
viewport[scrollOffsetKey] =
|
|
845
|
+
viewport[scrollOffsetKey] = normalizeScrollOffset(store.$getScrollOffset() + jump, isNegative);
|
|
831
846
|
if (shift) {
|
|
832
847
|
// https://github.com/inokawa/virtua/issues/357
|
|
833
848
|
cancelScroll();
|
|
@@ -841,6 +856,7 @@ const createScroller = (store, isHorizontal) => {
|
|
|
841
856
|
// https://github.com/inokawa/virtua/pull/765
|
|
842
857
|
initialized = createPromise();
|
|
843
858
|
},
|
|
859
|
+
$isNegative: () => isNegative,
|
|
844
860
|
$scrollTo(offset) {
|
|
845
861
|
scheduleScroll(() => offset);
|
|
846
862
|
},
|
|
@@ -888,9 +904,10 @@ const createWindowScroller = (store, isHorizontal) => {
|
|
|
888
904
|
let containerElement;
|
|
889
905
|
let scrollObserver;
|
|
890
906
|
let initialized = createPromise();
|
|
907
|
+
let isNegative = false;
|
|
891
908
|
const scrollToKey = isHorizontal ? "left" : "top";
|
|
892
909
|
const [scheduleScroll] = createScrollScheduler(store, () => initialized[0], (offset, smooth) => {
|
|
893
|
-
offset =
|
|
910
|
+
offset = normalizeScrollOffset(offset, isNegative);
|
|
894
911
|
const window = getCurrentWindow(getCurrentDocument(containerElement));
|
|
895
912
|
if (smooth) {
|
|
896
913
|
window.scroll({
|
|
@@ -908,7 +925,7 @@ const createWindowScroller = (store, isHorizontal) => {
|
|
|
908
925
|
// TODO calc offset only when it changes (maybe impossible)
|
|
909
926
|
const offsetKey = isHorizontal ? "offsetLeft" : "offsetTop";
|
|
910
927
|
const offsetSum = offset +
|
|
911
|
-
(isHorizontal &&
|
|
928
|
+
(isHorizontal && isNegative
|
|
912
929
|
? window.innerWidth - node[offsetKey] - node.offsetWidth
|
|
913
930
|
: node[offsetKey]);
|
|
914
931
|
const parent = node.offsetParent;
|
|
@@ -923,18 +940,23 @@ const createWindowScroller = (store, isHorizontal) => {
|
|
|
923
940
|
const scrollOffsetKey = isHorizontal ? "scrollX" : "scrollY";
|
|
924
941
|
const document = getCurrentDocument(container);
|
|
925
942
|
const window = getCurrentWindow(document);
|
|
926
|
-
|
|
943
|
+
if (isHorizontal) {
|
|
944
|
+
// Detect RTL document
|
|
945
|
+
isNegative =
|
|
946
|
+
getComputedStyle(getDocumentElement(document)).direction === "rtl";
|
|
947
|
+
}
|
|
948
|
+
scrollObserver = createScrollObserver(store, window, isHorizontal, () => normalizeScrollOffset(window[scrollOffsetKey], isNegative), (jump, shift) => {
|
|
927
949
|
// TODO support case two window scrollers exist in the same view
|
|
928
950
|
if (shift) {
|
|
929
951
|
// Use absolute position not to exceed scrollable bounds
|
|
930
952
|
window.scroll({
|
|
931
|
-
[scrollToKey]:
|
|
953
|
+
[scrollToKey]: normalizeScrollOffset(store.$getScrollOffset() + jump, isNegative),
|
|
932
954
|
});
|
|
933
955
|
}
|
|
934
956
|
else {
|
|
935
957
|
// Use window.scrollBy here, which causes less layout shift for some reason.
|
|
936
958
|
window.scrollBy({
|
|
937
|
-
[scrollToKey]:
|
|
959
|
+
[scrollToKey]: normalizeScrollOffset(jump, isNegative),
|
|
938
960
|
});
|
|
939
961
|
}
|
|
940
962
|
}, () => calcOffsetToViewport(container, document.body, window, isHorizontal));
|
|
@@ -947,6 +969,7 @@ const createWindowScroller = (store, isHorizontal) => {
|
|
|
947
969
|
// https://github.com/inokawa/virtua/pull/765
|
|
948
970
|
initialized = createPromise();
|
|
949
971
|
},
|
|
972
|
+
$isNegative: () => isNegative,
|
|
950
973
|
$fixScrollJump: () => {
|
|
951
974
|
scrollObserver && scrollObserver._fixScrollJump();
|
|
952
975
|
},
|
|
@@ -970,7 +993,7 @@ const createWindowScroller = (store, isHorizontal) => {
|
|
|
970
993
|
}
|
|
971
994
|
const document = getCurrentDocument(containerElement);
|
|
972
995
|
const window = getCurrentWindow(document);
|
|
973
|
-
const html = document
|
|
996
|
+
const html = getDocumentElement(document);
|
|
974
997
|
const getScrollbarSize = () => store.$getViewportSize() -
|
|
975
998
|
(isHorizontal ? html.clientWidth : html.clientHeight);
|
|
976
999
|
scheduleScroll(() => {
|
|
@@ -1006,6 +1029,7 @@ const createGridScroller = (rowStore, colStore) => {
|
|
|
1006
1029
|
rowScroller.$dispose();
|
|
1007
1030
|
colScroller.$dispose();
|
|
1008
1031
|
},
|
|
1032
|
+
$isNegative: colScroller.$isNegative,
|
|
1009
1033
|
$scrollTo(row, col) {
|
|
1010
1034
|
if (row != null) {
|
|
1011
1035
|
rowScroller.$scrollTo(row);
|
|
@@ -1266,9 +1290,6 @@ const createGridResizer = (rowStore, colStore) => {
|
|
|
1266
1290
|
};
|
|
1267
1291
|
};
|
|
1268
1292
|
|
|
1269
|
-
/**
|
|
1270
|
-
* @jsxImportSource solid-js
|
|
1271
|
-
*/
|
|
1272
1293
|
/**
|
|
1273
1294
|
* @internal
|
|
1274
1295
|
*/
|
|
@@ -1288,7 +1309,7 @@ const ListItem = (props) => {
|
|
|
1288
1309
|
position: "absolute",
|
|
1289
1310
|
[isHorizontal ? "height" : "width"]: "100%",
|
|
1290
1311
|
[isHorizontal ? "top" : "left"]: "0px",
|
|
1291
|
-
[isHorizontal ?
|
|
1312
|
+
[isHorizontal ? "left" : "top"]: props._offset + "px",
|
|
1292
1313
|
visibility: props._hide ? "hidden" : undefined,
|
|
1293
1314
|
};
|
|
1294
1315
|
if (isHorizontal) {
|
|
@@ -1343,6 +1364,7 @@ const Virtualizer = (props) => {
|
|
|
1343
1364
|
});
|
|
1344
1365
|
const isScrolling = createMemo(() => stateVersion() && store.$isScrolling());
|
|
1345
1366
|
const totalSize = createMemo(() => stateVersion() && store.$getTotalSize());
|
|
1367
|
+
const isNegative = createMemo(() => stateVersion() && scroller.$isNegative());
|
|
1346
1368
|
onMount(() => {
|
|
1347
1369
|
if (props.ref) {
|
|
1348
1370
|
props.ref({
|
|
@@ -1393,6 +1415,7 @@ const Virtualizer = (props) => {
|
|
|
1393
1415
|
store.$update(ACTION_ITEMS_LENGTH_CHANGE, [count, props.shift]);
|
|
1394
1416
|
}
|
|
1395
1417
|
});
|
|
1418
|
+
const pushKey = !horizontal && isNegative() ? "unshift" : "push";
|
|
1396
1419
|
const items = [];
|
|
1397
1420
|
const indexes = [];
|
|
1398
1421
|
if (props.keepMounted) {
|
|
@@ -1401,14 +1424,14 @@ const Virtualizer = (props) => {
|
|
|
1401
1424
|
mounted.add(i);
|
|
1402
1425
|
}
|
|
1403
1426
|
sort([...mounted]).forEach((index) => {
|
|
1404
|
-
items
|
|
1405
|
-
indexes
|
|
1427
|
+
items[pushKey](props.data[index]);
|
|
1428
|
+
indexes[pushKey](index);
|
|
1406
1429
|
});
|
|
1407
1430
|
}
|
|
1408
1431
|
else {
|
|
1409
1432
|
for (let [i, j] = range(); i <= j; i++) {
|
|
1410
|
-
items
|
|
1411
|
-
indexes
|
|
1433
|
+
items[pushKey](props.data[i]);
|
|
1434
|
+
indexes[pushKey](i);
|
|
1412
1435
|
}
|
|
1413
1436
|
}
|
|
1414
1437
|
return { _items: items, _indexes: indexes };
|
|
@@ -1416,7 +1439,7 @@ const Virtualizer = (props) => {
|
|
|
1416
1439
|
const renderItem = (data, index) => {
|
|
1417
1440
|
const offset = createMemo(() => {
|
|
1418
1441
|
stateVersion();
|
|
1419
|
-
return store.$getItemOffset(index());
|
|
1442
|
+
return store.$getItemOffset(index(), isNegative());
|
|
1420
1443
|
});
|
|
1421
1444
|
const hide = createMemo(() => {
|
|
1422
1445
|
stateVersion();
|
|
@@ -1517,6 +1540,7 @@ const WindowVirtualizer = (props) => {
|
|
|
1517
1540
|
});
|
|
1518
1541
|
const isScrolling = createMemo(() => stateVersion() && store.$isScrolling());
|
|
1519
1542
|
const totalSize = createMemo(() => stateVersion() && store.$getTotalSize());
|
|
1543
|
+
const isNegative = createMemo(() => stateVersion() && scroller.$isNegative());
|
|
1520
1544
|
onMount(() => {
|
|
1521
1545
|
if (props.ref) {
|
|
1522
1546
|
props.ref({
|
|
@@ -1556,9 +1580,10 @@ const WindowVirtualizer = (props) => {
|
|
|
1556
1580
|
store.$update(ACTION_ITEMS_LENGTH_CHANGE, [count, props.shift]);
|
|
1557
1581
|
}
|
|
1558
1582
|
});
|
|
1583
|
+
const pushKey = !horizontal && isNegative() ? "unshift" : "push";
|
|
1559
1584
|
const items = [];
|
|
1560
1585
|
for (let [i, j] = range(); i <= j; i++) {
|
|
1561
|
-
items
|
|
1586
|
+
items[pushKey](props.data[i]);
|
|
1562
1587
|
}
|
|
1563
1588
|
return items;
|
|
1564
1589
|
});
|
|
@@ -1576,7 +1601,7 @@ const WindowVirtualizer = (props) => {
|
|
|
1576
1601
|
const itemIndex = createMemo(() => range()[0] + index());
|
|
1577
1602
|
const offset = createMemo(() => {
|
|
1578
1603
|
stateVersion();
|
|
1579
|
-
return store.$getItemOffset(itemIndex());
|
|
1604
|
+
return store.$getItemOffset(itemIndex(), isNegative());
|
|
1580
1605
|
});
|
|
1581
1606
|
const hide = createMemo(() => {
|
|
1582
1607
|
stateVersion();
|