virtua 0.40.4 → 0.41.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.
@@ -1,4 +1,4 @@
1
- import { mergeProps, createEffect, onCleanup, createMemo, createRoot, createSignal, onMount, createComputed, on } from 'solid-js';
1
+ import { mergeProps, createEffect, onCleanup, createMemo, createSignal, onMount, createComputed, on, untrack, For, splitProps } from 'solid-js';
2
2
  import { Dynamic } from 'solid-js/web';
3
3
 
4
4
  /** @internal */
@@ -1272,65 +1272,11 @@ const ListItem = (props) => {
1272
1272
  }
1273
1273
  return style;
1274
1274
  });
1275
- return (<Dynamic component={props._as} ref={elementRef} style={style()}>
1275
+ return (<Dynamic component={props._as} index={props._index} ref={elementRef} style={style()}>
1276
1276
  {props._children}
1277
1277
  </Dynamic>);
1278
1278
  };
1279
1279
 
1280
- /**
1281
- * @jsxImportSource solid-js
1282
- */
1283
- /**
1284
- * https://github.com/solidjs/solid/blob/main/packages/solid/src/reactive/array.ts
1285
- * https://github.com/solidjs/solid/blob/main/packages/solid/src/render/flow.ts
1286
- * https://github.com/solidjs/solid/discussions/366
1287
- * @internal
1288
- */
1289
- const RangedFor = (props) => {
1290
- let prev = new Map();
1291
- onCleanup(() => {
1292
- for (const node of prev.values()) {
1293
- node._dispose();
1294
- }
1295
- });
1296
- return createMemo(() => {
1297
- const list = props._each;
1298
- const [start, end] = props._range;
1299
- const current = new Map();
1300
- const items = [];
1301
- for (let i = start; i <= end; i++) {
1302
- const newData = list[i];
1303
- const lookup = prev.get(i);
1304
- items.push(lookup
1305
- ? lookup._element
1306
- : createRoot((dispose) => {
1307
- const data = createSignal(newData);
1308
- const result = props._render(data[0], i);
1309
- current.set(i, {
1310
- _data: data,
1311
- _element: result,
1312
- _dispose: dispose,
1313
- });
1314
- return result;
1315
- }));
1316
- if (lookup) {
1317
- if (newData !== lookup._data) {
1318
- lookup._data[1](newData // TODO improve type
1319
- );
1320
- }
1321
- current.set(i, lookup);
1322
- }
1323
- }
1324
- for (const [key, node] of prev.entries()) {
1325
- if (!current.has(key)) {
1326
- node._dispose();
1327
- }
1328
- }
1329
- prev = current;
1330
- return items;
1331
- }); // TODO improve type
1332
- };
1333
-
1334
1280
  /**
1335
1281
  * @internal
1336
1282
  */
@@ -1346,9 +1292,9 @@ const isSameRange = (prev, next) => {
1346
1292
  */
1347
1293
  const Virtualizer = (props) => {
1348
1294
  let containerRef;
1349
- const { itemSize, horizontal = false, overscan } = props;
1295
+ const { itemSize, horizontal = false, overscan, cache } = props;
1350
1296
  props = mergeProps({ as: "div" }, props);
1351
- const store = createVirtualStore(props.data.length, itemSize, overscan, undefined, undefined, !itemSize);
1297
+ const store = createVirtualStore(props.data.length, itemSize, overscan, undefined, cache, !itemSize);
1352
1298
  const resizer = createResizer(store, horizontal);
1353
1299
  const scroller = createScroller(store, horizontal);
1354
1300
  const [stateVersion, setRerender] = createSignal(store.$getStateVersion());
@@ -1376,6 +1322,9 @@ const Virtualizer = (props) => {
1376
1322
  onMount(() => {
1377
1323
  if (props.ref) {
1378
1324
  props.ref({
1325
+ get cache() {
1326
+ return store.$getCacheSnapshot();
1327
+ },
1379
1328
  get scrollOffset() {
1380
1329
  return store.$getScrollOffset();
1381
1330
  },
@@ -1421,6 +1370,48 @@ const Virtualizer = (props) => {
1421
1370
  createEffect(on(stateVersion, () => {
1422
1371
  scroller.$fixScrollJump();
1423
1372
  }));
1373
+ const dataSlice = createMemo(() => {
1374
+ const [start, end] = range();
1375
+ const items = end >= 0 ? props.data.slice(start, end + 1) : [];
1376
+ const indexes = items.map((_, index) => start + index);
1377
+ if (props.keepMounted) {
1378
+ const startItems = [];
1379
+ const startIndexes = [];
1380
+ const endItems = [];
1381
+ const endIndexes = [];
1382
+ sort(props.keepMounted).forEach((index) => {
1383
+ if (index < 0 || index >= props.data.length)
1384
+ return;
1385
+ if (index < start) {
1386
+ startItems.push(props.data[index]);
1387
+ startIndexes.push(index);
1388
+ }
1389
+ if (index > end) {
1390
+ endItems.push(props.data[index]);
1391
+ endIndexes.push(index);
1392
+ }
1393
+ });
1394
+ items.unshift(...startItems);
1395
+ indexes.unshift(...startIndexes);
1396
+ items.push(...endItems);
1397
+ indexes.push(...endIndexes);
1398
+ }
1399
+ return { _items: items, _indexes: indexes };
1400
+ });
1401
+ const renderItem = (data, index) => {
1402
+ const offset = createMemo(() => {
1403
+ stateVersion();
1404
+ return store.$getItemOffset(index());
1405
+ });
1406
+ const hide = createMemo(() => {
1407
+ stateVersion();
1408
+ return store.$isUnmeasuredItem(index());
1409
+ });
1410
+ const children = createMemo(() => {
1411
+ return untrack(() => props.children(data, index));
1412
+ });
1413
+ return (<ListItem _as={props.item} _index={index()} _resizer={resizer.$observeItem} _offset={offset()} _hide={hide()} _children={children()} _isHorizontal={horizontal}/>);
1414
+ };
1424
1415
  return (<Dynamic component={props.as} ref={containerRef} style={{
1425
1416
  // contain: "content",
1426
1417
  "overflow-anchor": "none", // opt out browser's scroll anchoring because it will conflict to scroll anchoring of virtualizer
@@ -1431,35 +1422,48 @@ const Virtualizer = (props) => {
1431
1422
  height: horizontal ? "100%" : totalSize() + "px",
1432
1423
  "pointer-events": isScrolling() ? "none" : undefined,
1433
1424
  }}>
1434
- <RangedFor _each={props.data} _range={range()} _render={(data, index) => {
1435
- const offset = createMemo(() => {
1436
- stateVersion();
1437
- return store.$getItemOffset(index);
1438
- });
1439
- const hide = createMemo(() => {
1440
- stateVersion();
1441
- return store.$isUnmeasuredItem(index);
1442
- });
1443
- return (<ListItem _as={props.item} _index={index} _resizer={resizer.$observeItem} _offset={offset()} _hide={hide()} _children={props.children(data(), index)} _isHorizontal={horizontal}/>);
1444
- }}/>
1425
+ <For each={dataSlice()._items}>
1426
+ {(data, index) => {
1427
+ const itemIndex = createMemo(() => dataSlice()._indexes[index()]);
1428
+ // eslint-disable-next-line solid/reactivity
1429
+ return renderItem(data, itemIndex);
1430
+ }}
1431
+ </For>
1445
1432
  </Dynamic>);
1446
1433
  };
1447
1434
 
1435
+ /**
1436
+ * @jsxImportSource solid-js
1437
+ */
1448
1438
  /**
1449
1439
  * Virtualized list component. See {@link VListProps} and {@link VListHandle}.
1450
1440
  */
1451
1441
  const VList = (props) => {
1452
- const { ref, data, children, overscan, itemSize, shift, horizontal, onScroll, onScrollEnd, style, ...attrs } = props;
1453
- return (<div {...attrs} style={{
1454
- display: horizontal ? "inline-block" : "block",
1455
- [horizontal ? "overflow-x" : "overflow-y"]: "auto",
1442
+ const [local, others] = splitProps(props, [
1443
+ "ref",
1444
+ "data",
1445
+ "children",
1446
+ "overscan",
1447
+ "itemSize",
1448
+ "shift",
1449
+ "horizontal",
1450
+ "keepMounted",
1451
+ "cache",
1452
+ "item",
1453
+ "onScroll",
1454
+ "onScrollEnd",
1455
+ "style",
1456
+ ]);
1457
+ return (<div {...others} style={{
1458
+ display: local.horizontal ? "inline-block" : "block",
1459
+ [local.horizontal ? "overflow-x" : "overflow-y"]: "auto",
1456
1460
  contain: "strict",
1457
1461
  width: "100%",
1458
1462
  height: "100%",
1459
- ...props.style,
1463
+ ...local.style,
1460
1464
  }}>
1461
- <Virtualizer ref={props.ref} data={props.data} overscan={props.overscan} itemSize={props.itemSize} shift={props.shift} horizontal={horizontal} onScroll={props.onScroll} onScrollEnd={props.onScrollEnd}>
1462
- {props.children}
1465
+ <Virtualizer ref={local.ref} data={local.data} overscan={local.overscan} itemSize={local.itemSize} shift={local.shift} horizontal={local.horizontal} keepMounted={local.keepMounted} cache={local.cache} item={local.item} onScroll={local.onScroll} onScrollEnd={local.onScrollEnd}>
1466
+ {local.children}
1463
1467
  </Virtualizer>
1464
1468
  </div>);
1465
1469
  };
@@ -1472,8 +1476,8 @@ const VList = (props) => {
1472
1476
  */
1473
1477
  const WindowVirtualizer = (props) => {
1474
1478
  let containerRef;
1475
- const { ref: _ref, data: _data, children: _children, overscan, itemSize, shift: _shift, horizontal = false, onScrollEnd: _onScrollEnd, } = props;
1476
- const store = createVirtualStore(props.data.length, itemSize, overscan, undefined, undefined, !itemSize);
1479
+ const { ref: _ref, data: _data, children: _children, overscan, itemSize, shift: _shift, horizontal = false, cache, onScrollEnd: _onScrollEnd, } = props;
1480
+ const store = createVirtualStore(props.data.length, itemSize, overscan, undefined, cache, !itemSize);
1477
1481
  const resizer = createWindowResizer(store, horizontal);
1478
1482
  const scroller = createWindowScroller(store, horizontal);
1479
1483
  const [stateVersion, setRerender] = createSignal(store.$getStateVersion());
@@ -1502,6 +1506,9 @@ const WindowVirtualizer = (props) => {
1502
1506
  onMount(() => {
1503
1507
  if (props.ref) {
1504
1508
  props.ref({
1509
+ get cache() {
1510
+ return store.$getCacheSnapshot();
1511
+ },
1505
1512
  findStartIndex: store.$findStartIndex,
1506
1513
  findEndIndex: store.$findEndIndex,
1507
1514
  scrollToIndex: scroller.$scrollToIndex,
@@ -1528,6 +1535,10 @@ const WindowVirtualizer = (props) => {
1528
1535
  createEffect(on(stateVersion, () => {
1529
1536
  scroller.$fixScrollJump();
1530
1537
  }));
1538
+ const dataSlice = createMemo(() => {
1539
+ const [start, end] = range();
1540
+ return end >= 0 ? props.data.slice(start, end + 1) : [];
1541
+ });
1531
1542
  return (<div ref={containerRef} style={{
1532
1543
  // contain: "content",
1533
1544
  "overflow-anchor": "none", // opt out browser's scroll anchoring because it will conflict to scroll anchoring of virtualizer
@@ -1538,17 +1549,23 @@ const WindowVirtualizer = (props) => {
1538
1549
  height: horizontal ? "100%" : totalSize() + "px",
1539
1550
  "pointer-events": isScrolling() ? "none" : undefined,
1540
1551
  }}>
1541
- <RangedFor _each={props.data} _range={range()} _render={(data, index) => {
1552
+ <For each={dataSlice()}>
1553
+ {(data, index) => {
1554
+ const itemIndex = createMemo(() => range()[0] + index());
1542
1555
  const offset = createMemo(() => {
1543
1556
  stateVersion();
1544
- return store.$getItemOffset(index);
1557
+ return store.$getItemOffset(itemIndex());
1545
1558
  });
1546
1559
  const hide = createMemo(() => {
1547
1560
  stateVersion();
1548
- return store.$isUnmeasuredItem(index);
1561
+ return store.$isUnmeasuredItem(itemIndex());
1562
+ });
1563
+ const children = createMemo(() => {
1564
+ return untrack(() => props.children(data, itemIndex));
1549
1565
  });
1550
- return (<ListItem _index={index} _resizer={resizer.$observeItem} _offset={offset()} _hide={hide()} _children={props.children(data(), index)} _isHorizontal={horizontal}/>);
1551
- }}/>
1566
+ return (<ListItem _index={itemIndex()} _resizer={resizer.$observeItem} _offset={offset()} _hide={hide()} _children={children()} _isHorizontal={horizontal}/>);
1567
+ }}
1568
+ </For>
1552
1569
  </div>);
1553
1570
  };
1554
1571