virtua 0.47.2 → 0.48.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/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 +67 -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,23 @@ 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"}:${viewportSize + 1}px`;
|
|
819
|
+
viewport.appendChild(dummy);
|
|
820
|
+
viewport[scrollOffsetKey] = 1;
|
|
821
|
+
// It can be positive under some specific situations even if negative mode, so we use `<` for now.
|
|
822
|
+
isNegative = viewport[scrollOffsetKey] < 1;
|
|
823
|
+
viewport.removeChild(dummy);
|
|
824
|
+
viewport[scrollOffsetKey] = prev;
|
|
825
|
+
clean();
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
scrollObserver = createScrollObserver(store, viewport, isHorizontal, () => normalizeScrollOffset(viewport[scrollOffsetKey], isNegative), (jump, shift, isMomentumScrolling) => {
|
|
816
829
|
// If we update scroll position while touching on iOS, the position will be reverted.
|
|
817
830
|
// However iOS WebKit fires touch events only once at the beginning of momentum scrolling.
|
|
818
831
|
// That means we have no reliable way to confirm still touched or not if user touches more than once during momentum scrolling...
|
|
@@ -827,7 +840,7 @@ const createScroller = (store, isHorizontal) => {
|
|
|
827
840
|
}
|
|
828
841
|
// Use absolute position not to exceed scrollable bounds
|
|
829
842
|
// https://github.com/inokawa/virtua/discussions/475
|
|
830
|
-
viewport[scrollOffsetKey] =
|
|
843
|
+
viewport[scrollOffsetKey] = normalizeScrollOffset(store.$getScrollOffset() + jump, isNegative);
|
|
831
844
|
if (shift) {
|
|
832
845
|
// https://github.com/inokawa/virtua/issues/357
|
|
833
846
|
cancelScroll();
|
|
@@ -841,6 +854,7 @@ const createScroller = (store, isHorizontal) => {
|
|
|
841
854
|
// https://github.com/inokawa/virtua/pull/765
|
|
842
855
|
initialized = createPromise();
|
|
843
856
|
},
|
|
857
|
+
$isNegative: () => isNegative,
|
|
844
858
|
$scrollTo(offset) {
|
|
845
859
|
scheduleScroll(() => offset);
|
|
846
860
|
},
|
|
@@ -888,9 +902,10 @@ const createWindowScroller = (store, isHorizontal) => {
|
|
|
888
902
|
let containerElement;
|
|
889
903
|
let scrollObserver;
|
|
890
904
|
let initialized = createPromise();
|
|
905
|
+
let isNegative = false;
|
|
891
906
|
const scrollToKey = isHorizontal ? "left" : "top";
|
|
892
907
|
const [scheduleScroll] = createScrollScheduler(store, () => initialized[0], (offset, smooth) => {
|
|
893
|
-
offset =
|
|
908
|
+
offset = normalizeScrollOffset(offset, isNegative);
|
|
894
909
|
const window = getCurrentWindow(getCurrentDocument(containerElement));
|
|
895
910
|
if (smooth) {
|
|
896
911
|
window.scroll({
|
|
@@ -908,7 +923,7 @@ const createWindowScroller = (store, isHorizontal) => {
|
|
|
908
923
|
// TODO calc offset only when it changes (maybe impossible)
|
|
909
924
|
const offsetKey = isHorizontal ? "offsetLeft" : "offsetTop";
|
|
910
925
|
const offsetSum = offset +
|
|
911
|
-
(isHorizontal &&
|
|
926
|
+
(isHorizontal && isNegative
|
|
912
927
|
? window.innerWidth - node[offsetKey] - node.offsetWidth
|
|
913
928
|
: node[offsetKey]);
|
|
914
929
|
const parent = node.offsetParent;
|
|
@@ -923,18 +938,23 @@ const createWindowScroller = (store, isHorizontal) => {
|
|
|
923
938
|
const scrollOffsetKey = isHorizontal ? "scrollX" : "scrollY";
|
|
924
939
|
const document = getCurrentDocument(container);
|
|
925
940
|
const window = getCurrentWindow(document);
|
|
926
|
-
|
|
941
|
+
if (isHorizontal) {
|
|
942
|
+
// Detect RTL document
|
|
943
|
+
isNegative =
|
|
944
|
+
getComputedStyle(getDocumentElement(document)).direction === "rtl";
|
|
945
|
+
}
|
|
946
|
+
scrollObserver = createScrollObserver(store, window, isHorizontal, () => normalizeScrollOffset(window[scrollOffsetKey], isNegative), (jump, shift) => {
|
|
927
947
|
// TODO support case two window scrollers exist in the same view
|
|
928
948
|
if (shift) {
|
|
929
949
|
// Use absolute position not to exceed scrollable bounds
|
|
930
950
|
window.scroll({
|
|
931
|
-
[scrollToKey]:
|
|
951
|
+
[scrollToKey]: normalizeScrollOffset(store.$getScrollOffset() + jump, isNegative),
|
|
932
952
|
});
|
|
933
953
|
}
|
|
934
954
|
else {
|
|
935
955
|
// Use window.scrollBy here, which causes less layout shift for some reason.
|
|
936
956
|
window.scrollBy({
|
|
937
|
-
[scrollToKey]:
|
|
957
|
+
[scrollToKey]: normalizeScrollOffset(jump, isNegative),
|
|
938
958
|
});
|
|
939
959
|
}
|
|
940
960
|
}, () => calcOffsetToViewport(container, document.body, window, isHorizontal));
|
|
@@ -947,6 +967,7 @@ const createWindowScroller = (store, isHorizontal) => {
|
|
|
947
967
|
// https://github.com/inokawa/virtua/pull/765
|
|
948
968
|
initialized = createPromise();
|
|
949
969
|
},
|
|
970
|
+
$isNegative: () => isNegative,
|
|
950
971
|
$fixScrollJump: () => {
|
|
951
972
|
scrollObserver && scrollObserver._fixScrollJump();
|
|
952
973
|
},
|
|
@@ -970,7 +991,7 @@ const createWindowScroller = (store, isHorizontal) => {
|
|
|
970
991
|
}
|
|
971
992
|
const document = getCurrentDocument(containerElement);
|
|
972
993
|
const window = getCurrentWindow(document);
|
|
973
|
-
const html = document
|
|
994
|
+
const html = getDocumentElement(document);
|
|
974
995
|
const getScrollbarSize = () => store.$getViewportSize() -
|
|
975
996
|
(isHorizontal ? html.clientWidth : html.clientHeight);
|
|
976
997
|
scheduleScroll(() => {
|
|
@@ -1006,6 +1027,7 @@ const createGridScroller = (rowStore, colStore) => {
|
|
|
1006
1027
|
rowScroller.$dispose();
|
|
1007
1028
|
colScroller.$dispose();
|
|
1008
1029
|
},
|
|
1030
|
+
$isNegative: colScroller.$isNegative,
|
|
1009
1031
|
$scrollTo(row, col) {
|
|
1010
1032
|
if (row != null) {
|
|
1011
1033
|
rowScroller.$scrollTo(row);
|
|
@@ -1266,9 +1288,6 @@ const createGridResizer = (rowStore, colStore) => {
|
|
|
1266
1288
|
};
|
|
1267
1289
|
};
|
|
1268
1290
|
|
|
1269
|
-
/**
|
|
1270
|
-
* @jsxImportSource solid-js
|
|
1271
|
-
*/
|
|
1272
1291
|
/**
|
|
1273
1292
|
* @internal
|
|
1274
1293
|
*/
|
|
@@ -1288,7 +1307,7 @@ const ListItem = (props) => {
|
|
|
1288
1307
|
position: "absolute",
|
|
1289
1308
|
[isHorizontal ? "height" : "width"]: "100%",
|
|
1290
1309
|
[isHorizontal ? "top" : "left"]: "0px",
|
|
1291
|
-
[isHorizontal ?
|
|
1310
|
+
[isHorizontal ? "left" : "top"]: props._offset + "px",
|
|
1292
1311
|
visibility: props._hide ? "hidden" : undefined,
|
|
1293
1312
|
};
|
|
1294
1313
|
if (isHorizontal) {
|
|
@@ -1343,6 +1362,7 @@ const Virtualizer = (props) => {
|
|
|
1343
1362
|
});
|
|
1344
1363
|
const isScrolling = createMemo(() => stateVersion() && store.$isScrolling());
|
|
1345
1364
|
const totalSize = createMemo(() => stateVersion() && store.$getTotalSize());
|
|
1365
|
+
const isNegative = createMemo(() => stateVersion() && scroller.$isNegative());
|
|
1346
1366
|
onMount(() => {
|
|
1347
1367
|
if (props.ref) {
|
|
1348
1368
|
props.ref({
|
|
@@ -1393,6 +1413,7 @@ const Virtualizer = (props) => {
|
|
|
1393
1413
|
store.$update(ACTION_ITEMS_LENGTH_CHANGE, [count, props.shift]);
|
|
1394
1414
|
}
|
|
1395
1415
|
});
|
|
1416
|
+
const pushKey = !horizontal && isNegative() ? "unshift" : "push";
|
|
1396
1417
|
const items = [];
|
|
1397
1418
|
const indexes = [];
|
|
1398
1419
|
if (props.keepMounted) {
|
|
@@ -1401,14 +1422,14 @@ const Virtualizer = (props) => {
|
|
|
1401
1422
|
mounted.add(i);
|
|
1402
1423
|
}
|
|
1403
1424
|
sort([...mounted]).forEach((index) => {
|
|
1404
|
-
items
|
|
1405
|
-
indexes
|
|
1425
|
+
items[pushKey](props.data[index]);
|
|
1426
|
+
indexes[pushKey](index);
|
|
1406
1427
|
});
|
|
1407
1428
|
}
|
|
1408
1429
|
else {
|
|
1409
1430
|
for (let [i, j] = range(); i <= j; i++) {
|
|
1410
|
-
items
|
|
1411
|
-
indexes
|
|
1431
|
+
items[pushKey](props.data[i]);
|
|
1432
|
+
indexes[pushKey](i);
|
|
1412
1433
|
}
|
|
1413
1434
|
}
|
|
1414
1435
|
return { _items: items, _indexes: indexes };
|
|
@@ -1416,7 +1437,7 @@ const Virtualizer = (props) => {
|
|
|
1416
1437
|
const renderItem = (data, index) => {
|
|
1417
1438
|
const offset = createMemo(() => {
|
|
1418
1439
|
stateVersion();
|
|
1419
|
-
return store.$getItemOffset(index());
|
|
1440
|
+
return store.$getItemOffset(index(), isNegative());
|
|
1420
1441
|
});
|
|
1421
1442
|
const hide = createMemo(() => {
|
|
1422
1443
|
stateVersion();
|
|
@@ -1517,6 +1538,7 @@ const WindowVirtualizer = (props) => {
|
|
|
1517
1538
|
});
|
|
1518
1539
|
const isScrolling = createMemo(() => stateVersion() && store.$isScrolling());
|
|
1519
1540
|
const totalSize = createMemo(() => stateVersion() && store.$getTotalSize());
|
|
1541
|
+
const isNegative = createMemo(() => stateVersion() && scroller.$isNegative());
|
|
1520
1542
|
onMount(() => {
|
|
1521
1543
|
if (props.ref) {
|
|
1522
1544
|
props.ref({
|
|
@@ -1556,9 +1578,10 @@ const WindowVirtualizer = (props) => {
|
|
|
1556
1578
|
store.$update(ACTION_ITEMS_LENGTH_CHANGE, [count, props.shift]);
|
|
1557
1579
|
}
|
|
1558
1580
|
});
|
|
1581
|
+
const pushKey = !horizontal && isNegative() ? "unshift" : "push";
|
|
1559
1582
|
const items = [];
|
|
1560
1583
|
for (let [i, j] = range(); i <= j; i++) {
|
|
1561
|
-
items
|
|
1584
|
+
items[pushKey](props.data[i]);
|
|
1562
1585
|
}
|
|
1563
1586
|
return items;
|
|
1564
1587
|
});
|
|
@@ -1576,7 +1599,7 @@ const WindowVirtualizer = (props) => {
|
|
|
1576
1599
|
const itemIndex = createMemo(() => range()[0] + index());
|
|
1577
1600
|
const offset = createMemo(() => {
|
|
1578
1601
|
stateVersion();
|
|
1579
|
-
return store.$getItemOffset(itemIndex());
|
|
1602
|
+
return store.$getItemOffset(itemIndex(), isNegative());
|
|
1580
1603
|
});
|
|
1581
1604
|
const hide = createMemo(() => {
|
|
1582
1605
|
stateVersion();
|