@tanstack/marko-virtual 3.14.3 → 3.15.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,13 +1,44 @@
1
- import { VirtualizerOptions } from '@tanstack/virtual-core';
1
+ import { Virtualizer, Range, VirtualItem, VirtualizerOptions } from '@tanstack/virtual-core';
2
2
  export interface WindowVirtualizerInput {
3
3
  count: number;
4
4
  estimateSize?: (index: number) => number;
5
5
  overscan?: number;
6
+ horizontal?: boolean;
6
7
  paddingStart?: number;
7
8
  paddingEnd?: number;
8
9
  scrollPaddingStart?: number;
9
10
  scrollPaddingEnd?: number;
10
11
  gap?: number;
11
12
  lanes?: number;
13
+ initialOffset?: number | (() => number);
14
+ initialRect?: {
15
+ width: number;
16
+ height: number;
17
+ };
18
+ getItemKey?: (index: number) => number | string | bigint;
19
+ rangeExtractor?: (range: Range) => Array<number>;
20
+ indexAttribute?: string;
21
+ initialMeasurementsCache?: Array<VirtualItem>;
22
+ anchorTo?: 'start' | 'end';
23
+ followOnAppend?: boolean | ScrollBehavior;
24
+ scrollEndThreshold?: number;
25
+ scrollMargin?: number;
26
+ enabled?: boolean;
27
+ isRtl?: boolean;
28
+ isScrollingResetDelay?: number;
29
+ useScrollendEvent?: boolean;
30
+ useAnimationFrameWithResizeObserver?: boolean;
31
+ laneAssignmentMode?: 'estimate' | 'measured';
32
+ useCachedMeasurements?: boolean;
33
+ debug?: boolean;
34
+ measureElement?: (element: Element, entry: ResizeObserverEntry | undefined, instance: Virtualizer<Window, Element>) => number;
12
35
  }
13
36
  export declare function buildOptions(input: WindowVirtualizerInput, notify: () => void): VirtualizerOptions<Window, Element>;
37
+ export declare function renderSlice(input: WindowVirtualizerInput): {
38
+ items: Array<VirtualItem>;
39
+ size: number;
40
+ range: {
41
+ startIndex: number;
42
+ endIndex: number;
43
+ } | null;
44
+ };
@@ -0,0 +1,56 @@
1
+ import { Virtualizer } from "@tanstack/virtual-core";
2
+ import type { Range, VirtualItem, ScrollToOptions } from "@tanstack/virtual-core";
3
+ export interface VirtualizerHandle {
4
+ virtualItems: VirtualItem[];
5
+ totalSize: number;
6
+ range: {
7
+ startIndex: number;
8
+ endIndex: number;
9
+ } | null;
10
+ measureElement: ((el: Element | null) => void) | undefined;
11
+ scrollToIndex: (index: number, options?: ScrollToOptions) => void;
12
+ scrollToOffset: (offset: number, options?: ScrollToOptions) => void;
13
+ measure: () => void;
14
+ resizeItem: (index: number, size: number) => void;
15
+ scrollToEnd: (options?: {
16
+ behavior?: ScrollToOptions['behavior'];
17
+ }) => void;
18
+ isAtEnd: (threshold?: number) => boolean;
19
+ getDistanceFromEnd: () => number;
20
+ }
21
+ export interface Input {
22
+ count: number;
23
+ estimateSize?: (index: number) => number;
24
+ getScrollElement: () => Element | null;
25
+ overscan?: number;
26
+ horizontal?: boolean;
27
+ paddingStart?: number;
28
+ paddingEnd?: number;
29
+ scrollPaddingStart?: number;
30
+ scrollPaddingEnd?: number;
31
+ gap?: number;
32
+ lanes?: number;
33
+ initialOffset?: number | (() => number);
34
+ initialRect?: {
35
+ width: number;
36
+ height: number;
37
+ };
38
+ getItemKey?: (index: number) => number | string | bigint;
39
+ rangeExtractor?: (range: Range) => number[];
40
+ indexAttribute?: string;
41
+ initialMeasurementsCache?: VirtualItem[];
42
+ anchorTo?: 'start' | 'end';
43
+ followOnAppend?: boolean | ScrollBehavior;
44
+ scrollEndThreshold?: number;
45
+ scrollMargin?: number;
46
+ enabled?: boolean;
47
+ isRtl?: boolean;
48
+ isScrollingResetDelay?: number;
49
+ useScrollendEvent?: boolean;
50
+ useAnimationFrameWithResizeObserver?: boolean;
51
+ laneAssignmentMode?: 'estimate' | 'measured';
52
+ useCachedMeasurements?: boolean;
53
+ debug?: boolean;
54
+ measureElement?: (element: Element, entry: ResizeObserverEntry | undefined, instance: Virtualizer<Element, Element>) => number;
55
+ }
56
+ <return = (1 as any as VirtualizerHandle)/>
@@ -0,0 +1,59 @@
1
+ import { Virtualizer } from "@tanstack/virtual-core";
2
+ import { buildOptions, renderSlice } from "./options.js";
3
+ <!-- Render-time seed: derive the initial window as plain data via a transient virtual-core-->
4
+ <!-- instance (see renderSlice in ./options). On the server this paints the initial slice into-->
5
+ <!-- the HTML (when `initialRect` is given) or an empty container with the correct total height-->
6
+ <!-- (when it is not) — no <if=mounted> guard. The values serialize and recompute identically on-->
7
+ <!-- resume. The live, observing instance is then built client-only in onMount and takes over.-->
8
+ <const/seed=renderSlice(input)/>
9
+ <let/items=seed.items/>
10
+ <let/size=seed.size/>
11
+ <let/range=seed.range/>
12
+ <let/v=null/>
13
+ <let/cleanup=null/>
14
+ <lifecycle<{}> onMount() {
15
+ const notify = () => {
16
+ const inst = v;
17
+ if (!inst) return;
18
+ items = inst.getVirtualItems();
19
+ size = inst.getTotalSize();
20
+ range = inst.range;
21
+ };
22
+ v = new Virtualizer(buildOptions(input, notify));
23
+ cleanup = v._didMount();
24
+ v._willUpdate();
25
+ notify();
26
+ } onUpdate() {
27
+ const current = v;
28
+ if (!current) return;
29
+ const notify = () => {
30
+ const inst = v;
31
+ if (!inst) return;
32
+ items = inst.getVirtualItems();
33
+ size = inst.getTotalSize();
34
+ range = inst.range;
35
+ };
36
+ current.setOptions({
37
+ ...current.options,
38
+ ...buildOptions(input, notify)
39
+ });
40
+ current._willUpdate();
41
+ notify();
42
+ } onDestroy() {
43
+ cleanup?.();
44
+ v = null;
45
+ cleanup = null;
46
+ }/>
47
+ <return={
48
+ virtualItems: items,
49
+ totalSize: size,
50
+ range: range,
51
+ measureElement: v?.measureElement,
52
+ scrollToIndex: (index, options) => v?.scrollToIndex(index, options),
53
+ scrollToOffset: (offset, options) => v?.scrollToOffset(offset, options),
54
+ measure: () => v?.measure(),
55
+ resizeItem: (index, itemSize) => v?.resizeItem(index, itemSize),
56
+ scrollToEnd: options => v?.scrollToEnd(options),
57
+ isAtEnd: threshold => v?.isAtEnd(threshold) ?? false,
58
+ getDistanceFromEnd: () => v?.getDistanceFromEnd() ?? Infinity
59
+ }/>
@@ -0,0 +1,46 @@
1
+ import { Virtualizer } from '@tanstack/virtual-core';
2
+ import type { Range, VirtualItem, VirtualizerOptions } from '@tanstack/virtual-core';
3
+ export interface VirtualizerInput {
4
+ count: number;
5
+ estimateSize?: (index: number) => number;
6
+ getScrollElement: () => Element | null;
7
+ overscan?: number;
8
+ horizontal?: boolean;
9
+ paddingStart?: number;
10
+ paddingEnd?: number;
11
+ scrollPaddingStart?: number;
12
+ scrollPaddingEnd?: number;
13
+ gap?: number;
14
+ lanes?: number;
15
+ initialOffset?: number | (() => number);
16
+ initialRect?: {
17
+ width: number;
18
+ height: number;
19
+ };
20
+ getItemKey?: (index: number) => number | string | bigint;
21
+ rangeExtractor?: (range: Range) => Array<number>;
22
+ indexAttribute?: string;
23
+ initialMeasurementsCache?: Array<VirtualItem>;
24
+ anchorTo?: 'start' | 'end';
25
+ followOnAppend?: boolean | ScrollBehavior;
26
+ scrollEndThreshold?: number;
27
+ scrollMargin?: number;
28
+ enabled?: boolean;
29
+ isRtl?: boolean;
30
+ isScrollingResetDelay?: number;
31
+ useScrollendEvent?: boolean;
32
+ useAnimationFrameWithResizeObserver?: boolean;
33
+ laneAssignmentMode?: 'estimate' | 'measured';
34
+ useCachedMeasurements?: boolean;
35
+ debug?: boolean;
36
+ measureElement?: (element: Element, entry: ResizeObserverEntry | undefined, instance: Virtualizer<Element, Element>) => number;
37
+ }
38
+ export declare function buildOptions(input: VirtualizerInput, notify: () => void): VirtualizerOptions<Element, Element>;
39
+ export declare function renderSlice(input: VirtualizerInput): {
40
+ items: Array<VirtualItem>;
41
+ size: number;
42
+ range: {
43
+ startIndex: number;
44
+ endIndex: number;
45
+ } | null;
46
+ };
@@ -0,0 +1,63 @@
1
+ import { Virtualizer, elementScroll, observeElementOffset, observeElementRect, } from '@tanstack/virtual-core';
2
+ // Single source of truth for the input -> virtual-core option mapping,
3
+ // shared by the render-time seed, onMount (construction) and onUpdate (setOptions).
4
+ // Optional inputs are forwarded possibly-undefined on purpose: virtual-core's
5
+ // setOptions() skips undefined keys, so its own defaults apply.
6
+ export function buildOptions(input, notify) {
7
+ return {
8
+ count: input.count,
9
+ estimateSize: input.estimateSize ?? (() => 50),
10
+ getScrollElement: input.getScrollElement,
11
+ overscan: input.overscan ?? 5,
12
+ horizontal: input.horizontal ?? false,
13
+ paddingStart: input.paddingStart,
14
+ paddingEnd: input.paddingEnd,
15
+ scrollPaddingStart: input.scrollPaddingStart,
16
+ scrollPaddingEnd: input.scrollPaddingEnd,
17
+ gap: input.gap,
18
+ lanes: input.lanes,
19
+ initialOffset: input.initialOffset,
20
+ initialRect: input.initialRect,
21
+ getItemKey: input.getItemKey,
22
+ rangeExtractor: input.rangeExtractor,
23
+ indexAttribute: input.indexAttribute,
24
+ initialMeasurementsCache: input.initialMeasurementsCache,
25
+ anchorTo: input.anchorTo,
26
+ followOnAppend: input.followOnAppend,
27
+ scrollEndThreshold: input.scrollEndThreshold,
28
+ scrollMargin: input.scrollMargin,
29
+ enabled: input.enabled,
30
+ isRtl: input.isRtl,
31
+ isScrollingResetDelay: input.isScrollingResetDelay,
32
+ useScrollendEvent: input.useScrollendEvent,
33
+ useAnimationFrameWithResizeObserver: input.useAnimationFrameWithResizeObserver,
34
+ laneAssignmentMode: input.laneAssignmentMode,
35
+ useCachedMeasurements: input.useCachedMeasurements,
36
+ debug: input.debug,
37
+ measureElement: input.measureElement,
38
+ observeElementRect,
39
+ observeElementOffset,
40
+ scrollToFn: elementScroll,
41
+ onChange: notify,
42
+ };
43
+ }
44
+ // Render-time (server / pre-mount) slice. Constructs a transient virtual-core instance,
45
+ // reads the initial window as PLAIN DATA, and discards it — nothing live is retained, so
46
+ // nothing live is serialized, and the values recompute identically on resume. With
47
+ // `initialRect` this is a real slice of rows; without it renderSlice returns the trivial
48
+ // window (no items, size 0). The constructor and getVirtualItems()/getTotalSize() are
49
+ // SSR-safe (no DOM access); getScrollElement is forced to null so the probe never touches
50
+ // the scroll element, which does not exist on the server.
51
+ // `range` is the visible window ({ startIndex, endIndex }) the probe computed — plain data,
52
+ // null when there is no slice.
53
+ export function renderSlice(input) {
54
+ // Opt-in: without an explicit viewport hint there is no server slice. Returning the
55
+ // trivial window keeps every example that does not pass `initialRect` byte-for-byte
56
+ // identical to a client-only build (empty container, filled on mount).
57
+ if (!input.initialRect) {
58
+ return { items: [], size: 0, range: null };
59
+ }
60
+ const probe = new Virtualizer(buildOptions({ ...input, getScrollElement: () => null }, () => { }));
61
+ const items = probe.getVirtualItems();
62
+ return { items, size: probe.getTotalSize(), range: probe.range };
63
+ }
@@ -0,0 +1,55 @@
1
+ import { Virtualizer } from "@tanstack/virtual-core";
2
+ import type { Range, VirtualItem, ScrollToOptions } from "@tanstack/virtual-core";
3
+ export interface WindowVirtualizerHandle {
4
+ virtualItems: VirtualItem[];
5
+ totalSize: number;
6
+ range: {
7
+ startIndex: number;
8
+ endIndex: number;
9
+ } | null;
10
+ measureElement: ((el: Element | null) => void) | undefined;
11
+ scrollToIndex: (index: number, options?: ScrollToOptions) => void;
12
+ scrollToOffset: (offset: number, options?: ScrollToOptions) => void;
13
+ measure: () => void;
14
+ resizeItem: (index: number, size: number) => void;
15
+ scrollToEnd: (options?: {
16
+ behavior?: ScrollToOptions['behavior'];
17
+ }) => void;
18
+ isAtEnd: (threshold?: number) => boolean;
19
+ getDistanceFromEnd: () => number;
20
+ }
21
+ export interface Input {
22
+ count: number;
23
+ estimateSize?: (index: number) => number;
24
+ overscan?: number;
25
+ horizontal?: boolean;
26
+ paddingStart?: number;
27
+ paddingEnd?: number;
28
+ scrollPaddingStart?: number;
29
+ scrollPaddingEnd?: number;
30
+ gap?: number;
31
+ lanes?: number;
32
+ initialOffset?: number | (() => number);
33
+ initialRect?: {
34
+ width: number;
35
+ height: number;
36
+ };
37
+ getItemKey?: (index: number) => number | string | bigint;
38
+ rangeExtractor?: (range: Range) => number[];
39
+ indexAttribute?: string;
40
+ initialMeasurementsCache?: VirtualItem[];
41
+ anchorTo?: 'start' | 'end';
42
+ followOnAppend?: boolean | ScrollBehavior;
43
+ scrollEndThreshold?: number;
44
+ scrollMargin?: number;
45
+ enabled?: boolean;
46
+ isRtl?: boolean;
47
+ isScrollingResetDelay?: number;
48
+ useScrollendEvent?: boolean;
49
+ useAnimationFrameWithResizeObserver?: boolean;
50
+ laneAssignmentMode?: 'estimate' | 'measured';
51
+ useCachedMeasurements?: boolean;
52
+ debug?: boolean;
53
+ measureElement?: (element: Element, entry: ResizeObserverEntry | undefined, instance: Virtualizer<Window, Element>) => number;
54
+ }
55
+ <return = (1 as any as WindowVirtualizerHandle)/>
@@ -0,0 +1,57 @@
1
+ import { Virtualizer } from "@tanstack/virtual-core";
2
+ import { buildOptions, renderSlice } from "./options.js";
3
+ <!-- Render-time seed (window counterpart): paints the initial slice into the server HTML when-->
4
+ <!-- `initialRect` is given (empty container with correct total height otherwise), no <if=mounted>-->
5
+ <!-- guard. The live, observing instance is built client-only in onMount and takes over.-->
6
+ <const/seed=renderSlice(input)/>
7
+ <let/items=seed.items/>
8
+ <let/size=seed.size/>
9
+ <let/range=seed.range/>
10
+ <let/v=null/>
11
+ <let/cleanup=null/>
12
+ <lifecycle<{}> onMount() {
13
+ const notify = () => {
14
+ const inst = v;
15
+ if (!inst) return;
16
+ items = inst.getVirtualItems();
17
+ size = inst.getTotalSize();
18
+ range = inst.range;
19
+ };
20
+ v = new Virtualizer(buildOptions(input, notify));
21
+ cleanup = v._didMount();
22
+ v._willUpdate();
23
+ notify();
24
+ } onUpdate() {
25
+ const current = v;
26
+ if (!current) return;
27
+ const notify = () => {
28
+ const inst = v;
29
+ if (!inst) return;
30
+ items = inst.getVirtualItems();
31
+ size = inst.getTotalSize();
32
+ range = inst.range;
33
+ };
34
+ current.setOptions({
35
+ ...current.options,
36
+ ...buildOptions(input, notify)
37
+ });
38
+ current._willUpdate();
39
+ notify();
40
+ } onDestroy() {
41
+ cleanup?.();
42
+ v = null;
43
+ cleanup = null;
44
+ }/>
45
+ <return={
46
+ virtualItems: items,
47
+ totalSize: size,
48
+ range: range,
49
+ measureElement: v?.measureElement,
50
+ scrollToIndex: (index, options) => v?.scrollToIndex(index, options),
51
+ scrollToOffset: (offset, options) => v?.scrollToOffset(offset, options),
52
+ measure: () => v?.measure(),
53
+ resizeItem: (index, itemSize) => v?.resizeItem(index, itemSize),
54
+ scrollToEnd: options => v?.scrollToEnd(options),
55
+ isAtEnd: threshold => v?.isAtEnd(threshold) ?? false,
56
+ getDistanceFromEnd: () => v?.getDistanceFromEnd() ?? Infinity
57
+ }/>
@@ -0,0 +1,45 @@
1
+ import { Virtualizer } from '@tanstack/virtual-core';
2
+ import type { Range, VirtualItem, VirtualizerOptions } from '@tanstack/virtual-core';
3
+ export interface WindowVirtualizerInput {
4
+ count: number;
5
+ estimateSize?: (index: number) => number;
6
+ overscan?: number;
7
+ horizontal?: boolean;
8
+ paddingStart?: number;
9
+ paddingEnd?: number;
10
+ scrollPaddingStart?: number;
11
+ scrollPaddingEnd?: number;
12
+ gap?: number;
13
+ lanes?: number;
14
+ initialOffset?: number | (() => number);
15
+ initialRect?: {
16
+ width: number;
17
+ height: number;
18
+ };
19
+ getItemKey?: (index: number) => number | string | bigint;
20
+ rangeExtractor?: (range: Range) => Array<number>;
21
+ indexAttribute?: string;
22
+ initialMeasurementsCache?: Array<VirtualItem>;
23
+ anchorTo?: 'start' | 'end';
24
+ followOnAppend?: boolean | ScrollBehavior;
25
+ scrollEndThreshold?: number;
26
+ scrollMargin?: number;
27
+ enabled?: boolean;
28
+ isRtl?: boolean;
29
+ isScrollingResetDelay?: number;
30
+ useScrollendEvent?: boolean;
31
+ useAnimationFrameWithResizeObserver?: boolean;
32
+ laneAssignmentMode?: 'estimate' | 'measured';
33
+ useCachedMeasurements?: boolean;
34
+ debug?: boolean;
35
+ measureElement?: (element: Element, entry: ResizeObserverEntry | undefined, instance: Virtualizer<Window, Element>) => number;
36
+ }
37
+ export declare function buildOptions(input: WindowVirtualizerInput, notify: () => void): VirtualizerOptions<Window, Element>;
38
+ export declare function renderSlice(input: WindowVirtualizerInput): {
39
+ items: Array<VirtualItem>;
40
+ size: number;
41
+ range: {
42
+ startIndex: number;
43
+ endIndex: number;
44
+ } | null;
45
+ };
@@ -0,0 +1,67 @@
1
+ import { Virtualizer, observeWindowOffset, observeWindowRect, windowScroll, } from '@tanstack/virtual-core';
2
+ // Single source of truth for the input -> virtual-core option mapping,
3
+ // shared by the render-time seed, onMount (construction) and onUpdate (setOptions).
4
+ // Optional inputs are forwarded possibly-undefined on purpose: virtual-core's
5
+ // setOptions() skips undefined keys, so its own defaults apply.
6
+ export function buildOptions(input, notify) {
7
+ return {
8
+ count: input.count,
9
+ estimateSize: input.estimateSize ?? (() => 50),
10
+ getScrollElement: () => (typeof document !== 'undefined' ? window : null),
11
+ overscan: input.overscan ?? 5,
12
+ horizontal: input.horizontal,
13
+ paddingStart: input.paddingStart,
14
+ paddingEnd: input.paddingEnd,
15
+ scrollPaddingStart: input.scrollPaddingStart,
16
+ scrollPaddingEnd: input.scrollPaddingEnd,
17
+ gap: input.gap,
18
+ lanes: input.lanes,
19
+ // Consumer override first (mirrors react-virtual's spread-over-defaults), then the
20
+ // live window position on the client / 0 (top) on the server.
21
+ initialOffset: input.initialOffset ??
22
+ (() => typeof document !== 'undefined'
23
+ ? input.horizontal
24
+ ? window.scrollX
25
+ : window.scrollY
26
+ : 0),
27
+ initialRect: input.initialRect,
28
+ getItemKey: input.getItemKey,
29
+ rangeExtractor: input.rangeExtractor,
30
+ indexAttribute: input.indexAttribute,
31
+ initialMeasurementsCache: input.initialMeasurementsCache,
32
+ anchorTo: input.anchorTo,
33
+ followOnAppend: input.followOnAppend,
34
+ scrollEndThreshold: input.scrollEndThreshold,
35
+ scrollMargin: input.scrollMargin,
36
+ enabled: input.enabled,
37
+ isRtl: input.isRtl,
38
+ isScrollingResetDelay: input.isScrollingResetDelay,
39
+ useScrollendEvent: input.useScrollendEvent,
40
+ useAnimationFrameWithResizeObserver: input.useAnimationFrameWithResizeObserver,
41
+ laneAssignmentMode: input.laneAssignmentMode,
42
+ useCachedMeasurements: input.useCachedMeasurements,
43
+ debug: input.debug,
44
+ measureElement: input.measureElement,
45
+ observeElementRect: observeWindowRect,
46
+ observeElementOffset: observeWindowOffset,
47
+ scrollToFn: windowScroll,
48
+ onChange: notify,
49
+ };
50
+ }
51
+ // Render-time (server / pre-mount) slice — the window counterpart of the element helper.
52
+ // Opt-in: without `initialRect` there is no server slice (trivial window), which keeps every
53
+ // example that does not pass it byte-for-byte identical to a client-only build. With
54
+ // `initialRect` a transient instance derives the initial window as plain data and is discarded;
55
+ // nothing live is retained or serialized, and the values recompute identically on resume.
56
+ // The probe never observes, so it reads `initialRect` directly (getScrollElement returns null on
57
+ // the server and window on the client, but neither is measured before getVirtualItems()).
58
+ // `range` is the visible window ({ startIndex, endIndex }) the probe computed — plain data,
59
+ // null when there is no slice.
60
+ export function renderSlice(input) {
61
+ if (!input.initialRect) {
62
+ return { items: [], size: 0, range: null };
63
+ }
64
+ const probe = new Virtualizer(buildOptions(input, () => { }));
65
+ const items = probe.getVirtualItems();
66
+ return { items, size: probe.getTotalSize(), range: probe.range };
67
+ }
package/marko.json CHANGED
@@ -1 +1 @@
1
- { "tags-dir": "./src/tags", "script-lang": "ts" }
1
+ { "exports": "./dist/tags" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/marko-virtual",
3
- "version": "3.14.3",
3
+ "version": "3.15.1",
4
4
  "description": "Headless UI for virtualizing scrollable elements in Marko 6",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -38,22 +38,25 @@
38
38
  },
39
39
  "./package.json": "./package.json",
40
40
  "./marko.json": "./marko.json",
41
- "./src/tags/*": "./src/tags/*"
41
+ "./dist/tags/*": "./dist/tags/*"
42
42
  },
43
- "sideEffects": false,
43
+ "sideEffects": [
44
+ "**/*.marko"
45
+ ],
44
46
  "files": [
45
47
  "dist",
46
- "src/tags",
47
48
  "marko.json",
48
49
  "README.md"
49
50
  ],
50
51
  "dependencies": {
51
- "@tanstack/virtual-core": "3.17.6"
52
+ "@tanstack/virtual-core": "3.17.8"
52
53
  },
53
54
  "devDependencies": {
54
- "@marko/vite": "^3.0.0",
55
+ "@marko/language-tools": "^2.6.2",
56
+ "@marko/type-check": "3.1.1",
57
+ "@marko/vite": "^6.1.0",
55
58
  "@testing-library/dom": "^10.0.0",
56
- "marko": "^6.0.0"
59
+ "marko": "^6.2.2"
57
60
  },
58
61
  "peerDependencies": {
59
62
  "marko": "^6.0.0"
@@ -61,10 +64,10 @@
61
64
  "scripts": {
62
65
  "clean": "premove ./dist ./coverage",
63
66
  "test:eslint": "eslint ./src",
64
- "test:types": "tsc",
67
+ "test:types": "mtc -p tsconfig.typecheck.json",
65
68
  "test:lib": "vitest",
66
69
  "test:lib:dev": "pnpm run test:lib --watch",
67
70
  "test:build": "publint --strict",
68
- "build": "vite build"
71
+ "build": "vite build && marko-type-check -p tsconfig.tags.json && premove ./dist/tsconfig.tags.tsbuildinfo"
69
72
  }
70
73
  }
@@ -1,78 +0,0 @@
1
- import { Virtualizer } from "@tanstack/virtual-core"
2
- import type { VirtualItem, ScrollToOptions } from "@tanstack/virtual-core"
3
- import { buildOptions } from "./options"
4
-
5
- export interface Input {
6
- count: number
7
- estimateSize?: (index: number) => number
8
- getScrollElement: () => Element | null
9
- overscan?: number
10
- horizontal?: boolean
11
- paddingStart?: number
12
- paddingEnd?: number
13
- scrollPaddingStart?: number
14
- scrollPaddingEnd?: number
15
- gap?: number
16
- lanes?: number
17
- initialOffset?: number | (() => number)
18
- content: Marko.Body<[{
19
- virtualItems: VirtualItem[]
20
- totalSize: number
21
- measureElement: ((el: Element | null) => void) | undefined
22
- scrollToIndex: (index: number, options?: ScrollToOptions) => void
23
- scrollToOffset: (offset: number, options?: ScrollToOptions) => void
24
- }]>
25
- }
26
-
27
- <let/v = (null as Virtualizer<Element, Element> | null)/>
28
- <let/cleanup = (null as (() => void) | null)/>
29
- <let/items = ([] as VirtualItem[])/>
30
- <let/size = 0/>
31
-
32
- <lifecycle<{}>
33
- onMount() {
34
- const notify = () => {
35
- const inst = v
36
- if (!inst) return
37
- items = inst.getVirtualItems()
38
- size = inst.getTotalSize()
39
- }
40
-
41
- v = new Virtualizer<Element, Element>(buildOptions(input, notify))
42
- cleanup = v!._didMount()
43
- v!._willUpdate()
44
- }
45
-
46
- onUpdate() {
47
- const current = v
48
- if (!current) return
49
-
50
- const notify = () => {
51
- const inst = v
52
- if (!inst) return
53
- items = inst.getVirtualItems()
54
- size = inst.getTotalSize()
55
- }
56
-
57
- current.setOptions({ ...current.options, ...buildOptions(input, notify) })
58
-
59
- // _willUpdate() is a no-op when the scroll element is unchanged, so
60
- // notify() explicitly afterwards to recompute on count/option changes.
61
- current._willUpdate()
62
- notify()
63
- }
64
-
65
- onDestroy() {
66
- cleanup?.()
67
- v = null
68
- cleanup = null
69
- }
70
- />
71
-
72
- <${input.content}
73
- virtualItems=items
74
- totalSize=size
75
- measureElement=v?.measureElement
76
- scrollToIndex=(index: number, options?: ScrollToOptions) => v?.scrollToIndex(index, options)
77
- scrollToOffset=(offset: number, options?: ScrollToOptions) => v?.scrollToOffset(offset, options)
78
- />